共用方式為


建立數據驅動單元測試

您可以使用 Microsoft 單元測試架構 (MSTest) 為受控程式碼設定單元測試方法,以從資料來源擷取值。 方法會針對數據源中的每個數據列連續執行,這可讓您輕鬆地使用單一方法測試各種輸入。

資料驅動單元測試可以使用下列任何一種:

  • 使用 DataRow 屬性的內嵌數據
  • 使用 DynamicData 屬性的成員數據
  • 從一些知名的來源提供者使用 DataSource 屬性

受測方法

例如,假設您有:

  1. 稱為 MyBank 的解決方案,可接受和處理不同類型的帳戶交易。

  2. MyBank 中稱為 BankDb 的專案,可管理帳戶的交易。

  3. Maths 專案中稱為 BankDb 的類別,會執行數學函式,以確保任何交易對銀行有利。

  4. 稱為 BankDbTests 的單元測試專案,用來測試 BankDb 元件的行為。

  5. 稱為 MathsTests 的單元測試類別,用來驗證 Maths 類別的行為。

我們將在 Maths 中測試使用 迴圈新增兩個整數的方法:

public int AddIntegers(int first, int second)
{
    int sum = first;
    for (int i = 0; i < second; i++)
    {
        sum += 1;
    }

    return sum;
}

測試測試方法

內嵌資料驅動測試

針對內嵌測試,MSTest 會使用 DataRow 來指定數據驅動測試所使用的值。 此範例中的測試會針對每個數據列連續執行。

[TestMethod]
[DataRow(1, 1, 2)]
[DataRow(2, 2, 4)]
[DataRow(3, 3, 6)]
[DataRow(0, 0, 1)] // The test run with this row fails
public void AddIntegers_FromDataRowTest(int x, int y, int expected)
{
    var target = new Maths();
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

成員數據驅動測試

MSTest 會使用 DynamicData 屬性來指定成員的名稱、種類和定義類型(預設會使用目前類型),以提供資料驅動測試所使用的數據。

注意

在 MSTest 3.8 之前,DynamicDataSourceType 列舉有兩個成員,PropertyMethod。 預設值為 Property。 從 MSTest 3.8 開始,新的成員 AutoDetect 會新增至 列舉,而且是預設值。 因此,您不再需要指定 DynamicDataSourceType

public static IEnumerable<object[]> AdditionData
{
    get
    {
        return new[]
        { 
            new object[] { 1, 1, 2 },
            new object[] { 2, 2, 4 },
            new object[] { 3, 3, 6 },
            new object[] { 0, 0, 1 }, // The test run with this row fails
        };
    }
}

[TestMethod]
[DynamicData(nameof(AdditionData))]
public void AddIntegers_FromDynamicDataTest(int x, int y, int expected)
{
    var target = new Maths();
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

您也可以使用 DynamicData 屬性中的 DynamicDataDisplayName 屬性來更改預設生成的顯示名稱。 顯示名稱方法簽章必須 public static string 並接受兩個參數,第一個類型為 MethodInfo,第二個類型 object[]

public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data)
{
    return string.Format("DynamicDataTestMethod {0} with {1} parameters", methodInfo.Name, data.Length);
}

[DynamicData(nameof(AdditionData), DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))]

來源提供者數據驅動測試

建立資料源驅動單元測試牽涉到下列步驟:

  1. 建立數據源,其中包含您在測試方法中使用的值。 數據源可以是在執行測試的計算機上註冊的任何類型。

  2. 將類型為 TestContext 的公用 TestContext 屬性新增至測試類別。

  3. 建立單元測試方法

  4. DataSourceAttribute 屬性新增至其中。

  5. 使用 DataRow 索引器屬性來擷取您在測試中使用的值。

注意

DataSourceAttribute 目前僅支援 .NET Framework。 如果您在 .NET Core、.NET 5+、UWP 或 WinUI 單元測試專案中嘗試使用此方法存取測試數據,您會看到類似 “'TestContext' 未包含 'DataRow' 的定義,找不到接受 'TestContext' 類型第一個參數的擴充方法 'DataRow' (是不是少了 using 指令或組件參考?)”

建立數據源

若要測試 AddIntegers 方法,請建立數據源,指定參數的值範圍,以及您預期傳回的總和。 在此範例中,我們將建立名為 MathsData 的 Sql Compact 資料庫,以及名為 AddIntegersData 的數據表,其中包含下列數據行名稱和值

FirstNumber SecondNumber 總和
0 1 1
1 1 2
2 -3 -1

將 TestContext 新增至測試類別

單元測試架構會建立 TestContext 物件,以儲存數據驅動測試的數據源資訊。 架構接著會將這個物件設定為您建立的 TestContext 屬性值。

public TestContext TestContext { get; set; }

在測試方法中,您可以透過 DataRowTestContext 索引器屬性來存取數據。

撰寫測試方法

AddIntegers 的測試方法相當簡單。 針對數據源中的每一行,使用 AddIntegersSecondNumber 欄值作為參數來呼叫 ,然後將傳回值與 Sum 欄值進行驗證。

[TestMethod]
[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0; Data Source=C:\Data\MathsData.sdf;", "Numbers")]
public void AddIntegers_FromDataSourceTest()
{
    var target = new Maths();

    // Access the data
    int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
    int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
    int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

指定 DataSourceAttribute

DataSource 屬性會指定數據源的連接字串,以及您在測試方法中使用的數據表名稱。 連接字串中的確切信息會根據您使用的數據源類型而有所不同。 在此範例中,我們使用 SqlServerCe 資料庫。

[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0;Data Source=C:\Data\MathsData.sdf", "AddIntegersData")]

謹慎

連接字串可以包含敏感數據(例如密碼)。 連接字串以純文字形式儲存在原始碼和已編譯的程式集中。 限制對原始程式碼和元件的存取,以保護此敏感性資訊。

DataSource 屬性有三個建構函式。

[DataSource(dataSourceSettingName)]

具有一個參數的建構函式會使用儲存在解決方案 app.config 檔案中的連接資訊。 dataSourceSettingsName 是配置檔中指定連接資訊的 Xml 元素名稱。

使用 app.config 檔案可讓您變更數據源的位置,而不需變更單元測試本身。 如需如何建立和使用 app.config 檔案的詳細資訊,請參閱 逐步解說:使用組態檔定義數據源

[DataSource(connectionString, tableName)]

具有兩個參數的 DataSource 建構函式會指定數據源的連接字串,以及包含測試方法數據的數據表名稱。

連接字串取決於數據來源類型的類型,但它應該包含 Provider 元素,指定數據提供者的不變異名稱。

[DataSource(
    dataProvider,
    connectionString,
    tableName,
    dataAccessMethod
    )]

使用 TestContext.DataRow 存取數據

若要存取 AddIntegersData 數據表中的數據,請使用 TestContext.DataRow 索引器。 DataRowDataRow 物件,因此請依索引或數據行名稱擷取數據行值。 因為值會以 物件的形式傳回,因此將它們轉換成適當的類型:

int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);

執行測試和檢視結果

當您完成撰寫測試方法之後,請建置測試專案。 測試方法會出現在 [未執行測試] 群組 的 [測試總管] 。 當您執行、寫入及重新執行測試時,[測試總管] 會顯示結果群組 失敗的測試通過的測試,以及 [未執行測試]。 您可以選擇 [執行所有] 來執行所有測試,或選擇 [執行] 以選擇執行的測試子集。

測試管理器頂端的 測試結果列 在測試執行時會動畫顯示。 在測試回合結束時,如果所有測試都通過,則列會是綠色的,如果有任何測試失敗,則為紅色。 測試回合的摘要會出現在 [測試總管] 視窗底部的詳細數據窗格中。 選取測試以檢視底部窗格中該測試的詳細數據。

注意

每個數據列都有結果,還有一個摘要結果。 如果每個資料列的測試都通過,摘要結果會顯示為 通過。 如果測試在任何資料列上失敗,摘要執行會顯示為 [失敗]

如果您在範例中執行任何 AddIntegers_FromDataRowTestAddIntegers_FromDynamicDataTestAddIntegers_FromDataSourceTest 方法,結果列會變成紅色,並將測試方法移至 失敗的測試。 如果數據源的任何反覆執行方法失敗,數據驅動測試就會失敗。 當您在 [測試總管] 視窗中選擇失敗的數據驅動測試時,詳細數據窗格會顯示數據列索引所識別的每個反復項目結果。 在我們的範例中,AddIntegers 演算法似乎未正確處理負值。

當測試中的方法修正並重新執行測試時,結果列會變成綠色,並將測試方法移至 [通過的測試] 群組