走進單元測試三:實戰單元測試


   前兩篇文章講解了一些關於單元測試的基本理論知識,接下來我們應該理論聯系實踐,在實踐中體會單元測試帶給我們的便利!

   環境:VS2008,2010版本!

   關於怎么在VS中創建單元測試,園子里已經有很多這樣的文章了,請參考以下鏈接:  

   http://www.cnblogs.com/heqichang/archive/2011/09/30/2196779.html

         http://www.cnblogs.com/TerryFeng/archive/2009/05/24/1488333.html

         http://www.cnblogs.com/zhijianliutang/archive/2011/12/15/2288423.html

  

 目錄:

  1. 前言
  2. 單元測試框架
  3. 斷言(Assert)
  4. 測試異常
  5. 忽視測試
  6. 數據驅動測試
  7. 單元測試的利器 → Moles技術

 1.前言                             

    一個完整的測試必須符合以下幾點:    

A)  考慮到各種情況,准備測試所需要的各種數據,這一步是測試的關鍵所在!

B)  調用要測試的方法!

C)  驗證被測試方法的行為跟預期的是否一致!

D)  完成驗證測試之后清理各種資源!

 

 2.單元測試框架                          

  測試框架的DLL文件名為: Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll

             在VS自動創建單元測試項目就會自動引用這個DLL了!

    測試類的基本結構如圖:    

    

    基本的測試類結構就是這樣的!

 

 3.斷言(Assert)                              

    在單元測試代碼里斷言是無處不在的,我們應該合理的使用斷言來驗證數據!

    它是一個靜態類,主要有下面幾種方法用來驗證函數的結果跟我的預期是否一致!

    ① Assert.AreEqual

      主要是驗證函數產生的影響值或返回的值跟預期是否一致,這個方法不適合驗證返回的數據集以及集合之類的數據,主要針對字符串,數字等等的單一類型,它還有個泛型重載,這個是比較好的,建議多使用,它還有第三個參數,是一個string型的message,基本上不會用到!

       ☆ Note不要把參數的含義搞混,第一個參數是你的期望值(Except),第二個參數是函數返回或影響程序產生的實際值(Actual),不要把兩個顛倒過來,因為如果運行正確沒有什么大礙,如果運行產生錯誤,有的時候就會看錯掉,造成判斷失誤,要注意了,這是一個規范!

       例子:

                 Assert.AreEqual<string>("a", "a", "cheng xu yuan"); 

    ② Assert.AreNotEqual    

      沒什么要講的,情況跟上面相反,主要是驗證實際值跟期望值不相等的情況!

    ③ Assert.AreSame

      判斷實際值跟預期值的類型是否一致!

    ④ Assert.AreNotSame

      跟上面正好相反,測試類型不一致!

    ⑤ Assert.IsNotNull,Assert.IsNull,Assert.IsTrue,Assert.IsFalse

      看這些方法名就知道什么意思了!

    ⑥ Assert.IsInstanceOfType,Assert.IsNotInstanceOfType

      判斷指定的對象是否是指定的類型!

    ⑦ Assert.Fail

      迫使斷言失敗,不管前面的斷言是否都成功了,但測試結果是錯誤的,因為我強制斷言失敗了!

     總結:一個好的測試案例,里面的斷言至少是大於一個的,這樣才能驗證數據的准確性,保證驗證數據的嚴謹性!

     例:如果實際值是個DataSet一般測試流程為:①判斷是否為“null” → ②判斷是否為“Empty”(驗證是否有數據) →③ 接下來再驗證數據的一致性,所以驗證DataSet的基本流程就是這樣的!   

 1     /// <summary>
2      /// Modify.
3     /// </summary>
4 [TestMethod()]

