【问题标题】:Proper way to Mock repository objects for unit tests using Moq and Unity使用 Moq 和 Unity 为单元测试模拟存储库对象的正确方法
【发布时间】:2010-01-20 03:55:53
【问题描述】:

在我的工作中,我们使用 Moq 进行模拟,使用 Unity 进行 IOC 容器。我对此很陌生,没有很多资源可以帮助我确定我应该使用的最佳实践。

现在,我有一组存储库接口(例如:IRepository1、IRepository2...IRepository4),特定进程需要使用它们来完成其工作。

在实际代码中,我可以通过使用 IOC 容器和 RegisterType() 方法来确定所有 IRepository 对象。

我正在尝试找出能够测试需要上述 4 个存储库的方法的最佳方法。

我在想我可以只注册一个 Unity IOC 容器的新实例,并为每个传递 Mock.Object 值的模拟对象在容器上调用 RegisterInstance。我正在尝试使此注册过程可重用,因此我不必对每个单元测试一遍又一遍地做同样的事情,除非单元测试需要从存储库返回一些特定数据。这就是问题所在......在模拟存储库上设置预期值的最佳实践是什么?似乎如果我只是在 Unity 容器上调用 RegisterType,我会丢失对实际 Mock 对象的引用并且无法覆盖行为。

【问题讨论】:

    标签: c# .net moq unity-container


    【解决方案1】:

    单元测试根本不应该使用容器。依赖注入 (DI) 分为两个阶段:

    1. 使用 DI 模式将依赖项注入消费者。您不需要容器来执行此操作。
    2. 在应用程序的Composition Root,使用 DI 容器(或Poor Man's DI)将所有组件连接在一起。

    如何完全不使用任何 DI 容器进行单元测试

    例如,考虑一个使用 IRepository1 的类。通过使用 Constructor Injection 模式,我们可以使依赖项成为类的不变量。

    public class SomeClass
    {
        private readonly IRepository1 repository;
    
        public SomeClass(IRepository1 repository)
        {
            if (repository == null)
            {
                throw new ArgumentNullException("repository");
            }
    
            this.repository = repository;
        }
    
        // More members...
    }
    

    请注意,如果实例成功实例化,readonly 关键字与 Guard 子句结合可保证 repository 字段不为空。

    您不需要容器来创建 MyClass 的新实例。您可以使用 Moq 或另一个 Test Double 直接从单元测试中执行此操作:

    [TestMethod]
    public void Test6()
    {
        var repStub = new Mock<IRepository1>();
        var sut = new SomeClass(repStub.Object);
        // The rest of the test...
    }
    

    更多信息请参见here...

    如何使用 Unity 进行单元测试

    但是,如果您绝对必须在测试中使用 Unity,您可以创建容器并使用 RegisterInstance 方法:

    [TestMethod]
    public void Test7()
    {
        var repMock = new Mock<IRepository1>();
    
        var container = new UnityContainer();
        container.RegisterInstance<IRepository1>(repMock.Object);
    
        var sut = container.Resolve<SomeClass>();
        // The rest of the test...
    }
    

    【讨论】:

    • 我浏览了您的答案,并在考虑是否应该否决(因为您似乎建议在单元测试中使用 IoC 容器)然后实际阅读了您的答案...我想说,这是我可耻的+1。如果人们发现它直到今天仍然有用,您应该使用 ArgumentNullException(nameof(repository)) 而不是 ArgumentNullException("repository") - 帖子写于 5-6 年前当此功能不存在时。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-18
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 2014-12-04
    • 2014-06-07
    • 1970-01-01
    相关资源
    最近更新 更多