今天的文章內容是參考於 Testing C# Async Code with NUnit and NSubstitute,其目的在於想了解如何程式碼帶有非同步的方法時,則測試是否有需要改寫或注意的地方。大概簡述非同步方法的概念與語法,非同步方法是指執行某特定任務,無需等該特定任務完成就去執行其他的任務(比如泡咖啡要等咖啡出來的時候做早餐),而我們採用的是 Task-Based 的方式撰寫,其中會用到 async 與 await,詳細內容可參見async 與 await,僅節錄其中一段撰寫的語法:
static async Task<string> MyDownloadPageAsync(string url)
{
using (var webClient = new WebClient())
{
Task<string> task = webClient.DownloadStringTaskAsync(url);
string content = await task;
return content;
}
}
因此,從參考影片中提供了一個故事情節,其目的是我們取 Cache 中指定 Key 的數值,而 Cache 未有該 Key 的數值或點擊超過兩次,則會從 Storage 取得最新的值。而 IStorage 提供了兩隻 Task 方法:Save 與 Load,分別作為儲存與載入,程式碼如下:
public class DemoCache
{
private readonly IStorage Storage;
private readonly Dictionary<string, int> Hits
= new Dictionary<string, int>();
private readonly Dictionary<string, string> Cache
= new Dictionary<string, string>();
public DemoCache(IStorage inStorage) {
Storage = inStorage;
}
public async Task<string> Get(string key)
{
if (!Hits.ContainsKey(key) || Hits[key] >= 2)
{
string value = await Storage.Load(key);
Cache[key] = value;
Hits[key] = 1;
return value;
}
else
{
Hits[key]++;
return Cache[key];
}
}
}
public interface IStorage
{
Task Save(string key, string value);
Task<string> Load(string key);
}
於是乎,我們就可以開始撰寫測試碼,而測試的情境要考量數種情況,分別如下:
[Test]
public async Task SingleGet()
{
// Arrange
var storage = Substitute.For<IStorage>();
storage.Load("key").Returns("value1");
var cache = new DemoCache(storage);
// Act
var result = await cache.Get("key");
// Assert
await storage.Received(1).Load("key");
Assert.AreEqual(result, "value1");
}
[Test]
public async Task MultipleGets_WithDifferentKeys()
{
// Arrange
var storage = Substitute.For<IStorage>();
var cache = new DemoCache(storage);
// Act
await cache.Get("key1");
await cache.Get("key2");
// Assert
await storage.Received(1).Load("key1");
await storage.Received(1).Load("key2");
}
[Test]
public async Task MultipleGets_WithSametKeys()
{
// Arrange
var storage = Substitute.For<IStorage>();
var cache = new DemoCache(storage);
// Act
await cache.Get("key1");
await cache.Get("key1");
await cache.Get("key1");
// Assert
await storage.Received(2).Load("key1");
}
後面還有其他的測試情況,會在明天敘述。