其他分享
首页 > 其他分享> > Moq是否在嘲笑子接口的返回值并忽略中间步骤的错误或功能?

Moq是否在嘲笑子接口的返回值并忽略中间步骤的错误或功能?

作者:互联网

我最近正在构建一个应用程序,而一位同事写了一个我发誓会失败的设置.我错了.在该方法中,工厂方法设置为期望值为true,并且将返回整数.因为我们没有嘲笑我们的配置,所以布尔总是错误的.

设置是:

 var homeStoreDataServiceFactory = new Mock<IHomeStoreDataServiceFactory>();
 homeStoreDataServiceFactory.Setup(service => service.Create(true).GetStoreNumber())
                .Returns(5);

我认为对factory.Create(false)的调用不会生成模拟对象,因此我们将获得0的整数而不是模拟值5.相反,无论我们将service.Create(X)更改为什么,对GetStoreNumber的调用始终返回5,就像我们使用过It.IsAny()一样.

我建立了MVCE,以便您可以看到我的困惑:

using System;
using Moq;

namespace MoqBugMCV
{
    public interface IItemServiceFactory
    {
        IItemService Create(bool shouldCreateServiceA);
    }

    public class Item
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }

    public interface IItemService
    {
        Item GetItem();
    }

    public class ItemManager
    {
        private readonly IItemService _itemService;

        public ItemManager(IItemServiceFactory itemServiceFactory)
        {
            _itemService = itemServiceFactory.Create(true); //<==== configured true (like by app.config at runtime or something)
        }

        public Item GetAnItem()
        {
            return _itemService.GetItem();
        }
    }

    internal class Program
    {

        private static void Main(string[] args)
        {
            var itemServiceFactory = new Mock<IItemServiceFactory>();
            var chrisItem = new Item {Name = "Chris's Amazing Item", Price = 1000000};
            itemServiceFactory.Setup(factory => factory.Create(true).GetItem())
                .Returns(chrisItem);

            var itemManager = new ItemManager(itemServiceFactory.Object);

            var theItem = itemManager.GetAnItem();

            Console.WriteLine("The item is {0} and costs {1}", theItem.Name, theItem.Price);

            var itemServiceFactoryBroken = new Mock<IItemServiceFactory>();
            itemServiceFactoryBroken.Setup(factory => factory.Create(false).GetItem()).Returns(chrisItem); //expecting this to fail, because IItemServiceFactory.Create(true) is configured

            itemManager = new ItemManager(itemServiceFactoryBroken.Object);
            theItem = itemManager.GetAnItem();

            Console.WriteLine("The item is {0} and costs {1}", theItem.Name, theItem.Price); //would expect the item would be null or values to be blank
        }
    }
}

所以…这是错误还是功能,还是我不了解Moq?

解决方法:

是的,似乎将应用于递归Moq的中间解除引用的任何过滤器都忽略为per here-即使使用It.匹配器,则过滤器将被忽略.即,目前,递归模拟将始终生成与It.IsAny相关的内容.如前所述,在中间取消引用的任何参数上.

现在,我将分手:

.Setup(service => service.Create(true).GetStoreNumber()

并将其分为:

mockFactory.Setup(f => f.Create(xx))
mockService.Setup(service => service.GetStoreNumber())

将此分离应用于您的MVCE示例,以下设置仅模拟factory.Create(false),而将factory.Create(true)保留为引用类型的默认返回值null:

var itemServiceFactory = new Mock<IItemServiceFactory>();
var itemService = new Mock<IItemService>();

itemService.Setup(svc => svc.GetItem()).Returns(chrisItem);
itemServiceFactory.Setup(factory => factory.Create(false))
    .Returns(itemService.Object);

var itemManager = new ItemManager(itemServiceFactory.Object);
var theItem = itemManager.GetAnItem(); // Get NRE on _itemService.GetItem as expected

因此,嘲笑factory.Create(true)将使用设置服务,而GetAnItem()将返回令人惊奇的项目.

编辑

似乎有一种方法可以在中间步骤上实现过滤,并使用Linq to Mocks一次性压缩工厂和服务模拟的设置:

var itemServiceFactory = Mock.Of<IItemServiceFactory>(
    fac => fac.Create(false) == Mock.Of<IItemService>(
        svc => svc.GetItem() == chrisItem));

var itemManager = new ItemManager(itemServiceFactory);
var theItem = itemManager.GetAnItem();

标签:unit-testing,moq,configuration,c,visual-studio
来源: https://codeday.me/bug/20191120/2047480.html