分享方式:


建立資料驅動的單元測試

使用適用於受控碼的 Microsoft 單元測試架構 (MSTest),設定單元測試方法來從資料來源擷取值。 這個方法會針對資料來源中每個資料列依序執行,讓您輕鬆地用單一方法來測試各種輸入。

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

  • 使用 DataRow 屬性的內嵌資料
  • 使用 DynamicData 屬性的成員資料
  • 來自一些使用 DataSource 屬性的已知來源提供者

受測方法

例如,假設我們具有:

  1. 名為 MyBank 的方案,可接受並處理不同帳戶類型的交易。

  2. MyBank 中名為 BankDb 的專案,負責管理帳戶的交易。

  3. BankDb 專案中名為 Maths 的類別,可執行數學函式以確保所有交易都是對銀行有利的。

  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;
}

Test 測試方法

內嵌資料驅動型測試

針對內嵌測試,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 屬性來指定成員的名稱、種類 (屬性、預設值或方法) 以及定義類型 (預設會使用目前類型),該成員將提供資料驅動型測試所使用的資料。

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 屬性 (attribute) 的 DynamicDataDisplayName 屬性 (property) 覆寫預設產生的顯示名稱。 顯示名稱方法簽章必須是 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 索引子屬性,以擷取您在測試中使用的值。

建立資料來源

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

FirstNumber SecondNumber Sum
0 1 1
1 7 2
2 -3 -1

將 TestContext 新增至測試類別

單元測試架構會建立 TestContext 物件來儲存資料驅動型測試的資料來源資訊。 架構接著會設定此物件作為我們建立的 TestContext 屬性值。

public TestContext TestContext { get; set; }

在測試方法中,您可以透過 TestContextDataRow 索引子屬性來存取資料。

注意

.NET Core 不支援 DataSource 屬性。 若您嘗試在 .NET Core、UWP 或 WinUI 單元測試專案中透過此方式存取測試資料,您會看到與以下內容相似的錯誤:"'TestContext' 沒有包含 'DataRow' 的定義,也找不到接受型別為 'TextContext' 第一個引數的可存取擴充方法 (您是否遺漏使用指示詞或組件參考?)"

撰寫測試方法

AddIntegers 的測試方法相當簡單。 針對資料來源中的每個資料列,請使用 FirstNumberSecondNumber 資料行值作為參數呼叫 AddIntegers,並針對 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 演算法並未正確處理負數值。

當受測方法已修正並重新執行測試時,結果列會變成綠色,且測試方法會移動到 [通過的測試] 群組。