自动 Mock,让编写单元测试更简单

问题

由于依赖注入,特别是构造函数注入的广泛使用,使得编写单元测试时,需要使用 Mock 框架(例如 Moq)生成测试类的依赖接口的"模拟"实现,并验证接口是否按预期使用。

例如 eShopOnContainers 中的测试代码就使用了 Moq,实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private readonly Mock<IMediator> _mediatorMock;
private readonly Mock<IOrderQueries> _orderQueriesMock;
private readonly Mock<IIdentityService> _identityServiceMock;
private readonly Mock<ILogger<OrdersController>> _loggerMock;

public OrdersWebApiTest()
{
    _mediatorMock = new Mock<IMediator>();
    _orderQueriesMock = new Mock<IOrderQueries>();
    _identityServiceMock = new Mock<IIdentityService>();
    _loggerMock = new Mock<ILogger<OrdersController>>();
}

[Fact]
public async Task Cancel_order_with_requestId_success()
{
    //Arrange
    _mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<CancelOrderCommand, bool>>(), default(CancellationToken)))
        .Returns(Task.FromResult(true));

    //Act
    var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object);
    var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), Guid.NewGuid().ToString()) as OkResult;

    //Assert
    Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK);

}

可以看到 OrdersController 依赖了4个接口,而每个测试用例都必须将 Mock 对象传入构造函数,一旦更改了 OrdersController 的构造函数参数,那么你将需要更改大量单元测试代码。

Moq.AutoMocker

Moq.AutoMocker 是一款基于 MoqIoC 容器,它可以用来自动创建待测试类的依赖。

引用 nugetMoq.AutoMock 之后,上面的测试代码可以修改如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[Fact]
public async Task Cancel_order_with_requestId_success()
{
    //Arrange
    var autoMocker = new AutoMocker();
    var orderController = autoMocker.CreateInstance<OrdersController>();
    
    var mediatorMock = autoMocker.GetMock<IMediator>();
    mediatorMock.Setup(x => x.Send(It.IsAny<IdentifiedCommand<CancelOrderCommand, bool>>(), default(CancellationToken)))
        .Returns(Task.FromResult(true));

    //Act
    var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), Guid.NewGuid().ToString()) as OkResult;

    //Assert
    Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK);
}

可以看到,无需提前生成 Mock 对象,autoMocker.CreateInstance<T> 即可创建指定类型的实例。autoMocker.GetMock<T> 可以获得依赖接口的 Mock 实例,然后像以前一样,执行 SetupVerify 方法。

结论

Moq.AutoMocker 大大减少了在单位测试中编写重复 Mock 代码的数量,简化了待测试类的生成。

0%