.NET Core之单元测试(三):Mock框架Moq的使用

编写一个API

新增一个接口

public interface IFoo
{
    bool Ping(string ip);
}

接口实现

public class Foo : IFoo
{
    public bool Ping(string ip)
    {
        return new Random().Next(1, 100) % 2 == 1;
    }
}

编写一个待测试的API,其中有三个依赖项:SqliteDbContextILogger<SampleController>IFoo
API实现的功能:先获取SqliteDbContext中的SampleEntity,如果其中的属性StringValue值为ping,并且结果为False,则返回404,否则返回SampleEntity

[Route("api/[controller]")]
[ApiController]
public class SampleController : ControllerBase
{
    private readonly SqliteDbContext _sqliteDbContext;
    private readonly ILogger<SampleController> _logger;
    private readonly IFoo _foo;
    public SampleController(SqliteDbContext sqliteDbContext, ILogger<SampleController> logger,IFoo foo)
    {
        _logger = logger;
        _foo = foo;
        _sqliteDbContext = sqliteDbContext;
    }
    [HttpGet("{id}")]
    public async Task<IActionResult> Get(int id)
    {
        var sampleData = await _sqliteDbContext.SampleEntity
            .SingleOrDefaultAsync(s => s.Id == id);
        if (sampleData?.StringValue == "ping")
        {
            var pingOk = _foo.Ping("localhost");
            if (!pingOk)
                return NotFound();
        }
        return Ok(sampleData);
    }
}

测试用例

var loggerMock = new Mock<ILogger<SampleController>>();
var logger = loggerMock.Object;
var fooMock = new Mock<IFoo>();
fooMock.Setup(foo => foo.Ping("localhost")).Returns(true);
var foo = fooMock.Object;

关于Moq的更多操作可参见:Moq4 Quickstart

private async Task<SqliteDbContext> GetSqliteDbContextAsync()
{
    var options = new DbContextOptionsBuilder<SqliteDbContext>()
        .UseInMemoryDatabase(Guid.NewGuid().ToString())
        .Options;
    var sqliteDbContext = new SqliteDbContext(options);
    sqliteDbContext.SampleEntity.Add(new Infrastructure.Entities.SampleEntity
    {
        Id = 1,
        BoolValue = false,
        DateTimeValue = DateTime.Now,
        StringValue = "sample"
    });

    sqliteDbContext.SampleEntity.Add(new Infrastructure.Entities.SampleEntity
    {
        Id = 2,
        BoolValue = false,
        DateTimeValue = DateTime.Now,
        StringValue = "ping"
    });
    await sqliteDbContext.SaveChangesAsync();
    return sqliteDbContext;
}
[TestMethod]
public async Task Get_ReturnOK_WithPingTrue()
{
    // Arrange
    var dbContext = await GetSqliteDbContextAsync();

    var loggerMock = new Mock<ILogger<SampleController>>();
    var logger = loggerMock.Object;

    var fooMock = new Mock<IFoo>();
    fooMock.Setup(foo => foo.Ping("localhost")).Returns(true);
    var foo = fooMock.Object;

    var controller = new SampleController(dbContext, logger, foo);

    // Act
    var response = await controller.Get(2);
    var responseModel = ((OkObjectResult)response).Value as SampleEntity;

    // Assert
    Assert.IsInstanceOfType(response, typeof(OkObjectResult));
    Assert.IsTrue(responseModel.Id == 2);
    Assert.IsFalse(responseModel.BoolValue);
    Assert.IsTrue(responseModel.StringValue == "ping");
}
[TestMethod]
public async Task Get_ReturnOK_WithPingFalse()
{
    // Arrange
    var dbContext = await GetSqliteDbContextAsync();

    var loggerMock = new Mock<ILogger<SampleController>>();
    var logger = loggerMock.Object;

    var fooMock = new Mock<IFoo>();
    fooMock.Setup(foo => foo.Ping("localhost")).Returns(false);
    var foo = fooMock.Object;

    var controller = new SampleController(dbContext, logger, foo);

    // Act
    var response = await controller.Get(2);

    // Assert
    Assert.IsInstanceOfType(response, typeof(NotFoundResult));
}

.NET Core之单元测试(三):Mock框架Moq的使用

全文结束