[英]Wait for void async method to complete in unit test case
這些是我的業務引擎的方法。 上傳是在內部調用異步方法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被稱為任務仍然沒有執行。 任何人都可以提供一些解決方案,以便這個測試用例每次都會通過
正如其他人所說, 最好的解決方案是讓方法返回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
類型 :
[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
。
單元測試的主要好處之一是它們可以幫助您設計更好的代碼。 大多數情況下,當您努力編寫測試時,它告訴您需要改進代碼,而不是測試。
在這種情況下,問題是使用帶有async
方法的void
返回類型,這是Stephen Cleary 建議的 。 實際上,他在文章中引用的原因之一是:
異步void方法很難測試。
我認為他對async void
方法的一個最有說服力的論點是這樣的:
當返回類型為Task時,調用者知道它正在處理將來的操作; 當返回類型為void時,調用者可能會認為該方法在返回時已完成。
如果您還不相信, 這里有另一篇文章 ,其中包含避免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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.