走進(jìn)單元測(cè)試三:實(shí)戰(zhàn)單元測(cè)試
前兩篇文章講解了一些關(guān)于單元測(cè)試的基本理論知識(shí),接下來(lái)我們應(yīng)該理論聯(lián)系實(shí)踐,在實(shí)踐中體會(huì)單元測(cè)試帶給我們的便利!
環(huán)境:VS2008,2010版本!
目錄:
1.前言
2.單元測(cè)試框架
3.斷言(Assert)
4.測(cè)試異常
5.忽視測(cè)試
6.數(shù)據(jù)驅(qū)動(dòng)測(cè)試
7.單元測(cè)試的利器 → Moles技術(shù)
1.前言
一個(gè)完整的測(cè)試必須符合以下幾點(diǎn):
A) 考慮到各種情況,準(zhǔn)備測(cè)試所需要的各種數(shù)據(jù),這一步是測(cè)試的關(guān)鍵所在!
B) 調(diào)用要測(cè)試的方法!
C) 驗(yàn)證被測(cè)試方法的行為跟預(yù)期的是否一致!
D) 完成驗(yàn)證測(cè)試之后清理各種資源!
2.單元測(cè)試框架
測(cè)試框架的DLL文件名為: Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll
在VS自動(dòng)創(chuàng)建單元測(cè)試項(xiàng)目就會(huì)自動(dòng)引用這個(gè)DLL了!
測(cè)試類的基本結(jié)構(gòu)如圖:
基本的測(cè)試類結(jié)構(gòu)就是這樣的!
3.斷言(Assert)
在單元測(cè)試代碼里斷言是無(wú)處不在的,我們應(yīng)該合理的使用斷言來(lái)驗(yàn)證數(shù)據(jù)!
它是一個(gè)靜態(tài)類,主要有下面幾種方法用來(lái)驗(yàn)證函數(shù)的結(jié)果跟我的預(yù)期是否一致!
① Assert.AreEqual
主要是驗(yàn)證函數(shù)產(chǎn)生的影響值或返回的值跟預(yù)期是否一致,這個(gè)方法不適合驗(yàn)證返回的數(shù)據(jù)集以及集合之類的數(shù)據(jù),主要針對(duì)字符串,數(shù)字等等的單一類型,它還有個(gè)泛型重載,這個(gè)是比較好的,建議多使用,它還有第三個(gè)參數(shù),是一個(gè)string型的message,基本上不會(huì)用到!
☆ Note:不要把參數(shù)的含義搞混,第一個(gè)參數(shù)是你的期望值(Except),第二個(gè)參數(shù)是函數(shù)返回或影響程序產(chǎn)生的實(shí)際值(Actual),不要把兩個(gè)顛倒過(guò)來(lái),因?yàn)槿绻\(yùn)行正確沒(méi)有什么大礙,如果運(yùn)行產(chǎn)生錯(cuò)誤,有的時(shí)候就會(huì)看錯(cuò)掉,造成判斷失誤,要注意了,這是一個(gè)規(guī)范!
例子:
Assert.AreEqual<string>("a", "a", "cheng xu yuan");
② Assert.AreNotEqual
沒(méi)什么要講的,情況跟上面相反,主要是驗(yàn)證實(shí)際值跟期望值不相等的情況!
③ Assert.AreSame
判斷實(shí)際值跟預(yù)期值的類型是否一致!
④ Assert.AreNotSame
跟上面正好相反,測(cè)試類型不一致!
⑤ Assert.IsNotNull,Assert.IsNull,Assert.IsTrue,Assert.IsFalse
看這些方法名就知道什么意思了!
⑥ Assert.IsInstanceOfType,Assert.IsNotInstanceOfType
判斷指定的對(duì)象是否是指定的類型!
⑦ Assert.Fail
迫使斷言失敗,不管前面的斷言是否都成功了,但測(cè)試結(jié)果是錯(cuò)誤的,因?yàn)槲覐?qiáng)制斷言失敗了!
總結(jié):一個(gè)好的測(cè)試案例,里面的斷言至少是大于一個(gè)的,這樣才能驗(yàn)證數(shù)據(jù)的準(zhǔn)確性,保證驗(yàn)證數(shù)據(jù)的嚴(yán)謹(jǐn)性!
例:如果實(shí)際值是個(gè)DataSet一般測(cè)試流程為:①判斷是否為“null” → ②判斷是否為“Empty”(驗(yàn)證是否有數(shù)據(jù)) →③ 接下來(lái)再驗(yàn)證數(shù)據(jù)的一致性,所以驗(yàn)證DataSet的基本流程就是這樣的!
/// <summary> Assert.IsNotNull(actual); //第一步 驗(yàn)證是否為Null |
4.測(cè)試異常 → ExpectedException(異常屬性)
當(dāng)代碼中有拋出異常的情況時(shí),我們應(yīng)該對(duì)這個(gè)異常的準(zhǔn)確性進(jìn)行測(cè)試,首先要捕獲這個(gè)異常,然后再跟我預(yù)期定義的異常進(jìn)行比較就行了!
在VS自帶的測(cè)試框架中提供了處理異常的測(cè)試!
這個(gè)異常屬性有兩個(gè)構(gòu)造函數(shù)重載:
第一個(gè)參數(shù):函數(shù)中出現(xiàn)異常的類型 → [ExpectedException(typeof(NullReferenceException))]
第二個(gè)參數(shù):異常所提示的信息(Message) → [ExpectedException(typeof(NullReferenceException),"Don't is null.")]
總結(jié):在測(cè)試一些非法數(shù)據(jù),邊界值,異常測(cè)試是非常有用的,一旦發(fā)現(xiàn)異常,后面的一切斷言和代碼將跳過(guò),然后系統(tǒng)將會(huì)把異常和你預(yù)期的異常進(jìn)行比對(duì),一致則表示通過(guò),反之有錯(cuò)誤!
/// <summary> /// Is null. /// </summary> [TestMethod()] [RollBack()] [ExpectedException(typeof(NullReferenceException))] //第一種只定義了異常類型! public void UpdateLookupChequeNumberRegion_UpdateChequeNumberRegionAndDataSetIsNull_ThrowException() { LookupChequeNumberRegionDataSet actual = target.UpdateLookupChequeNumberRegion(null); } /// <summary> /// Add. /// </summary> [TestMethod()] [RollBack()] //第二種定義了預(yù)期的異常類型還定義了異常信息! [ExpectedException(typeof(BusinessException), "The category and code combination you have entered already exist. Please enter a different category and code combination.")] public void UpdatePolicyConsideration_AddPolicyTheCODEIsSame_ThrowException() { string newGUID = Guid.NewGuid().ToString(); CodeTableDataSet expectedDataSet = target.GetPolicyConsideration(); expectedDataSet.T_IC_CODE.AddT_IC_CODERow(GetNewRow(expectedDataSet, null, ref newGUID)); CodeTableDataSet actual = target.UpdatePolicyConsideration(expectedDataSet); } |
5.忽視測(cè)試 → Ignore屬性
添加這個(gè)屬性表明現(xiàn)在這個(gè)測(cè)試案例在運(yùn)行時(shí)將不會(huì)被執(zhí)行,跳過(guò)此方法!
[TestMethod()] [Ignore()] //運(yùn)行單元測(cè)試時(shí)將忽視這個(gè)測(cè)試案例 public void GetBondDebt_InputValidClientID_RecordFound() { int clientCoreID = GetClientIDForSomeCondition(); DebtDataSet actual = target.GetBondDebt(clientCoreID); Assert.IsNotNull(actual); Assert.IsTrue(actual.Debt.Rows.Count > 0); Assert.IsTrue(CompareToDataSetAndList(actual, clientCoreID)); } |
6.數(shù)據(jù)驅(qū)動(dòng)測(cè)試
在上一篇提到過(guò)當(dāng)你的數(shù)據(jù)量很大的時(shí)候,有一種解決方案是采用數(shù)據(jù)驅(qū)動(dòng)測(cè)試,把我們需要用來(lái)測(cè)試的數(shù)據(jù)放在文件中,然后運(yùn)行測(cè)試,讓測(cè)試代碼去讀取文件中的數(shù)據(jù)!
其實(shí)它也有一定的局限性,所以在合理的場(chǎng)合中合理的使用將減輕我們的工作量,這個(gè)判斷只能給為看官去判斷了!
當(dāng)前支持Sql Server ,Oracle,CSV,XML等等文件,下面我就介紹下CSV和XML文件的使用方法!
①CSV作為數(shù)據(jù)文件
我們寫一個(gè)簡(jiǎn)單不能再簡(jiǎn)單的的加法運(yùn)算方法來(lái)作為示例:
public int Add(int numberOne, int numberTwo) { int one = numberOne; int two = numberTwo; int three = numberOne + numberTwo; return three; } |
a) 首先要?jiǎng)?chuàng)建連接字符串,具體步驟如下:
I,
II,
III,
通過(guò)上面步驟的操作,就會(huì)進(jìn)入選擇文件的界面,按照提示即可完成,當(dāng)然前提你的數(shù)據(jù)文件要準(zhǔn)備好,完成之后會(huì)出現(xiàn)如下代碼:
/// <summary> ///Add 的測(cè)試 ///</summary> [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\number.csv", "number#csv", DataAccessMethod.Sequential), //這個(gè)是連接字符串,你也可以手動(dòng)寫,不要按照上面步驟操作,當(dāng)然要寫對(duì)單詞和“注意文件的路徑”! DeploymentItem("MyTest\\number.csv"), TestMethod()] public void AddTest1() { Program target = new Program(); int numberOne = Convert.ToInt32(this.testContextInstance.DataRow["One"]); //獲取數(shù)據(jù)! int numberTwo = Convert.ToInt32(this.testContextInstance.DataRow["Two"]); ; int expected = Convert.ToInt32(this.testContextInstance.DataRow["Except"]); int actual = target.Add(numberOne, numberTwo); Assert.AreEqual(expected, actual); } |
點(diǎn)擊運(yùn)行你的測(cè)試,程序會(huì)通過(guò)你設(shè)置的路徑去文件里面一行一行的讀取數(shù)據(jù),然后驗(yàn)證數(shù)據(jù),如果其中有一行數(shù)據(jù)報(bào)錯(cuò),那么整個(gè)測(cè)試也就是失敗了,所以保證數(shù)據(jù)的正確性很重要!
注意:CSV默認(rèn)會(huì)以Excel的方式打開,但是它里面的數(shù)據(jù)擺放有一個(gè)規(guī)則,就是以逗號(hào)的形式呈現(xiàn),所以我還是建議大家使用記事本來(lái)添加數(shù)據(jù)!
②使用XML作為數(shù)據(jù)文件
其實(shí)使用方法跟上面的一樣,尤其注意的是你XML文件里面數(shù)據(jù)的格式!
<table> //一定要設(shè)置根節(jié)點(diǎn),如果不設(shè)置在文件選擇的時(shí)候會(huì)報(bào)錯(cuò)! <my> <price>1</price> <number>1</number> <total>1</total> </my> <my> <price>2</price> <number>2</number> <total>4</total> </table> |
注意:怎么來(lái)獲取數(shù)據(jù)呢?Convert.ToInt32(this.testContextInstance.DataRow["price"]) 通過(guò)這樣的方式來(lái)獲取數(shù)據(jù),還有在選擇文件時(shí),要確認(rèn)是否選中了一個(gè)數(shù)據(jù)源文件,這個(gè)不要疏忽了!
③使用數(shù)據(jù)庫(kù)作為數(shù)據(jù)文件很簡(jiǎn)單,配置一下連接字符串,設(shè)置一下路徑就好了,在此就不講解了!
7.單元測(cè)試的利器 → “Mole”技術(shù)
首先想感謝下項(xiàng)目組的Tian Mi大哥是他把這項(xiàng)技術(shù)帶給我們的,謝謝他的無(wú)私奉獻(xiàn)!
“Mole”文件的下載地址:http://www.kuaipan.cn/index.php?ac=file&oid=29568238492847207
①應(yīng)用環(huán)境:
所屬模塊依賴于系統(tǒng)的其它模塊,依賴于系統(tǒng)的一些配置環(huán)境,還有就是調(diào)用第三方接口或服務(wù)等等的場(chǎng)景!
在這樣的場(chǎng)景下我們的測(cè)試是不能直接調(diào)用第三方接口或服務(wù)的,所以我們要制造一個(gè)虛擬的環(huán)境,當(dāng)我們?nèi)フ{(diào)用接口時(shí),Mole技術(shù)會(huì)攔截我們調(diào)用的方法,從而轉(zhuǎn)向我們自己制造的虛擬環(huán)境,那么就不會(huì)直接調(diào)用第三方接口和服務(wù)了!
②基本的操作流程不講解了,如果你看了http://www.cnblogs.com/hwade/archive/2010/11/26/Moles.html這篇文章就明白了,我就將一些在寫測(cè)試時(shí)遇到的幾個(gè)小問(wèn)題!
③如果“Moles”結(jié)束后,項(xiàng)目編譯的時(shí)候報(bào)錯(cuò),具體的錯(cuò)誤我也不怎么知道了,但是我在網(wǎng)上搜了很久才找到答案的!
步驟:點(diǎn)擊你的Moles文件,你會(huì)發(fā)現(xiàn)他是個(gè)XML配置文件,修改它的屬性就好了,如:
<Moles xmlns=http://schemas.microsoft.com/moles/2010/ DisableCache="true" > //增加一個(gè)DisableCache = "true"這個(gè)屬性,如果遇到問(wèn)題,基本都是這個(gè)問(wèn)題,這樣解決就Ok了! <Assembly Name="Foundation.FinanceManagement.BusinessService" /> </Moles> |
③有“Ref”參數(shù)的方法應(yīng)該怎么寫?
如果參數(shù)中ref參數(shù),那么就不能按一般的方式寫了,應(yīng)該這樣寫:
④Moles的基本語(yǔ)法
Moles的內(nèi)部原理沒(méi)有明白,只懂的怎么去用它,基本的語(yǔ)法可以模仿上面的寫法!
因?yàn)槟惆岩粋€(gè)DLL Moles之后并編譯之后會(huì)生成一個(gè)新的DLL,這里面就是虛擬環(huán)境場(chǎng)所,而且類名稱都是以“M”開頭的!
語(yǔ)法:命名空間 + Moles + M + 你Moles掉的類名 + Allinstance + 選擇你要攔截的方法名 = (instance(必填參數(shù),如果方法沒(méi)參數(shù),也必要添個(gè))) =>
{
//里面寫你的邏輯,制造你自己的虛擬環(huán)境,如果方法有返回值,可以自己指定返回值的!
};
好了,關(guān)于在實(shí)踐中的單元測(cè)試就這么多了,其實(shí)最重要的還是你考慮測(cè)試的角度要多樣化,就是考慮問(wèn)題要全面,還有代碼的簡(jiǎn)潔性,重構(gòu),封裝等等,下一篇將是對(duì)這方面的著重討論!
相關(guān)文章:
走進(jìn)單元測(cè)試二:測(cè)試需要從哪些方面著手
走進(jìn)單元測(cè)試四:?jiǎn)卧獪y(cè)試背后的思考和感悟
posted on 2013-08-09 10:48 順其自然EVO 閱讀(223) 評(píng)論(0) 編輯 收藏