前兩篇文章講解了一些關於單元測試的基本理論知識,接下來我們應該理論聯系實踐,在實踐中體會單元測試帶給我們的便利!
環境: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.前言
一個完整的測試必須符合以下幾點:
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(必填參數,如果方法沒參數,也必要添個)) =>
{
//里面寫你的邏輯,制造你自己的虛擬環境,如果方法有返回值,可以自己指定返回值的!
};
好了,關於在實踐中的單元測試就這么多了,其實最重要的還是你考慮測試的角度要多樣化,就是考慮問題要全面,還有代碼的簡潔性,重構,封裝等等,下一篇將是對這方面的着重討論!
下一篇:走進單元測試四:測試背后的思考和總結!