我有这个代码
public class ClassToTest
{
private readonly IRepository repository;
public ClassToTest(DI GOES HERE){...}
public DoSomething()
{
Task.Run(async () => {
//some code
repository.ExecuteAsync();
}
}
}
public class Repository : IRepository
{
public Task ExecuteAsync()
{
using (var connection = new SqlConnection(DbConfiguration.DatabaseConnection))
{
return connection.ExecuteAsync(storedProcedure, parameters, commandType: CommandType.StoredProcedure, commandTimeout: Configuration.TransactionTimeout);
}
}
}
[Test]
public void TestMethod()
{
var repository = new Mock<IRepository>;
var classToTest = new ClassToTest();
classToTest.DoSomething();
repository.Veryfy(p => p.ExecuteAsync(), Times.Once());
}
此消息的测试失败
模拟一次的预期调用,但是是0次:p =&gt; p.ExecuteAsync()
有谁知道为什么?
由于
答案 0 :(得分:5)
正如其他人所提到的那样,因为您正在调用Task.Run
而不是等待响应,所以单元测试可能会在后台任务开始之前完成,因此Moq
验证失败
此外,您的代码不会按原样编译 - 当在StackOverflow上询问Q时,请确保提供完整的,可编译的MVP。
特别重要的是您要测试的代码中的错误。 Repository.ExecuteAsync
在connection.ExecuteAsync
范围内调用using
,但尚未等待。这意味着连接将be disposed before the task completes。您需要将方法更改为async
和await
调用以推迟处理连接。
包装器方法DoSomething
方法不应该使用Task.Run()
,但是因为它adds no value到存储库任务,它不需要重复async / return await
1}},或者。
调用者(在本例中为您的单元测试)可以等待DoSomething
(或者如果调用者真的想要在不等待任务的情况下进行进一步处理,则将其留给调用者来决定。至少这个方式,调用者获取任务的句柄,以检查完成情况。
代码的最终状态看起来可能更像:
public class ClassToTest
{
private readonly IRepository _repository;
public ClassToTest(IRepository repository)
{
_repository = repository;
}
// Doesn't necessarily need to be async
public Task DoSomething()
{
// We're return the wrapped task directly, and adding no additional value.
return repository.ExecuteAsync();
}
}
public class Repository : IRepository
{
public async Task ExecuteAsync()
{
using (var connection = new SqlConnection(DbConfiguration.DatabaseConnection))
{
// Here we do need to await, otherwise we'll dispose the connection
return await connection.ExecuteAsync(storedProcedure, parameters,
commandType: CommandType.StoredProcedure,
commandTimeout: Configuration.TransactionTimeout);
}
}
}
// NUnit has full support for async / await
[Test]
public async Task TestMethod()
{
var repository = new Mock<IRepository>();
var classToTest = new ClassToTest(repository.Object);
repository.Setup(_ => _.ExecuteAsync()).Returns(Task.FromResult((object)null));
// Moq also has support for async, e.g. .ReturnsAsync
// You need to await the test.
await classToTest.DoSomething();
repository.Verify(p => p.ExecuteAsync(), Times.Once());
}