简体   繁体   English

UnitTests-Moq-如何从Moq中返回与Setup()中的参数匹配的对象

[英]UnitTests - Moq - How to Return() object from Moq that matches Parameter in Setup()

I am confused in Moq and I'm not sure what is wrong here. 我对起订量感到困惑,我不确定这里出了什么问题。

I would like to test LeadService that depend on ILeadStorageService, and I want to configure Moq in that way - Return the object that matches the GUID passed in Setup. 我想测试依赖ILeadStorageService的LeadService,我想以这种方式配置Moq-返回与安装程序中传递的GUID匹配的对象。

The problem is in the Moq Setup/Returns line, because when I substitute dependent object to its real instantiation - Test passes, but it is totally wrong. 问题出在Moq设置/返回行中,因为当我用依赖对象代替其实际实例时-测试通过,但这是完全错误的。 I don't want to test just LeadService, not a dependent storage. 我不想仅测试LeadService,而不要测试依赖存储。

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

    }

Tested method 测试方法

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

then in test preparation: 然后在测试准备中:

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

    }

and the test itself: 和测试本身:

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

    }

Instead of expected Return - I got an exception: 而不是预期的回报-我有一个例外: ArgumentException

What I am missing in _leadStorageMock.Setup().Returns() ? 我在_leadStorageMock.Setup()。Returns()中缺少什么?

The Returns extension method accepts a delegate with same parameters as the method which you are mocking. Returns扩展方法接受与您要模拟的方法具有相同参数的委托。 And those arguments will be passed to delegate during invocation of mocked method. 并且这些参数将在调用模拟方法期间传递给委托。 So instead of Lead object, you will get the argument which is passed to mc.Get method - the lead id: 因此,您将获得传递给mc.Get方法的参数mc.Get ID,而不是Lead对象:

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

Check QuickStart section related to accessing invocation arguments when returning a value. 检查与返回值时访问调用参数有关的“ 快速入门”部分。


Note that there is a bunch of Returns extension methods which accept a value function as an argument: 请注意,有一堆Returns扩展方法接受一个value函数作为参数:

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

As you can see those value functions calculate the value to return from mocked method, but they all receive a different number of arguments (up to 16). 如您所见,这些值函数计算要从模拟方法返回的值,但是它们都接收不同数量的参数(最多16个)。 Those arguments will exactly match arguments of the method you are mocking and they will be passed to the valueFunction during invocation of the mocked method. 这些参数将完全匹配您要valueFunction的方法的参数,并且在调用valueFunction期间会将它们传递给valueFunction So if you are mocking some function with two arguments, then corresponding extension should be used: 因此,如果要模拟带有两个参数的某个函数,则应使用相应的扩展名:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM