.NET單元測試(五):多線程

鑑於.NET Framework 4.5後.NET增長了對 async/await 的支持,本文討論的異步內容均基於async/awaitbash

  客戶端界面開發,多線程是逃不了的話題,而多線程的加入勢必對程序的穩定性帶來挑戰,單元測試就顯得更爲重要。相對於同步代碼的測試,多線程單元測試有更多細節須要注意。多線程

async void 和 async Task。

  假設某一天你運氣很差,須要爲相似以下的方法補充單元測試:異步

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

  在測試程序是否按照預期的拋出了異常,咱們經常會用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();
    });
}
複製代碼

異步方法mock

  在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
相關文章
相關標籤/搜索