單元測試與解耦


1.標題是什么意思?

1.1什么是單元測試?

單元測試,目的是為了保證代碼的質量;

1.2什么是解耦?

解耦,目的是為了方便單元測試。當然,另一個目的是為了保持程序的擴展性

 

思想工具:為了同時達到單元測試與代碼解耦(或者稱為設計優良的OO代碼),那么依賴注入的思想是必不可少的工具。

  • 之所以說是思想,從設計的角度來說,這確實是需要思想上的超越;
  • 之所以說是工具,是因為有許多工具可以實現這一思想,如Ninject,Unity。

簡要如下圖所示:

clip_image002

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.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM