1.标題是什麼意思?
1.1什麼是單元測試?
單元測試,目的是為了保證代碼的品質;
1.2什麼是解耦?
解耦,目的是為了友善單元測試。當然,另一個目的是為了保持程式的擴充性。
思想工具:為了同時達到單元測試與代碼解耦(或者稱為設計優良的OO代碼),那麼依賴注入的思想是必不可少的工具。
- 之是以說是思想,從設計的角度來說,這确實是需要思想上的超越;
- 之是以說是工具,是因為有許多工具可以實作這一思想,如Ninject,Unity。
簡要如下圖所示:

2.解除外部依賴及實踐
外部依賴:配置檔案、WS、資料庫、IO等,可控性較差,是內建測試的接縫點。
為什麼要解除外部依賴,對于一個函數來說,隻關注某一功能(即SRP,除非你想把所有的事情在一個方法内做完,但這不是OO,也沒有讨論的價值)。
2.1.耦合的代碼
Eg:調用一個Web服務,最後發送郵件,但如果郵件服務挂了,剩餘的邏輯就無法判斷了,是以,郵件服務是一個外部依賴,要模拟,或者打樁。
代碼清單1:——正常耦合的代碼
public class WebService
{
public void KaoQinSign(string userName,string from, string to)
{
//check the argument
//validate
// do something logistic
MailHelper.SendMail("some content", from, to);
}
}
public class MailHelper
{
public static void SendMail(string content, string from, string to)
{
//...
}
}
對于上述的KaoQinSign方法的寫法,常見,但不易測試,嚴格來說,在TDD開發中是不容出現的,根本原因是靜态方法的存在,阻止了可測試性,當然,對于Wrapper模式就另當别論。
考慮一個問題:如果KaoQinSign執行到MailHelper.SendMail方法,但運作時郵件系統不知道出了什麼問題,異常了。
我通常的做法:将焦點轉移到了MailHelper.SendMail方法,修複之後,然後再回到KaoQinSign進行調試,如果MailHelper.SendMail有問題,繼續往前。——随着方法調用的層次越來越深,焦點轉移的次數越來越遠,Bug率會很高。一般來說,調試的成功率和工作經驗成反比。
2.2 接口注入
代碼清單2:使用接口注入來解耦
public class WebService
{
public void KaoQinSign(IMail mail,string userName, string from, string to)
{
//check the argument
//validate
// do something logistic
mail.SendMail("some content", from, to);
}
}
public interface IMail
{
void SendMail(string content, string from, string to);
}
public class MailStub : IMail
{
public void SendMail(string content, string from, string to) { }
}
上述代碼,解除了對外部郵件系統的依賴,使KaoQinSign具有可測試性,如果對模拟架構有所了解,那麼使用Moq就可以輕松地模拟一個IMail接口,進而使代碼開發和測試能夠一路向前。
2.3 模拟與測試
代碼清單3:使用模拟架構進行方法的測試
[TestFixture]
public class WebServiceTests
{
[Test]
public void Method1_When_Exception_Will_SendMail()
{
WebService ws = new WebService();
//模拟郵件服務
Moq.Mock<IMail> mockMail = new Moq.Mock<IMail>();
//Verifiable表示:将要驗證SendMail是否被調用
mockMail.Setup(zw => zw.SendMail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Verifiable();
ws.KaoQinSign(mockMail.Object,,"5299530", "One", "Other");
//驗證是否被調用
mockMail.Verify();
}
}
代碼清單3,模拟郵件服務意思是這個服務是假的,用來確定這個方法通過的。
總結
單元測試的目的——確定(專業點來說稱為斷言)某一分支能夠正确地執行。
耦合的常見:
- 靜态方法;
- 一個函數幹了幾百件事情;
- 一個函數内容有幾百個流程。
而解除外部依賴是常用的OO編碼方法。而上述的靜态方法就是典型的,符合二八定律。
使用
- SRP確定函數功能的唯一性;
- 針對接口程式設計(IOC);
- 使編碼具有可測試性(提取接口以及依賴注入)
才是TDD的最佳實踐,同時,TDD是開發能夠有效地橫向覆寫(BFS式前進),而不需要使用DFS式地向前開發、調試,進而避免了DFS帶來的大腦爆棧。~~~ come from hp.