c# - Moq 是在模拟子接口(interface)返回值并忽略中间步骤是错误还是功能?

标签 c# visual-studio unit-testing configuration moq

我最近正在构建一个应用程序,一位同事写了一个我发誓会失败的设置。我错了。在其中设置了一个工厂方法,其预期值为 true 并将返回一个整数。因为我们没有模拟我们的配置,所以 bool 总是假的。

设置是:

 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.Is<>匹配器,过滤器被忽略。即目前,递归模拟将始终生成相当于 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

因此, mock 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();

关于c# - Moq 是在模拟子接口(interface)返回值并忽略中间步骤是错误还是功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27723216/

相关文章:

C# react 扩展当 OnNext 花费很长时间并且可观察到产生新事件时会发生什么

c# - CaSTLe Windsor 注册多个实例

c# - 将电话号码解析为其部分

visual-studio - Azure Function v2 Web Deploy 遇到服务器连接问题,不得不终止连接

visual-studio - Team Foundation Server - 解决方案绑定(bind)丢失

c# - 在 LINQ-to-SQL 中使用跨上下文连接

visual-studio - 在 Azure 中使用 Visual Studio Online 加载测试性能计数器

unit-testing - 为什么在此D程序中无法进行单元测试?

objective-c - OCUnit 中的简化断言

Angularjs:使用 spyOn 模拟 location.path() 进行单元测试