我正在尝试为具有以下构造函数定义的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中的方法,但是为了隔离它,我必须模拟所有这些依赖项,这变得非常荒谬(不得不同时模拟子依赖项)。
在这种情况下,这是单元测试这个类的最佳实践吗?
谢谢。
答案 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)
您只需要根据测试要求提供尽可能多的存根,假货和假人。
傻瓜只需要很少的工作来生成,可能足以涵盖您的场景。一个例子是一个带有IEmailer和ILogWriter的构造函数。如果您只测试Log方法,则只需提供足够的IEmailer实现,以便测试不会抛出Argument异常。
另外,关于你对子依赖关系的观点...... Moq会为你解决这个问题,因为你的界面的Moq实现不会带来依赖关系。