2017-06-06 1 views
1

Je suis confus dans Moq et je ne suis pas sûr de ce qui ne va pas ici.UnitTests - Moq - Comment retourner un objet de Moq qui correspond à Parameter dans Setup()

Je voudrais tester LeadService qui dépend de ILeadStorageService, et je veux configurer Moq de cette façon - Renvoyer l'objet qui correspond au GUID passé dans le programme d'installation.

Le problème est dans la ligne Moq Setup/Returns, car lorsque je substitue un objet dépendant à son instanciation réelle - Test passe, mais c'est totalement faux. Je ne veux pas tester uniquement LeadService, pas un stockage dépendant.

public LeadService(IConfigurationDbContext configurationDbContext, 
         ILeadStorageService leadStorageService, 
         ILeadDeliveryService deliveryService) 
    { 
     this.configurationDbContext = configurationDbContext; 
     this.leadStorageService = leadStorageService; 
     this.deliveryService = deliveryService; 

    } 

Testé méthode

 public TestLeadResponse ProcessTestLead(TestLeadRequest request) 
     { 
     var response = new TestLeadResponse() 
     { 
      Status = TestLeadStatus.Ok 
     }; 

     try 
     { 
      var lead = leadStorageService.Get(request.LeadId); 
      if (lead == null) 
      { 
       throw new LeadNotFoundException(request.LeadId); 
      } 

      var buyerContract = 
       configurationDbContext.BuyerContracts.SingleOrDefault(bc => bc.Id == request.BuyerContractId); 
      if (buyerContract == null) 
      { 
       throw new BuyerContractNotFoundException(request.BuyerContractId); 
      } 

      response.DeliveryEntry = deliveryService.DeliverLead(lead, buyerContract); 
     } 
     catch (LeadNotFoundException e) 
     { 
      response.Status = TestLeadStatus.LeadNotFound; 
      response.StatusDescription = e.Message; 
     } 
     catch (BuyerContractNotFoundException e) 
     { 
      response.Status = TestLeadStatus.BuyerContractNotFound; 
      response.StatusDescription = e.Message; 
     } 

     return response; 
    } 

puis dans la préparation du test:

[TestInitialize] 
    public void Initialize() 
    { 
     _leadIdStr = "2c3ac0c0-f0c2-4eb0-a55e-600ae3ada221"; 

     _dbcontext = new ConfigurationDbContext(); 
     _lead = PrepareLeadObject(); 
     _buyerContract = PrepareBuyerContractObject(Id : 1, BuyerContractId : 1, BuyerTag: "GAME"); 
     _leadDeliveryMock = new Mock<ILeadDeliveryService>(); 
     _leadStorageMock = new Mock<ILeadStorageService>(); 
     _leadStorageService = new LeadStorageService("LeadGeneration_Dev"); 

    } 

    private Lead PrepareLeadObject() 
    { 
     var lead = new Lead() {CountryId = 1, Country = "NL", Id = Guid.Parse(_leadIdStr)}; 
     return lead; 

    } 

et le test lui-même:

[TestMethod] 
    public void LeadServiceTest_ProcessTestLeadWithWrongBuyerContractIDThrowsBuyerContractNotFoundException() 
    { 
     _leadDeliveryMock 
      .Setup(methodCall => methodCall.DeliverLead(_lead, _buyerContract)) 
      .Returns<LeadDeliveryEntry>((r) => PrepareLeadDeliveryEntry()); 

     // here is the problem!!!! 
     _leadStorageMock.Setup(mc => mc.Get(_leadId)).Returns((Lead l) => PrepareLeadObject()); 

     //if i change to real object - test passes 
     //_service = new LeadService(_dbcontext, _leadStorageService, _leadDeliveryMock.Object); 
     _service = new LeadService(_dbcontext, _leadStorageMock.Object, _leadDeliveryMock.Object); 

     var response = _service.ProcessTestLead(new TestLeadRequest() { BuyerContractId = int.MaxValue, LeadId = _leadId }); 

     Assert.IsNotNull(response); 
     Assert.AreEqual(response.Status, TestLeadStatus.BuyerContractNotFound); 

    } 

lieu de rendement prévu - je suis une exception: ArgumentException

Ce qui me manque dans _leadStorageMock.Setup(). Renvoie()?

Répondre

4

La méthode d'extension Returns accepte un délégué avec les mêmes paramètres que la méthode dont vous vous moquez. Et ces arguments seront passés à déléguer lors de l'invocation de méthode mockée. Ainsi, au lieu de Lead objet, vous obtiendrez l'argument qui est passé à mc.Get méthode - l'identifiant principal:

_leadStorageMock.Setup(mc => mc.Get(_leadId)) 
     .Returns((Guid leadId) => PrepareLeadObject()); 

Vérifiez la section QuickStart liés à l'accès invocation arguments lors du retour d'une valeur.


Notez qu'il ya un tas de Returns méthodes d'extension qui acceptent une fonction de valeur comme argument:

Returns<T>(Func<T, TResult> valueFunction); 
Returns<T1, T2>(Func<T1, T2, TResult> valueFunction); 
Returns<T1, T2, T3>(Func<T1, T2, T3, TResult> valueFunction); 
// etc 

Comme vous pouvez le voir les fonctions de valeur calculer la valeur de retour de la méthode moqué, mais ils reçoivent tous un nombre différent d'arguments (jusqu'à 16). Ces arguments correspondront exactement aux arguments de la méthode que vous moquez et ils seront passés au valueFunction lors de l'invocation de la méthode mockée. Donc, si vous vous moquez d'une fonction avec deux arguments, alors l'extension correspondante doit être utilisée:

mock.Setup(m => m.Activate(It.IsAny<int>(), It.IsAny<bool>()) 
    .Returns((int i, bool b) => b ? i : 0); // Func<T1, T2, TResult> 
+1

Merci! Ce n'était pas si évident. – Lightning3