2017-06-06 117 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