下面我將編寫一個 IntegerList 類來介紹事先測試開發(fā)的工作原理。IntegerList 是 ArrayList 類的變體,用于在本地存儲整數(shù),因此不存在裝箱和取消裝箱的開銷。
第一步是創(chuàng)建一個控制臺項目,并向其中添加一個 IntegerList.cs 源文件。要連接 nUnit 框架,需要添加對 nUnit 框架的引用。在我的系統(tǒng)中,它們位于 d:\program files\nUnit v2.0\bin 。
第二步是花些時間考慮如何對該類進行測試。這與確定類應該具備哪些功能的過程類似,但重點放在功能的特定用途(將值 1 添加到列表并檢查是否成功),而不是功能本身(將一個項目添加到列表)。要生成此類,我們首先要提供一個要使用的測試列表:
- 測試該類可以構造
- 將兩個整數(shù)添加到列表,并確保數(shù)目和項目都正確。
- 執(zhí)行同一操作,但針對更多的項目。
- 將此列表轉換為一個字符串。
- 使用 foreach 枚舉此列表。
此示例從某種程度上代表了我開始時的想法,即希望這個類執(zhí)行的操作。多數(shù)類一次只會創(chuàng)建一小部分,測試應隨著類的增長而添加。
現(xiàn)在我可以開始了。我創(chuàng)建一個名為 IntegerListTest.cs 的新 C# 類文件,用于存放所有測試。下面是包含第一個測試的文件: using System;
using System.Collections;
using NUnit.Framework;
namespace IntegerList
{
/// <summary>
/// IntegerClassTest 的摘要說明。
/// </summary>
[TestFixture]
public class IntegerClassTest
{
[Test]
public void ListCreation()
{
IntegerList list = new IntegerList();
Assertion.AssertNotNull(list);
}
}
}
[TestFixture] 屬性將此類標記為測試類,[Test] 屬性將 ListCreation() 方法標記為測試方法。在此方法中,我創(chuàng)建了一個列表,然后使用 Assertion 類測試對象 gets 已經(jīng)創(chuàng)建。
我啟動 nUnit GUI 測試程序,打開可執(zhí)行文件,并執(zhí)行這些測試。將獲得如下顯示。
圖 1:顯示測試結果的 nUnit GUI
這表明所有測試都已通過。現(xiàn)在我想添加一些真實功能。第一個操作就是向列表中添加一個整數(shù)。此測試如下所示: [Test]
public void TestSimpleAdd()
{
IntegerList list = new IntegerList();
list.Add(5);
list.Add(10);
Assertion.AssertEquals(2, list.Count);
Assertion.AssertEquals(5, list[0]);
Assertion.AssertEquals(10, list[1]);
}
在此測試中,我選擇同時測試兩個操作:
- 列表正確維護 Count 屬性。
- 列表可以包含兩個項。
某些測試驅動開發(fā)的倡議者提倡測試應盡可能只測試數(shù)目,但是如果只測試數(shù)目而不測試項目,這對于我而言有些不可思議,因此我所選擇的是兩者一起測試。
編譯這段代碼時,由于 IntegerList 類中沒有方法,因此編譯失敗,為此我加上以下代碼進行編譯: public int Count
{
get
{
return -1;
}
}
public void Add(int value)
{
}
public int this[int index]
{
get
{
return -1;
}
}
然后我返回并運行測試,這時它們顯示為紅色,表示測試失敗。這很好,因為它意味著測試實際上已測試出程序錯誤,F(xiàn)在我可以執(zhí)行此實現(xiàn)。我可以做些簡單的工作,盡管這樣做效率不是很高: public int Count
{
get
{
return elements.Length;
}
}
public void Add(int value)
{
int newIndex;
if (elements != null)
{
int[] newElements = new int[elements.Length + 1];
for (int index = 0; index < elements.Length;
index++)
{
newElements[index] = elements[index];
}
newIndex = elements.Length;
elements = newElements;
}
else
{
elements = new int[1];
newIndex = 0;
}
elements[newIndex] = value;
}
public int this[int index]
{
get
{
return elements[index];
}
}
我現(xiàn)在已經(jīng)完成類的一小部分,并已經(jīng)編寫了可確保其正常工作的測試,但我僅僅測試了項目中很少的一部分。接下來,我要編寫一個用于檢查 1000 個項的測試: [Test]
public void TestOneThousandItems()
{
list = new IntegerList();
for (int i = 0; i < 1000; i++)
{
list.Add(i);
}
Assertion.AssertEquals(1000, list.Count);
for (int i = 0; i < 1000; i++)
{
Assertion.AssertEquals(i, list[i]);
}
}
此測試運行正常,因此無須進行任何更改。
添加 ToString() 方法
接下來,我將添加測試代碼,以測試 ToString() 能否正常運行: [Test]
public void TestToString()
{
IntegerList list = new IntegerList();
list.Add(5);
list.Add(10);
string t = list.ToString();
Assertion.AssertEquals("5, 10", t.ToString());
}
失敗了,沒關系。以下代碼可以使其通過: public override string ToString()
{
string[] items = new string[elements.Length];
for (int index = 0; index < elements.Length; index++)
{
items[index] = elements[index].ToString();
}
return String.Join(", ", items);
}
啟用 Foreach
許多用戶希望能夠使用 foreach 遍歷我的列表。為此,我需要在類中實現(xiàn) Ienumerable,并定義一個單獨的用于實現(xiàn) Ienumerable 的類。第一步,測試: [Test]
public void TestForeach()
{
IntegerList list = new IntegerList();
list.Add(5);
list.Add(10);
list.Add(15);
list.Add(20);
ArrayList items = new ArrayList();
foreach (int value in list)
{
items.Add(value);
}
Assertion.AssertEquals("Count", 4, items.Count);
Assertion.AssertEquals("index 0", 5, items[0]);
Assertion.AssertEquals("index 1", 10, items[1]);
Assertion.AssertEquals("index 2", 15, items[2]);
Assertion.AssertEquals("index 3", 20, items[3]);
}
我還通過 IntegerList 實現(xiàn) IEnumerable: public IEnumerator GetEnumerator()
{
return null;
}
運行測試時,此代碼生成異常。為了正確地實現(xiàn)此功能,我將使用一個嵌套類作為枚舉器。 class IntegerListEnumerator: IEnumerator
{
IntegerList list;
int index = -1;
public IntegerListEnumerator(IntegerList list)
{
this.list = list;
}
public bool MoveNext()
{
index++;
if (index == list.Count)
return(false);
else
return(true);
}
public object Current
{
get
{
return(list[index]);
}
}
public void Reset()
{
index = -1;
}
}
此類將一個指針傳遞給 IntegerList 對象,然后只返回此對象中的元素。
這樣,便可以對列表執(zhí)行 foreach 操作,但遺憾的是 Current 屬性屬于對象類型,這意味著每個值將被裝箱才能將其返回。此問題可采用一種基于模式的方法加以解決,此方法酷似當前方法,但它通過 GetEnumerator() 返回一個真正的類(而非 IEnumerator),且此類中的 Current 屬性為 int 類型。
然而執(zhí)行此操作后,我要確保在不支持該模式的語言中仍然可以使用這種基于接口的方法。我將復制編寫的上一個測試并修改 foreach 以轉換為接口: foreach (int value in (IEnumerable) list)
只需少許改動,列表即可在兩種情況下正常運行。請查看代碼樣例以獲取更多細節(jié)和更多測試。
出處:Microsoft
責任編輯:藍色
上一頁 事先測試開發(fā) 下一頁
◎進入論壇網(wǎng)絡編程版塊參加討論
|