建立資料驅動的單元測試
使用適用於受控碼的 Microsoft 單元測試架構 (MSTest),設定單元測試方法來從資料來源擷取值。 這個方法會針對資料來源中每個資料列依序執行,讓您輕鬆地用單一方法來測試各種輸入。
資料驅動型單元測試可以使用下列任何一種:
- 使用
DataRow
屬性的內嵌資料 - 使用
DynamicData
屬性的成員資料 - 來自一些使用
DataSource
屬性的已知來源提供者
受測方法
例如,假設我們具有:
名為
MyBank
的方案,可接受並處理不同帳戶類型的交易。在
MyBank
中名為BankDb
的專案,負責管理帳戶的交易。BankDb
專案中名為Maths
的類別,可執行數學函式以確保所有交易都是對銀行有利的。名為
BankDbTests
的單元測試專案,用來測試BankDb
元件的行為。名為
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))]
來源提供者資料驅動型測試
建立資料來源驅動單元測試包含下列步驟:
建立資料來源,其中包含您在測試方法中使用的值。 資料來源可以是已註冊在執行測試之電腦上的任何類型。
將 TestContext 類型的公用
TestContext
屬性新增至測試類別。建立單元測試方法
將 DataSourceAttribute 屬性新增至其中。
使用 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; }
在測試方法中,您可以透過 TestContext
的 DataRow
索引子屬性來存取資料。
注意
.NET Core 不支援 DataSource 屬性。 若您嘗試在 .NET Core、UWP 或 WinUI 單元測試專案中透過此方式存取測試資料,您會看到與以下內容相似的錯誤:"'TestContext' 沒有包含 'DataRow' 的定義,也找不到接受型別為 'TextContext' 第一個引數的可存取擴充方法 (您是否遺漏使用指示詞或組件參考?)"。
撰寫測試方法
AddIntegers
的測試方法相當簡單。 針對資料來源中的每個資料列,請使用 FirstNumber 和 SecondNumber 資料行值作為參數呼叫 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
索引子。 DataRow
是 DataRow 物件,因此請透過索引或資料行名稱來擷取資料行值。 因為值會以物件傳回,請將它們轉換為適當類型:
int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
執行測試並檢視結果
當您完成撰寫測試方法後,就可建置測試專案。 測試方法會顯示於 [測試總管] 中的 [未執行的測試] 群組。 當您執行、撰寫及重新執行測試時,[測試總管] 會將結果顯示在 [失敗的測試]、[通過的測試] 和 [未執行的測試] 等群組中。 您可以選擇 [全部執行] 以執行所有測試,或選擇 [執行] 以選擇要執行的一小組測試。
[測試總管] 頂端的測試結果列會隨您的測試回合產生動畫效果。 在測試回合結束時,如果所有的測試都通過,狀態列會變成綠色,如果有任何測試失敗則變成紅色。 測試回合摘要會顯示在 [測試總管] 視窗底部的詳細資料窗格中。 在底部窗格中選取某個測試以檢視該測試的詳細資料。
注意
每個資料的資料列都會有結果,也會有一個摘要結果。 如果資料的每個資料列都測試通過,執行的摘要會顯示為通過。 如果有任何資料列測試失敗,執行的摘要會顯示為失敗。
如果您在範例中執行任何 AddIntegers_FromDataRowTest
、AddIntegers_FromDynamicDataTest
或 AddIntegers_FromDataSourceTest
方法,結果列會變成紅色,而測試方法會移至 [失敗的測試]。 如果來自資料來源的任何反覆執行方法失敗,資料驅動的測試將會失敗。 當您在 [測試總管] 視窗中選擇失敗的資料驅動型測試時,詳細資料窗格會顯示每個反覆項目的結果,各反覆項目是以資料列索引識別。 在本範例中,它會顯示 AddIntegers
演算法並未正確處理負數值。
當受測方法已修正並重新執行測試時,結果列會變成綠色,且測試方法會移動到 [通過的測試] 群組。