鑑於.NET Framework 4.5後.NET增長了對 async/await 的支持,本文討論的異步內容均基於async/awaitbash
客戶端界面開發,多線程是逃不了的話題,而多線程的加入勢必對程序的穩定性帶來挑戰,單元測試就顯得更爲重要。相對於同步代碼的測試,多線程單元測試有更多細節須要注意。多線程
假設某一天你運氣很差,須要爲相似以下的方法補充單元測試:異步
public static bool Changed;
public static async void ChangeAsync()
{
await Task.Run(() =>
{
Task.Delay(1000);
Changed = true;
});
}
複製代碼
你發現,要測試此方法須要用一些奇葩的方式,好比:async
[TestMethod()]
public void ChangeAsyncTest_OriginalFalse_ChangeToTrue()
{
AsyncClient.Changed = false;
AsyncClient.ChangeAsync();
Thread.Sleep(1100);
Assert.IsTrue(AsyncClient.Changed);
}
複製代碼
顯然,這種延時等待是極其噁心的,若是ChangeAsync方法返回的不是void而是Task,咱們就能夠愉快的await了:單元測試
[TestMethod()]
public async Task ChangeAsyncTest_OriginalFalse_ChangeToTrue()
{
AsyncClient.Changed = false;
await AsyncClient.ChangeAsync();
Assert.IsTrue(AsyncClient.Changed);
}
複製代碼
須要特別注意的是,在異步單元測試方法中也必須返回Task,這是MSTest的約定,不然這個測試方法沒法運行起來。(實際上MSTest也須要使用返回的Task來收集異常,關於這部分更多內容能夠參見Async/Await最佳實踐)測試
在測試程序是否按照預期的拋出了異常,咱們經常會用ExpectedException,這傢伙有一個問題,它是對整個測試方法的方法體作捕獲,也就是說測試方法中的非action代碼拋出了異常依然可以被ExpectedException捕獲,這就形成潛在的bug,爲了解決此問題,在MSTest V2以前每每須要寫一些輔助方法,但MSTest V2斷言庫中增長了Assert.ThrowsExceptionAsync和Assert.ThrowsException,能夠精確的定位在哪段代碼中拋出了異常。假設咱們的被測代碼跟下面相似:spa
public static async Task ChangeAsync()
{
await Task.Run(() =>
{
throw new InvalidOperationException();
});
}
複製代碼
測試代碼能夠這樣寫:線程
[TestMethod()]
public async Task ChangeAsyncTest_ThrowInvalidOperationException()
{
await Assert.ThrowsExceptionAsync<InvalidOperationException>(async () =>
{
await AsyncClient.ChangeAsync();
});
}
複製代碼
在moq中,異步方法的mock也是極其簡單的,假設有這樣的接口:code
public interface ITextReader
{
Task<string> ReadTextAsync();
}
複製代碼
測試代碼中mock其返回結果能夠有以下兩種寫法:接口
var mockTextReader = new Mock<ITextReader>();
//能夠這樣
mockTextReader.Setup(x => x.ReadTextAsync()).Returns(async ()=>await Task.FromResult("mockValue"));
//也能夠這樣
mockTextReader.Setup(x => x.ReadTextAsync()).ReturnsAsync(()=> "mockValue");
複製代碼
2017-11-30 15:26:34