是否有必要模拟单元测试中的所有依赖项?

时间:2014-11-24 11:44:53

标签: c# .net unit-testing mocking

我正在尝试为具有以下构造函数定义的ASP.NET创建单元测试(在运行实际应用程序时填充Ninject):

    public OrderController(IViewModelFactory modelFactory, INewsRepository repository, ILoggedUserHelper loggedUserHelper,
        IDelegateHelper delegateHelper, ICustomerContextWrapper customerContext) {
        this.factory = modelFactory;
        this.loggedUserHelper = loggedUserHelper;
        this.delegateHelper = delegateHelper;
        this.customerContext = customerContext;
    }

我想测试OrderController中的方法,但是为了隔离它,我必须模拟所有这些依赖项,这变得非常荒谬(不得不同时模拟子依赖项)。

在这种情况下,这是单元测试这个类的最佳实践吗?

谢谢。

2 个答案:

答案 0 :(得分:3)

嗯,您必须为所有依赖项提供测试双打,而不一定是模拟。

幸运的是,这是21世纪,有一些工具可以让我们的工作更轻松。您可以使用AutoFixture创建OrderController的实例,并根据需要注入模拟。

var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());
var orderController = fixture.Create<OrderController>();

其中,基本上相当于:

var factory = new Mock<IViewModelFactory>();
var repository = new Mock<INewsRepository>();
var delegateHelper = new Mock<IDelegateHelper >();
var customerContext = new Mock<ICustomerContextWrapper >();

var orderController = new OrderController(factory.Object, repository.Object, delegateHelper.Object, customerContext.Object);

如果这些依赖项依赖于其他类型,那么也会设置它们。具有AutoConfiguredMoqCustomization自定义功能的AutoFixture将构建完整的依赖关系图。

如果您需要访问存储库模拟,以便稍后可以对其进行一些额外的设置或断言,您可以冻结它。冻结类型将使fixture容器仅包含该类型的一个实例,例如:

var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());
var repositoryMock = fixture.Freeze<Mock<INewsRepository>>();
repositoryMock.Setup(x => x.Retrieve()).Returns(1);

//the frozen instance will be injected here
var orderController = fixture.Create<OrderController>(); 

repositoryMock.Verify(x => x.Retrieve(), Times.Once);

我在这些例子中使用过Moq,但AutoFixture也与NSubstitute,RhinoMock和Foq集成。

披露:我是该项目的贡献者之一

答案 1 :(得分:1)

不,你不是。您可以使用的测试对象实现的不同概念称为测试双打。 Mocks只是Gerard Meszaros在his book中定义的一种Test Double:

  • 虚拟对象传递但从未实际使用过。通常它们仅用于填充参数列表。
  • 对象实际上有工作实现,但通常需要一些使它们不适合生产的快捷方式(InMemoryTestDatabase就是一个很好的例子)。
  • Stubs 为测试期间拨打的电话提供固定答案,通常根本不会对测试中编程的任何内容做出任何响应。
  • 间谍是存根,它们还会根据调用方式记录一些信息。其中一种形式可能是电子邮件服务,它记录发送的邮件数量。
  • 模拟预先编程了预期,形成了预期会收到的来电规范。如果他们接到他们不期望的电话并在验证期间进行检查,他们可以抛出异常,以确保他们得到了他们期待的所有电话。

您只需要根据测试要求提供尽可能多的存根,假货和假人。

傻瓜只需要很少的工作来生成,可能足以涵盖您的场景。一个例子是一个带有IEmailer和ILogWriter的构造函数。如果您只测试Log方法,则只需提供足够的IEmailer实现,以便测试不会抛出Argument异常。

另外,关于你对子依赖关系的观点...... Moq会为你解决这个问题,因为你的界面的Moq实现不会带来依赖关系。

相关问题