5 [RollBack()]
6 public void UpdateBondAssessApplicationConfiguration_ModifyData_DataUpdated() //注意命名規范,下一篇會着重講解!
7 {
8 AssessApplicationConfigurationDataSet assessConfigurationDataSet = target.GetBondAssessApplicationConfiguration();
9 assessConfigurationDataSet.CM_LookupConfiguration[0].Description = "This is my modify";
10 AssessApplicationConfigurationDataSet actual = target.UpdateBondAssessApplicationConfiguration(assessConfigurationDataSet);
11 AssessApplicationConfigurationDataSet newAssessAppDataSet = target.GetBondAssessApplicationConfiguration();
12
13 Assert.IsNotNull(actual); //第一步 驗證是否為Null
14 Assert.IsTrue(actual.CM_LookupConfiguration.Rows.Count > 0); //第二步 驗證是否為Empty
15 Assert.IsTrue(newAssessAppDataSet.CM_LookupConfiguration[0].Description == "This is my modify");//第三步 驗證數據的一致性
16 Assert.IsTrue(CompareToTable(newAssessAppDataSet.CM_LookupConfiguration, actual.CM_LookupConfiguration));

17 }

 

 4.測試異常 → ExpectedException(異常屬性)                

    當代碼中有拋出異常的情況時,我們應該對這個異常的准確性進行測試,首先要捕獲這個異常,然后再跟我預期定義的異常進行比較就行了!

    在VS自帶的測試框架中提供了處理異常的測試!

             這個異常屬性有兩個構造函數重載:

    第一個參數:函數中出現異常的類型 →  [ExpectedException(typeof(NullReferenceException))]

    第二個參數:異常所提示的信息(Message) → [ExpectedException(typeof(NullReferenceException),"Don't is null.")]

      總結:在測試一些非法數據,邊界值,異常測試是非常有用的,一旦發現異常,后面的一切斷言和代碼將跳過,然后系統將會把異常和你預期的異常進行比對,一致則表示通過,反之有錯誤!    

 1      /// <summary>
2      /// Is null.
3      /// </summary>
4 [TestMethod()]

5 [RollBack()]
6 [ExpectedException(typeof(NullReferenceException))] //第一種只定義了異常類型!
7 public void UpdateLookupChequeNumberRegion_UpdateChequeNumberRegionAndDataSetIsNull_ThrowException()
8 {
9 LookupChequeNumberRegionDataSet actual = target.UpdateLookupChequeNumberRegion(null);
10 }
11
12 /// <summary>
13      /// Add.
14      /// </summary>
15 [TestMethod()]

16 [RollBack()]            //第二種定義了預期的異常類型還定義了異常信息!
17 [ExpectedException(typeof(BusinessException), "The category and code combination you have entered already exist. Please enter a different category and code combination.")]
18 public void UpdatePolicyConsideration_AddPolicyTheCODEIsSame_ThrowException()
19 {
20 string newGUID = Guid.NewGuid().ToString();
21 CodeTableDataSet expectedDataSet = target.GetPolicyConsideration();
22 expectedDataSet.T_IC_CODE.AddT_IC_CODERow(GetNewRow(expectedDataSet, null, ref newGUID));
23 CodeTableDataSet actual = target.UpdatePolicyConsideration(expectedDataSet);
31 }

   

 5.忽視測試 → Ignore屬性                      

    添加這個屬性表明現在這個測試案例在運行時將不會被執行,跳過此方法!    

 1      [TestMethod()]
2 [Ignore()] //運行單元測試時將忽視這個測試案例
3 public void GetBondDebt_InputValidClientID_RecordFound()

4 {
5 int clientCoreID = GetClientIDForSomeCondition();
6 DebtDataSet actual = target.GetBondDebt(clientCoreID);
7
8 Assert.IsNotNull(actual);
9 Assert.IsTrue(actual.Debt.Rows.Count > 0);
10 Assert.IsTrue(CompareToDataSetAndList(actual, clientCoreID));
11 }

  

 6.數據驅動測試                                  

    在上一篇提到過當你的數據量很大的時候,有一種解決方案是采用數據驅動測試,把我們需要用來測試的數據放在文件中,然后運行測試,讓測試代碼去讀取文件中的數據!

    其實它也有一定的局限性,所以在合理的場合中合理的使用將減輕我們的工作量,這個判斷只能給為看官去判斷了!

    當前支持Sql Server ,Oracle,CSV,XML等等文件,下面我就介紹下CSV和XML文件的使用方法!

    也可以參照園子里面的博文:

    http://www.cnblogs.com/zhijianliutang/archive/2011/12/15/2289398.html

    http://www.cnblogs.com/heqichang/archive/2011/10/08/2202441.html

    ①CSV作為數據文件

      我們寫一個簡單不能再簡單的的加法運算方法來作為示例: 

1      public int Add(int numberOne, int numberTwo)
2 {
3 int one = numberOne;
4 int two = numberTwo;
5 int three = numberOne + numberTwo;
6 return three;
7 }

       a) 首先要創建連接字符串,具體步驟如下:        

        I,  

        II,

        III,

        通過上面步驟的操作,就會進入選擇文件的界面,按照提示即可完成,當然前提你的數據文件要准備好,完成之后會出現如下代碼:      

 1     /// <summary>
