iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0

Unit Test 應用於 Async Code-2 - 用程式碼講故事(測試碼 Exception 篇章)

那今天的內容是延續昨天的商業邏輯所撰寫的測試碼,那接下來要探討的議題是當 Load 方法要注入 Exception 的情境時,針對非同步處理要如何撰寫,那在 NUnit3 的套件中有提供一個方法叫 Assert.ThrowsAsync,那其方法是類似於 Assert.Throws,應用於非同步的情境,測試碼如下:

[Test]
public void FailedGet_Ver1()
{
    // Arrange
    var storage = Substitute.For<IStorage>();

    storage.Load("key").Throws<TimeoutException>();

    var cache = new DemoCache(storage);

    // Act + Assert
    Assert.ThrowsAsync<TimeoutException>(async () => await cache.Get("key"));
}

從這邊我們可以看到,若使用 Assert.ThrowsAsync 的語法,在 Lambda 結構裡就會去執行非同步處理;因此,從 Lambda 中就會取得相對應的值(範例為 string),所以在宣告的時候並不是用 async Task<string>,而是 void 方法。但倘若我們還要驗證共執行幾次,如下:

[Test]
public async Task FailedGet_Ver2()
{
    // Arrange
    var storage = Substitute.For<IStorage>();

    storage.Load("key").Throws<TimeoutException>();

    var cache = new DemoCache(storage);

    // Act + Assert
    Assert.ThrowsAsync<TimeoutException>(async () => await cache.Get("key"));

    await storage.Received(1).Load("key");
}

因 Received() 需要等 cache.Get 執行完,因此就需要使用非同步語法。


Unit Test 應用於 Async Code-3 - 用程式碼講故事(測試碼 Reload 篇章)

接下來要探討當 Returns 含有 Multiple Values 的時候,在搭配 Reload 的概念,把 Cache 裡面的東西從 Storage 取出,每當執行 Load 的概念時,則會往後取一個數值,若已到最後一個時,則會固定在該數值,測試馬如下:

[Test]
public async Task SingleGet_WithReload()
{
    // Round 1
    // Arrange
    var storage = Substitute.For<IStorage>();

    storage.Load("key").Returns("value1", "value2", "value3");

    var cache = new DemoCache(storage);

    // Act
    var result1 = await cache.Get("key");
    var result2 = await cache.Get("key");

    // Assert
    Assert.AreEqual(result1, "value1");
    Assert.AreEqual(result2, "value1");

    // Round 2
    // Act
    var result3 = await cache.Get("key");

    // Assert
    Assert.AreEqual(result3, "value2");

    // Round 3
    // Act
    var result4 = await cache.Get("key");
    var result5 = await cache.Get("key");
    
    // Assert
    Assert.AreEqual(result4, "value3");
    Assert.AreEqual(result5, "value3");
    
    // Round 4
    // Act
    var result6 = await cache.Get("key");
    var result7 = await cache.Get("key");
    
    // Assert
    Assert.AreEqual(result6, "value3");
    Assert.AreEqual(result7, "value3");
}

原參考來源是只測試兩次,那為了示意每 Reload 會往後延一個以及固定在最後一個數值;因此,這邊測試碼寫了 Reload 4 Round,並且在 Round 3 就已到第三個數值,Round 4 依舊在第三個數值。而最後的測試碼是測試當有多個非同步執行的時候,兩邊執行的次數是否符合預期,測試碼如下:

[Test]
public async Task MultipleGets_WithReload()
{
    // Arrange
    var storage = Substitute.For<IStorage>();

    var cache = new DemoCache(storage);

    // Act
    await cache.Get("key1");
    await cache.Get("key1");
    await cache.Get("key2");
    await cache.Get("key1");
    await cache.Get("key2");
    await cache.Get("key2");
    await cache.Get("key2");
    await cache.Get("key2");

    // Assert
    await storage.Received(2).Load("key1");
    await storage.Received(3).Load("key2");
}

Unit Test 應用於 Async Code - 結尾

從這兩天的範例可以看出測試碼驗證非同步處理與商業邏輯一樣,都是要採用 Task、async 與 await 語法;但如果把 Act 的方法直接寫在 Assert 的 Lambda 方法中,則宣告的時候則直接採用 void 即可(如 Exception 篇章),以及探討 Returns 的用碼。


上一篇
Day 25-Unit Test 應用於 Async Code-1 (情境及應用-5)
下一篇
Day 27-Unit Test 應用於使用重構與測試手法優化 C# Code-1 (情境及應用-7)
系列文
單元測試從入門到進階之路 (以 C# NUnit 3 X NSubstitute 為例)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言