只有累積,沒有奇蹟

2019年1月20日 星期日

[UnitTest] NUnit 參數測試 - 使用 TestCase、TestCaseSource

前言
上一篇 NUnit 入門教學 簡單介紹如何使用 NUnit 撰寫單元測試,但在實務上在寫單元測試時不會那麼單純,舉例來說如果要針對某個 method 進行種不同情境的測試,僅差在不同的參數來多次驗證結果
NUnit 官方文件中有提供很多好用的方法像是 TestCaseTestCaseSource 等方式可以解決此問題,以下會簡單分享小小研究後的說明與差異性,依據適合的情境使用相對應的方法

解決方案
要驗證的 sample Code 如下,Example 類別有個 Reverse 方法內容為將輸入的字串反轉
public class Example
{
    public string Reverse(string input)
    {
        char[] charArray = input.ToCharArray();
        Array.Reverse(charArray);
        return new string(charArray);
    }
}
為了驗證該方法是否正確,因此準備多種情境的參數來進行單元測試,預計測試資料如下
  • input : abc | output : cba
  • input : marcus output : sucram
  • input : apple output : elppa
撰寫多個 Method 
如果沒有使用參數化進行測試時,預估會根據每個要驗證的內容撰寫一個 method 來對應,為了在 Test Explorer 在眾多 Test cases 好識別,建議定義命名規則 ReverseTest_Input_輸入參數_Return_回傳參數,sample code 如下
using NUnit.Framework;

namespace ConsoleApp2.Tests
{
    [TestFixture()]
    public class ExampleTests
    {
        [Test()]
        public void ReverseTest_Input_abc_Return_cba()
        {
            //Arrange
            var ex = new Example();
            var input = "abc";
            
            //Act
            var actual = ex.Reverse(input);

            //Assert
            Assert.AreEqual("cba", actual);
        }
        [Test()]
        public void ReverseTest_Input_marcus_Return_sucram()
        {
            //Arrange
            var ex = new Example();
            var input = "marcus";

            //Act
            var actual = ex.Reverse(input);

            //Assert
            Assert.AreEqual("sucram", actual);
        }
        [Test()]
        public void ReverseTest_Input_apple_Return_elppa()
        {
            //Arrange
            var ex = new Example();
            var input = "apple";

            //Act
            var actual = ex.Reverse(input);

            //Assert
            Assert.AreEqual("elppa", actual);
        }
    }
} 
缺點會如果測試案例很多的化會有很多重複的 Code,避免重複可能會自己寫一個類別用集合跟迴圈來解決重複 Code的問題

使用 TestCase 
在 NUnit 中提供 TestCase Attribute 來透過不同數據來測試,首先改寫 ReverseTest 方法將輸入值預期結果當作參數,接著在要測試的方法上掛 TestCase attribute 並定義要測試的參數資料,看起來 Code 比較乾淨沒有重複的代碼sample code 如下
using NUnit.Framework;

namespace ConsoleApp2.Tests
{
    [TestFixture()]
    public class ExampleCaseTests
    {
        [Test()]
        [TestCase("abc", "cba")]
        [TestCase("marcus", "sucram")]
        [TestCase("apple", "elppa")]
        public void ReverseTest(string input, string output)
        {
            var ex = new Example();
            var actual = ex.Reverse(input);
            Assert.AreEqual(input, actual);
        }
    }
} 

使用 TestCaseSource 
NUnit 另外也提供 TestCaseSource 測試不同數據,在 ReverseTest 方法使用 TestCaseSource 定義要測試的集合名稱 TestDataCases,另外在 TestDataCases 定義要測試的數據內容集合sample code 如下

using NUnit.Framework;
using System.Collections;

namespace ConsoleApp2.Tests
{
    [TestFixture()]
    public class ExampleCaseDataTests
    {
        [Test, TestCaseSource("TestDataCases")]
        public void ReverseTest(string input, string output)
        {
            var ex = new Example();
            var actual = ex.Reverse(input);
            Assert.AreEqual(output, actual);
        }

        public static IEnumerable TestDataCases
        {
            get
            {
                yield return new TestCaseData("abc","cba");
                yield return new TestCaseData("marcus","sucram");
                yield return new TestCaseData("apple","elppa");
            }
        }
    }
} 
TestCaseData 提供 returns 方法,如果要測試的方法是有回傳值時可以在 return 方法加上參數來驗證回傳值是否正確

Test Explore 可讀性
三種方式都可以解決問題(第一種有點來亂的?),但可讀性也是蠻重要的一個環節,可以比對三者在 Test Explore 呈現的結果,以這 sample code 簡單的測試案例來說 TestCase 與 TestCaseSource 可讀性來看是較好的,測試代碼上亦是如此,但如果在較為複雜的測試案例上或許有不同結果,各位可以在實作時自己斟酌使用適合的方式

參考
NUnit
Test Generator NUnit extension
TestCase Attribute



1 則留言:

  1. 第一章圖使用 TestCase 的截圖裡面code寫錯了,是Assert.AreEqual(output, actual);才對,我看後面的截圖有修正

    回覆刪除

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

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com