2      ///Add 的測試
3      ///</summary>
4 [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV",

5 "|DataDirectory|\\number.csv",
6 "number#csv",
7 DataAccessMethod.Sequential), //這個是連接字符串,你也可以手動寫,不要按照上面步驟操作,當然要寫對單詞和“注意文件的路徑”!
8 DeploymentItem("MyTest\\number.csv"),
9 TestMethod()]
10 public void AddTest1()
11 {
12 Program target = new Program();
13 int numberOne = Convert.ToInt32(this.testContextInstance.DataRow["One"]);  //獲取數據!
14 int numberTwo = Convert.ToInt32(this.testContextInstance.DataRow["Two"]); ;
15 int expected = Convert.ToInt32(this.testContextInstance.DataRow["Except"]);
16 int actual = target.Add(numberOne, numberTwo);
17 Assert.AreEqual(expected, actual);
18 }

       點擊運行你的測試,程序會通過你設置的路徑去文件里面一行一行的讀取數據,然后驗證數據,如果其中有一行數據報錯,那么整個測試也就是失敗了,所以保證數據的正確性很重要!

       注意:CSV默認會以Excel的方式打開,但是它里面的數據擺放有一個規則,就是以逗號的形式呈現,所以我還是建議大家使用記事本來添加數據!

       

    ②使用XML作為數據文件

      其實使用方法跟上面的一樣,尤其注意的是你XML文件里面數據的格式!      

 1     <table> //一定要設置根節點,如果不設置在文件選擇的時候會報錯!   
2 <my>
3 <price>1</price>
4 <number>1</number>
5 <total>1</total>
6 </my>
7 <my>
8 <price>2</price>
9 <number>2</number>
10 <total>4</total>
11 </my>
12 </table>

      注意:怎么來獲取數據呢?Convert.ToInt32(this.testContextInstance.DataRow["price"]) 通過這樣的方式來獲取數據,還有在選擇文件時,要確認是否選中了一個數據源文件,這個不要疏忽了!

    ③使用數據庫作為數據文件很簡單,配置一下連接字符串,設置一下路徑就好了,在此就不講解了!

  

 7.單元測試的利器 → “Mole”技術                    

    首先想感謝下項目組的Tian Mi大哥是他把這項技術帶給我們的,謝謝他的無私奉獻!

    園子里面也有一篇精華文章,講解了基本用法,鏈接為:http://www.cnblogs.com/hwade/archive/2010/11/26/Moles.html

    “Mole”文件的下載地址:http://www.kuaipan.cn/index.php?ac=file&oid=29568238492847207

 

    ①應用環境:

      所屬模塊依賴於系統的其它模塊,依賴於系統的一些配置環境,還有就是調用第三方接口或服務等等的場景!

      在這樣的場景下我們的測試是不能直接調用第三方接口或服務的,所以我們要制造一個虛擬的環境,當我們去調用接口時,Mole技術會攔截我們調用的方法,從而轉向我們自己制造的虛擬環境,那么就不會直接調用第三方接口和服務了!

 

    ②基本的操作流程不講解了,如果你看了http://www.cnblogs.com/hwade/archive/2010/11/26/Moles.html這篇文章就明白了,我就將一些在寫測試時遇到的幾個小問題!

 

    ③如果“Moles”結束后,項目編譯的時候報錯,具體的錯誤我也不怎么知道了,但是我在網上搜了很久才找到答案的!

      步驟:點擊你的Moles文件,你會發現他是個XML配置文件,修改它的屬性就好了,如:      

1       <Moles  xmlns="http://schemas.microsoft.com/moles/2010/"  DisableCache="true" >  //增加一個DisableCache = "true"這個屬性,如果遇到問題,基本都是這個問題,這樣解決就Ok了!
2       <Assembly Name="Foundation.FinanceManagement.BusinessService" />
3       </Moles>

    

    ③有“Ref”參數的方法應該怎么寫?

      如果參數中ref參數,那么就不能按一般的方式寫了,應該這樣寫:

 

    ④Moles的基本語法

      Moles的內部原理沒有明白,只懂的怎么去用它,基本的語法可以模仿上面的寫法!

      因為你把一個DLL Moles之后並編譯之后會生成一個新的DLL,這里面就是虛擬環境場所,而且類名稱都是以“M”開頭的!

      語法:命名空間 + Moles + M + 你Moles掉的類名 + Allinstance + 選擇你要攔截的方法名 = (instance(必填參數,如果方法沒參數,也必要添個))  =>

         {

            //里面寫你的邏輯,制造你自己的虛擬環境,如果方法有返回值,可以自己指定返回值的!

         };

  

  好了,關於在實踐中的單元測試就這么多了,其實最重要的還是你考慮測試的角度要多樣化,就是考慮問題要全面,還有代碼的簡潔性,重構,封裝等等,下一篇將是對這方面的着重討論!

  下一篇:走進單元測試四:測試背后的思考和總結!

  鏈接:走進單元測試一:初認Unit Test

       走進單元測試二:測試需要從哪些方面着手


免責聲明!

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



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