建議155:随生産代碼一起送出單元測試代碼
首先提出一個問題:我們害怕修改代碼嗎?是否曾經無數次面對亂糟糟的代碼,下決心進行重構,然後在一個月後的某個周一,卻收到來自測試版的報告:新的版本,沒有之前的版本穩定,性能也更差了,Bug似乎也變多了。也就是說,重構的代碼看上去品質更高了,可實際測試結果卻不如人意。
幾乎每個程式員都因為此類問題糾結過。我們要修改的代碼也許來自某些不負責任或經驗欠佳的程式員,也許這些代碼是自己一年前寫的,但是看上去已經慘不忍睹。我們想要修改這些代碼,卻擔心重構出别的問題。即便是一個開發周期中的産品,也會有這樣的選擇出現。某個子產品可能已經送出測試并确認通過,不過現在發現有更優的算法和邏輯,改還是不改,成了一個問題。
“單元測試”減輕甚至消除了開發者這種恐懼。如果項目沒有測試代碼,說明我們隻是生産“定時炸彈”。很多人将生産代碼和測試代碼分别對待,這是一種過時的做法。程式員在送出自己的生産代碼時,必須同時送出自己的單元測試代碼。很多現代化的版本管理工具可以在背景訂制項目建構計劃,自動運作測試項目,統計代碼覆寫率,并生成相應報告。我們應該在早上一邊喝咖啡,一邊讀取這樣的報告。
有了測試代碼做保證,在很大程度上我們可以放心去重構了。如果某個功能偏離了既有成果,就會有醒目的提醒。
将單元測試放在首要地位的一種開發模式是TDD模式。TDD(Test Driven Development測試驅動開發)有三條嚴格的定律:
- 在編寫不能通過測試的單元測試前,不要編寫任何生産代碼。
- 隻編寫恰好無法通過的單元測試,不能編譯也算不通過。
- 隻編寫剛好足以通過目前失敗測試的生産代碼。
即使我們的團隊沒有完全采用TDD的開發模式,也可以借鑒這些定律來編寫我們自己的測試代碼。我們無需一次性編寫完全部的測試代碼,那沒有必要,這跟過度設計一樣,也不可能實作。事實上,我們應該逐漸地編寫測試代碼,而且按如下步驟來編寫:
測試代碼->生産代碼->測試代碼
下面編寫一個單元測試的例子:
先編寫一個Add方法:
public class SampleClass
{
public int Add(int a, int b)
{
return a + b;
}
}
右鍵建立單元測試:
VS會自動為我們生成一個測試方法:
/// <summary>
///Add 的測試
///</summary>
[TestMethod()]
public void AddTest()
{
SampleClass target = new SampleClass(); // TODO: 初始化為适當的值
int a = 0; // TODO: 初始化為适當的值
int b = 0; // TODO: 初始化為适當的值
int expected = 0; // TODO: 初始化為适當的值
int actual;
actual = target.Add(a, b);
Assert.AreEqual(expected, actual);
Assert.Inconclusive("驗證此測試方法的正确性。");
}
将方法修改成我們需要的方法就可以了:
/// <summary>
///Add 的測試
///</summary> [TestMethod()] public void AddTest() { SampleClass target = new SampleClass(); // TODO: 初始化為适當的值 int a = 1; // TODO: 初始化為适當的值 int b = 2; // TODO: 初始化為适當的值 int expected = 3; // TODO: 初始化為适當的值 int actual; actual = target.Add(a, b); Assert.AreEqual(expected, actual); Assert.Inconclusive("驗證此測試方法的正确性。"); }
VS這個可視化測試工具太重量級了,導緻開發的過程中運作測試代碼太繁瑣也太耗時。可以考慮用測試工具TestDriven.NET,這裡不再介紹。
單元測試要注意一下幾點:
首先,單元測試不應引入任何人機互動的内容。如,測試過程中不應該彈出對話框,等待使用者輸入或确認。單元測試不應該是被阻滞的。
其次,多線程也不屬于單元測試範疇,單元測試應該是快速被執行的,而不是需要等待的。
最後,單元測試不應該跨應用程式域,例如,資料通路或者遠端通信屬于內建測試範疇,而不是單元測試。
轉自:《編寫高品質代碼改善C#程式的157個建議》陸敏技