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

Dav*_*nez 3 .net c# 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;
    }
Run Code Online (Sandbox Code Playgroud)

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

在这种情况下,对此类进行单元测试的最佳实践是什么?

dca*_*tro 6

好吧,您必须为所有依赖项提供测试替身,不一定是模拟。

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

var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());
var orderController = fixture.Create<OrderController>();
Run Code Online (Sandbox Code Playgroud)

基本上,这相当于:

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);
Run Code Online (Sandbox Code Playgroud)

如果这些依赖项依赖于其他类型,那么这些依赖项也会被设置。具有定制功能的 AutoFixtureAutoConfiguredMoqCustomization将构建完整的依赖关系图。

如果您需要访问存储库模拟,以便稍后可以对其进行一些额外的设置或断言,则可以冻结它。冻结一种类型将使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);
Run Code Online (Sandbox Code Playgroud)

我在这些示例中使用了 Moq,但 AutoFixture 还与 NSubstitute、RhinoMock 和 Foq 集成。

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