泛型方法的單元測試
如 HOW TO:建立並執行單元測試所述,您可以產生泛型方法的單元測試,而且產生的方式與其他方法的單元測試完全相同。 下列章節提供建立泛型方法之單元測試的相關資訊與範例。
型別引數和型別條件約束
在產生泛型類別 (例如 MyList<T>) 的單元測試時,Visual Studio 會產生兩個方法:一個泛型 Helper 方法和一個測試方法。 如果 MyList<T> 具有一個或多個型別條件約束 (Constraint),則型別引數必須滿足所有的型別條件約束。 為了確定受測試的泛型程式碼是否如預期般適用於允許的所有輸入,測試方法會使用您想測試的所有條件約束來呼叫泛型 Helper 方法。
範例
下列範例說明泛型的單元測試:
編輯產生的測試程式碼。 本範例包含「產生的測試程式碼」和「編輯的測試程式碼」兩個區段。 其示範如何將由泛型方法產生之未經處理的測試程式碼,編輯成有用的測試方法。
使用型別約束條件。 本範例示範的單元測試適用於使用型別條件約束的泛型方法。 本範例中並未滿足型別條件約束。
範例 1:編輯產生的測試程式碼
此區段中的測試程式碼測試的是名稱為 SizeOfLinkedList() 的受測試程式碼方法。 這個方法會傳回指定連結串列 (Linked List) 中節點數目的整數。
<產生的測試程式碼>一節中的第一個程式碼範例顯示 Visual Studio Premium 或 Visual Studio Ultimate 所產生的原始測試程式碼。 「編輯的測試程式碼」區段中的第二個程式碼範例,則顯示如何針對 int 和 char 兩種不同的資料型別來測試 SizeOfLinkedList 方法的運作狀況。
此程式碼說明兩個方法:
測試 Helper 方法,SizeOfLinkedListTestHelper<T>()。 根據預設,測試 Helper 方法的名稱中包含 "TestHelper" 字串。
測試方法,SizeOfLinkedListTest()。 每個測試方法都會以 TestMethod 屬性 (Attribute) 標示。
產生的測試程式碼
下列測試程式碼是從 SizeOfLinkedList() 方法產生。 因為這項產生的測試尚未經過編輯,所以您必須修改它才能正確測試 SizeOfLinkedList 方法。
public void SizeOfLinkedListTestHelper<T>()
{
T val = default(T); // TODO: Initialize to an appropriate value
MyLinkedList<T> target = new MyLinkedList<T>(val); // TODO: Initialize to an appropriate value
int expected = 0; // TODO: Initialize to an appropriate value
int actual;
actual = target.SizeOfLinkedList();
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
}
[TestMethod()]
public void SizeOfLinkedListTest()
{
SizeOfLinkedListTestHelper<GenericParameterHelper>();
}
在前面的程式碼中,泛型型別參數是 GenericParameterHelper。 雖然您可以編輯此參數來提供特定的資料型別 (如下面的範例所示),但是您也可以直接執行測試,不對這個陳述式進行任何編輯。
編輯的測試程式碼
在下列程式碼中,測試方法和測試 Helper 方法都已經過編輯,使其能夠成功測試受測試程式碼方法 SizeOfLinkedList()。
測試 Helper 方法
測試 Helper 方法會執行下列步驟,而這些步驟與程式碼中標記為步驟 1 到步驟 5 的各行相對應。
建立泛型連結串列。
將四個節點附加到連結串列。 這些節點中有資料型別未知的內容。
將預期的連結串列大小指派給變數 expected。
計算連結串列的實際大小,並將它指派給變數 actual。
比較 Assert 陳述式中的 actual 與 expected。 如果 actual 不等於 expected,測試便會失敗。
測試方法
測試方法會編譯到您在執行名為 SizeOfLinkedListTest 之測試時所呼叫的程式碼中。 它會執行下列步驟,而這些步驟與程式碼中標記為步驟 6 和步驟 7 的各行相對應。
在呼叫測試 Helper 方法時指定 <int>,以驗證此測試是否適用於 integer 變數。
在呼叫測試 Helper 方法時指定 <char>,以驗證此測試是否適用於 char 變數。
public void SizeOfLinkedListTestHelper<T>()
{
T val = default(T);
MyLinkedList<T> target = new MyLinkedList<T>(val); // step 1
for (int i = 0; i < 4; i++) // step 2
{
MyLinkedList<T> newNode = new MyLinkedList<T>(val);
target.Append(newNode);
}
int expected = 5; // step 3
int actual;
actual = target.SizeOfLinkedList(); // step 4
Assert.AreEqual(expected, actual); // step 5
}
[TestMethod()]
public void SizeOfLinkedListTest()
{
SizeOfLinkedListTestHelper<int>(); // step 6
SizeOfLinkedListTestHelper<char>(); // step 7
}
注意事項 |
---|
每次 SizeOfLinkedListTest 測試執行時,都會呼叫其 TestHelper 方法兩次。 Assert 陳述式每次都必須判定為 true,測試才會通過。 如果測試失敗,可能無法確定造成失敗的是指定 <int> 的呼叫,還是指定 <char> 的呼叫。 如果要找出答案,您可以檢查呼叫堆疊,也可以在測試方法中設定中斷點,然後在執行測試時同時進行偵錯。 如需詳細資訊,請參閱 HOW TO:在 ASP.NET 方案中執行測試時偵錯。 |
範例 2:使用型別條件約束
本範例示範泛型方法的單元測試,而該泛型方法使用了未能滿足的型別條件約束。 第一個區段顯示來自受測試程式碼專案的程式碼。 其中的型別條件約束以反白顯示。
第二個區段顯示來自測試專案的程式碼。
受測試程式碼專案
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
namespace ClassLibrary2
{
public class Employee
{
public Employee(string s, int i)
{
}
}
public class GenericList<T> where T : Employee
{
private class Node
{
private T data;
public T Data
{
get { return data; }
set { data = value; }
}
}
}
}
測試專案
如同所有新產生的單元測試一樣,您必須將非結果不明的 Assert 陳述式加入至此單元測試,使它傳回有用的結果。 您不會將它們加入至標記為 TestMethod 屬性的方法,而會加入至 "TestHelper" 方法 (在此測試中的名稱為 DataTestHelper<T>())。
在這個範例中,泛型型別參數 T 具有條件約束 where T : Employee。 測試方法並未滿足此條件約束。 因此,DataTest() 方法包含 Assert 陳述式,以提醒您必須提供對 T 所設的型別條件約束。 此 Assert 陳述式的內容如下:("No appropriate type parameter is found to satisfies the type constraint(s) of T. " + "Please call DataTestHelper<T>() with appropriate type parameters.");
換句話說,當您從測試方法呼叫 DataTestHelper<T>() 方法 DataTest() 時,您必須傳遞型別為 Employee 的參數,或衍生自 Employee 的類別 (Class)。
using ClassLibrary2;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestProject1
{
[TestClass()]
public class GenericList_NodeTest
{
public void DataTestHelper<T>()
where T : Employee
{
GenericList_Shadow<T>.Node target = new GenericList_Shadow<T>.Node(); // TODO: Initialize to an appropriate value
T expected = default(T); // TODO: Initialize to an appropriate value
T actual;
target.Data = expected;
actual = target.Data;
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
}
[TestMethod()]
public void DataTest()
{
Assert.Inconclusive("No appropriate type parameter is found to satisfies the type constraint(s) of T. " +
"Please call DataTestHelper<T>() with appropriate type parameters.");
}
}
}