2017-06-06 113 views
1

我在Moq中感到困惑,我不確定這裏有什麼問題。UnitTests - Moq - 如何從Moq中返回()對象與Setup中的參數匹配()

我想測試依賴於ILeadStorageService的LeadService,並且我想以這種方式配置Moq - 返回與在Setup中傳遞的GUID相匹配的對象。

問題出現在Moq設置/返回行中,因爲當我將依賴對象替換爲它的實例時 - 測試通過,但它是完全錯誤的。我不想測試LeadService,而不是依賴存儲。

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

    } 

測試方法

 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; 
    } 

然後在測試準備:

[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; 

    } 

和測試本身:

[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); 

    } 

相反的預期回報 - 我有一個例外: ArgumentException

我在_leadStorageMock.Setup()中缺少的是什麼?

回答

4

Returns擴展方法接受一個與您正在模擬的方法具有相同參數的委託。這些參數將在調用模擬方法時傳遞給委託。因此,而不是Lead對象,你會得到它傳遞給mc.Get方法的參數 - 領先ID:

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

檢查QuickStart部分相關返回值時調用訪問參數


注意,有一幫其接受一個值函數作爲參數Returns擴展方法:

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 

正如你可以看到這些值的函數計算從模擬的方法返回的值,但他們都收到不同數量的參數(最多16個)。這些參數將與您嘲笑的方法的參數完全匹配,並且在調用模擬方法期間它們將被傳遞到valueFunction。所以如果你用兩個參數嘲弄某個函數,那麼應該使用相應的擴展名:

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

謝謝!它並不那麼明顯。 – Lightning3