这些是我的业务引擎的方法。 Upload正在调用内部异步方法UploadAsync()
public void Upload(Stream data)
{
//Some logic
//Call private async method
UploadAsync(data);
}
private async void UploadAsync(Object data)
{
await Task.Run(() =>
{
using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory))
{
factory.Repository.UploadData(data);
}
}
);
}
这是Upload()方法的单元测试用例
[TestMethod, TestCategory("Unit")]
public void Upload_ValidData_Success()
{
//other logic to setup mock repository
//call public method which calls async method
engine.Upload(data);
_mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
}
此测试用例在验证方法偶尔失败,但有以下异常:
016-10-06T19:25:20.4982657Z ##[error]Expected invocation on the mock once, but was 0 times: x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
根据我的分析,在调用异步方法之前调用验证方法。这可能是此测试用例失败的原因。 当在传递给Task.Run的委托中调用方法本身时,如何验证在模拟上调用方法?按时间mock.Verify被称为任务仍然没有执行。 任何人都可以提供一些解决方案,以便每次都能通过这个测试用例
答案 0 :(得分:2)
正如其他人所说,最佳解决方案是让方法返回Task
。 Task
- 返回async
方法更容易测试:
private async Task UploadAsync(Object data)
{
await Task.Run(() =>
{
using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory))
{
factory.Repository.UploadData(data);
}
});
}
private async void Upload(Object data)
{
await UploadAsync(data);
}
然后您的单元测试可以是:
[TestMethod, TestCategory("Unit")]
public async Task Upload_ValidData_Success()
{
//other logic to setup mock repository
//call public method which calls async method
await engine.UploadAsync(data);
_mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
}
但是,如果由于某种原因您需要来测试async void
方法,可以使用我的AsyncContext
type来完成:
[TestMethod, TestCategory("Unit")]
public void Upload_ValidData_Success()
{
//other logic to setup mock repository
//call public method which calls async method
AsyncContext.Run(() => engine.Upload(data));
_mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
}
附注:在理想情况下,我建议您在存储库中添加UploadDataAsync
,然后移除Task.Run
。
答案 1 :(得分:1)
单元测试的主要好处之一是它们可以帮助您设计更好的代码。大多数情况下,当您努力编写测试时,它告诉您需要改进代码,而不是测试。
在这种情况下,问题是使用void
返回类型和async
方法,这是Stephen Cleary advises against。实际上,他在文章中引用的原因之一是:
Async void方法很难测试。
我认为他反对async void
方法的最有说服力的论据之一就是:
当返回类型为Task时,调用者知道它正在处理将来的操作;当返回类型为void时,调用者可能会认为该方法在返回时已完成。
如果您还不确定,here's another article包含其他一些原因可以避免async void
。
总之,您应该更改方法以返回Task
。然后你就可以在测试中等待它了。
private async Task UploadAsync(Object data)
{
return Task.Run(() => {
using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory))
{
factory.Repository.UploadData(data);
}
});
}
和测试...
[TestMethod, TestCategory("Unit")]
public void Upload_ValidData_Success()
{
//other logic to setup mock repository
//call public method which calls async method
engine.Upload(data).Wait();
_mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once);
}