建議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個建議》陸敏技