只有累積,沒有奇蹟

2019年4月17日 星期三

[UnitTest] 使用 Fluent Assertions 增加單元測試碼可讀性

前言
過去在撰寫單元測試代碼時都是使用 NUnit 內建的 Assert.AreEqual 來驗證是否符合預期,雖然早已聽過 Fluent Assertions 盛名但並未實際使用過,直到最近在與同事討論時同事大推發現真的很不錯,讓戴碼的可能性增加不少,想起之前上 91 Training 時不斷強調測試代碼可讀性的重要性,這一篇就來簡單介紹 Flnent Asserentions 的安裝與使用若有問題或是錯誤的地方歡迎各位高手給予指導

安裝 Fluent Assertions
Fluent Assertion 支援 .NET Framework 與 .NET Core,常用的測試框架像是 NUnit、MSTest、xUnit 等都支援,一開始在 Visual Studio 2019 建立名稱叫做 FluentAssertionsConsoleApp 的 ConsoleApp專案,接著在專案 Program.cs 輸入要測試的程式碼 Add 方法,如下所示
public class TestMethod
{
    public int Add(int numberOne, int numberTwo)
    {
        return numberOne + numberTwo;
    }
}
接著要建立測試專案,在 Visual Studio 2019 建立專案介面有做調整,可以透過搜尋輸入框及下拉選單過找到想要建立的專案範本,這陣子使用操作上覺得挺方便的,如下圖所示,在搜尋框輸入 NUnit 選定 NUmit 專案並按下一步 
建立完畢 NUnit 測試專案後,下一步是在測試專案安裝 Fluent Assertions nuget 套件,步驟如下
Step 1 : 按下 CTRL + Q 輸入 Nuget,開啟 nuget 
Step 2 : 在 Nuget Package Mnager 輸入 FluentAssertions,目前最新版為 5.6.0 按下安裝
安裝完畢後確認測試專案有 Fluent Assertional 即代表安裝成功

測試代碼
接著要開始測試 TestMethod 中的 Add 方法,開始嘗試用 Fluent Asserentions 語法寫測試,代碼如下
using FluentAssertions;
using FluentAssertionsConsoleApp;
using NUnit.Framework;

namespace Tests
{
    public class Tests
    {       
        [Test]
        public void AddTest_Using_FluentAssertions()
        {
            var testMethod = new TestMethod();
            var actual = testMethod.Add(2, 4);
            actual.Should().Be(6);
        }
    }
}
使用 Fluent Assertions 寫法後代碼變得容易理解許多,actual.Should().Be(6) 用口語化解釋為 " 測試的結果應該為 6 ",另外 Be 方法也提供 Because 參數,加上後可以讓語句與閱讀上更加通順,加上 Because 後代碼如下
[Test]
public void AddTest_Using_FluentAssertions_Becasue()
{
    var testMethod = new TestMethod();
    var actual = testMethod.Add(2, 2);
    actual.Should().Be(4, because:"2+2=4");
}
測試案例加上 because 後當測試失敗時,可以透過錯誤訊息內容讓你或是你的團隊成員更容易理解測試的目的,測試失敗的 Testcase 如下
透過 Resharper 可以看到 Should 擴充方法內容如下,Should 與 Be 都是 FluentAssertions 針對 int 型別定義的擴充方法 ( Extension Methods ),透過 F12 後可以看到內容

/// <summary>
/// Returns an <see cref="T:FluentAssertions.Numeric.NumericAssertions`1" /> object that can be used to assert the
/// current <see cref="T:System.Int32" />.
/// </summary>
[Pure]
public static NumericAssertions<int> Should(this int actualValue)
{
  return new NumericAssertions<int>((object) actualValue);
}
擴充方法可以參考 MSDN 說明 : 傳送門,擴充方法可以在很多地方看到像是 LINQ 中的 Where ,針對集合物件做篩選的動作在回傳 IEnumerable<T>,在 Fluent Assertions 針對現有類別加入方法,方便使用 Should、Be 或是其他語法,方便在單元測試中撰寫,以下列出一些常用的方式,

public void TestBasicMethod()
{
    // object
    object obj = null;
    obj.Should().BeNull("because the obj is null");
    obj.Should().NotBeNull();

    // string 
    var something = "something";
    something.Should().BeEmpty();
    something.Should().NotBeEmpty();

    // datetime 
    var time= new DateTime();
    time.Should().HaveYear(2019);
    time.Should().HaveDay(4);
    time.Should().HaveMonth(3);
    time.Should().HaveHour(22);
    time.Should().HaveMinute(15);
    time.Should().HaveSecond(0);

    // dictionary 
    var dic = new Dictionary<int, string>()
    {
        {1, "Marcus"},
        {2, "Flash"},
        {3, "Neil"}
    };
    dic.Should().NotBeEmpty();
    
    // type
    something.Should().BeOfType<string>("because a {0} is set", typeof(string));
    something.Should().BeOfType(typeof(string), "because a {0} is set", typeof(string));

    // equal 
    string otherObject = "whatever";
    something.Should().Be(otherObject, "because they have the same values");
    something.Should().NotBe(otherObject);

    // exception 
    something.Should().BeOfType<Exception>()
        .Which.Message.Should().Be("Other Message");
}

如果您希望測試代碼更容易理解,建議你可以使用 Fluent Assertion Library 來協助撰寫單元測試,更多的測試語法可以參考官方文件說明,這篇就簡單介紹 Fluent Assertion,日後如果有遇過不錯的寫法也會在一併分享給各位,Happy Coding :)

參考
Fluent Assertions 
讓單元測試代碼更好寫、好讀:Fluent Assertions

0 意見:

張貼留言

Copyright © m@rcus 學習筆記 | Powered by Blogger

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com