关于C#程序的单元测试
目錄
- 1.單元測(cè)試概念
- 2.單元測(cè)試的原則
- 3.單元測(cè)試簡(jiǎn)單示例
- 4.單元測(cè)試框架特性標(biāo)簽
- 5.單元測(cè)試中的斷言Assert
- 6.單元測(cè)試中驗(yàn)證預(yù)期的異常
- 7.單元測(cè)試中針對(duì)狀態(tài)的間接測(cè)試
- 8.單元測(cè)試在MVC模式中的實(shí)現(xiàn)
- 8.單元測(cè)試相關(guān)參考
- 9.示例源代碼下載
志銘-2020年1月23日 11:49:41
1.單元測(cè)試概念
-
什么是單元測(cè)試?
單元測(cè)試(unit testing)是一段自動(dòng)化的代碼,用來(lái)調(diào)用被測(cè)試的方法或類,而后驗(yàn)證基于該方法或類的邏輯行為的一些假設(shè)。
簡(jiǎn)而言之說(shuō):單元測(cè)試是一段代碼(通常一個(gè)方法)調(diào)用另外一段代碼,隨后檢驗(yàn)一些假設(shè)的正確性。
在過(guò)程化編程中,一個(gè)單元就是單個(gè)程序、函數(shù)、過(guò)程等;
對(duì)于面向?qū)ο缶幊?#xff0c;最小單元就是方法,包括基類(超類)、抽象類、或者派生類(子類)中的方法。
-
為什么要單元測(cè)試?
單元測(cè)試的目標(biāo)是隔離程序部件并證明這些單個(gè)部件是正確的。單元測(cè)試在軟件開(kāi)發(fā)過(guò)程的早期就能發(fā)現(xiàn)問(wèn)題。
在代碼重構(gòu)或是修改的時(shí)候,可以根據(jù)單元測(cè)試快速驗(yàn)證新修改的代碼的正確性,換句話說(shuō)為了方便系統(tǒng)的后期維護(hù)升級(jí)!
單元測(cè)試某種程度上相當(dāng)于系統(tǒng)的文檔。借助于查看單元測(cè)試提供的功能和單元測(cè)試中如何使用程序單元,開(kāi)發(fā)人員可以直觀的理解程序單元的基礎(chǔ)API,即提高了代碼的可讀性!
若是開(kāi)發(fā)流程按照測(cè)試驅(qū)動(dòng)開(kāi)發(fā)則先行編寫的單元測(cè)試案例就相當(dāng)于:軟件工程瀑布模式中第二階段——設(shè)計(jì)階段的文檔
使用測(cè)試驅(qū)動(dòng)開(kāi)發(fā),可以避免實(shí)際開(kāi)發(fā)中編程人員不完全按照文檔規(guī)范,因?yàn)槭腔趩卧獪y(cè)試設(shè)計(jì)方法,開(kāi)發(fā)人員不遵循設(shè)計(jì)要求的解決方案永遠(yuǎn)不會(huì)通過(guò)測(cè)試。 -
什么時(shí)候需要單元測(cè)試?
“單元測(cè)試通常被認(rèn)為是編碼階段的附屬工作。可以在編碼開(kāi)始之前或源代碼生成之后進(jìn)行單元測(cè)試的設(shè)計(jì)。”——《軟件工程:實(shí)踐者的研究方法》
對(duì)于需要長(zhǎng)期維護(hù)的項(xiàng)目,單元測(cè)試可以說(shuō)是必須的
通常來(lái)說(shuō),程序員每修改一次程序就會(huì)進(jìn)行最少一次單元測(cè)試,在編寫程序的過(guò)程中前后很可能要進(jìn)行多次單元測(cè)試,以保證沒(méi)有程序錯(cuò)誤;雖然單元測(cè)試不是必須的,但也不壞,這牽涉到項(xiàng)目管理的政策決定。
-
單元測(cè)試誰(shuí)來(lái)編寫?
不需要專門的軟件測(cè)試人員編寫測(cè)試案例,單元測(cè)試通常由軟件開(kāi)發(fā)人員編寫。
也正式因?yàn)槭情_(kāi)發(fā)人員自己寫單元測(cè)試部分,也可以讓開(kāi)發(fā)者仔細(xì)的思考自己方法和接口是否可以更加便于調(diào)用
-
單元測(cè)試局限性
不能發(fā)現(xiàn)集成錯(cuò)誤、性能問(wèn)題、或者其他系統(tǒng)級(jí)別的問(wèn)題。單元測(cè)試結(jié)合其他軟件測(cè)試活動(dòng)更為有效。
-
單元測(cè)試框架
通常在沒(méi)有特定框架支持下,自行創(chuàng)建一個(gè)項(xiàng)目作為單元測(cè)試項(xiàng)目完全是可行的。
使用單元測(cè)試框架,同時(shí)配合編輯器VS,編寫單元測(cè)試相對(duì)來(lái)說(shuō)會(huì)簡(jiǎn)單許多。
.NET下的單元測(cè)試框架:MSTest、NUnit
?
?
2.單元測(cè)試的原則
根本原則:
- Automatic(自動(dòng)化)
單元測(cè)試應(yīng)該是全自動(dòng)執(zhí)行的,并且非交互式的 - Independent
單元測(cè)試方法的執(zhí)行順序無(wú)關(guān)緊要
單元測(cè)試的各個(gè)方法之間不應(yīng)該相互依賴 - Repeatable
功能代碼不改的前提下,相同的測(cè)試代碼多次運(yùn)行,應(yīng)該得到相同的結(jié)果 - Self-validating
單元測(cè)試方法只有兩個(gè)可能的運(yùn)行結(jié)果:通過(guò)或失敗,沒(méi)有第三種情況。
其他一些規(guī)范:
-
最理想的情況下,應(yīng)該盡量多寫測(cè)試用例,以保證代碼功能的正確性符合預(yù)期,具有良好的容錯(cuò)性。如果代碼較復(fù)雜,條件分支較多,測(cè)試用例最好能覆蓋所有的分支路徑。
-
實(shí)際開(kāi)發(fā)中,沒(méi)有必要對(duì)每一個(gè)函數(shù)都進(jìn)行單元測(cè)試。但是若是一個(gè)比較獨(dú)立的功能(當(dāng)然也可能這個(gè)功能就一個(gè)函數(shù)),應(yīng)該對(duì)這個(gè)功能進(jìn)行比較詳盡的測(cè)試。
-
單元測(cè)試的基本目標(biāo):語(yǔ)句覆蓋率達(dá)到 70%;核心模塊的語(yǔ)句覆蓋率和分支覆蓋率都要達(dá)到 100%。
-
注意一個(gè)類中可能有許多方法,我們不是要把所有的方法的單元測(cè)試都寫完,在去實(shí)現(xiàn)代碼,而是寫完一個(gè)單元測(cè)試,就去實(shí)現(xiàn)一個(gè)方法,是一種快速的迭代
-
不測(cè)試私有方法,因?yàn)樗接蟹椒ú槐煌獠空{(diào)用,測(cè)試意義不大,而且你非要測(cè)試,那就要使用反射,比較麻煩。
-
一個(gè)測(cè)試只測(cè)試一個(gè)功能
?
?
3.單元測(cè)試簡(jiǎn)單示例
3.1一個(gè)簡(jiǎn)單的手寫單元測(cè)試實(shí)例
為了簡(jiǎn)潔明了的說(shuō)明什么是單元測(cè)試,首先不使用單元測(cè)試框架,自行編寫單元測(cè)試項(xiàng)目
比如說(shuō)新建了一個(gè)類Calculator用于對(duì)數(shù)據(jù)的計(jì)算,
如下只是隨便的的寫了個(gè)方法,方便理解:
public class Calculator {//求一個(gè)數(shù)的二倍public int DoubleValue(int i){return i * 2;} }新建了Calculator類之后,我們編寫單元測(cè)試代碼對(duì)該類中方法進(jìn)行單元測(cè)試:
首先新建一個(gè)項(xiàng)目,對(duì)待測(cè)試的方法所在的項(xiàng)目添加引用,
編寫代碼,測(cè)試ClassLib項(xiàng)目中Calculator類中的DoubleValue()方法
測(cè)試DoubleValue(int value),該函數(shù)是求一個(gè)數(shù)的二倍,給其一個(gè)參數(shù)value=2,則期望其得到的結(jié)果是4,若是其他值則說(shuō)明函數(shù)編寫是錯(cuò)誤的,測(cè)試不通過(guò)。若是該函數(shù)的運(yùn)行結(jié)果和期望的結(jié)果一樣則運(yùn)行通過(guò)
public static void CalculatorDoubleValueTest() { //生成一個(gè)測(cè)試對(duì)象的實(shí)例Calculator obj = new Calculator();//設(shè)計(jì)測(cè)試案例int value = 2;int expected = 4;//與預(yù)期比較if (expected == obj.DoubleValue(value)){Console.WriteLine("測(cè)試通過(guò)");}else{Console.WriteLine($"測(cè)試未通過(guò),測(cè)試的實(shí)際結(jié)果是{obj.DoubleValue(value)}");}Console.ReadKey(); }通過(guò)上面的示例,簡(jiǎn)單的演示了單元測(cè)試是什么,但是實(shí)際中一般都是使用已有的單元測(cè)試框架。而且測(cè)試一個(gè)方法為了完備性一般都要到所有的邏輯路徑進(jìn)行測(cè)試,所以會(huì)對(duì)一個(gè)方法寫多個(gè)測(cè)試方法。
3.2單元測(cè)試框架MSTest
單元測(cè)試一般都是使用現(xiàn)成的單元測(cè)試框架,關(guān)于.net的單元測(cè)試框架有許多,常見(jiàn)的有NUnit,MSTest等等。
這里使用VS自帶的MStest框架做簡(jiǎn)單的演示(一般推薦使用NUnit框架:Undone)
演示的案例,繼續(xù)對(duì)上述的Calculator類中的DoubleValue()進(jìn)行單元測(cè)試
注意:通常的做法是為每個(gè)被測(cè)項(xiàng)目建立一個(gè)測(cè)試項(xiàng)目,為每個(gè)被測(cè)類建立一個(gè)測(cè)試類,并且為每個(gè)被測(cè)方法至少建立一個(gè)測(cè)試方法。
新建項(xiàng)目--->選擇測(cè)試類項(xiàng)目中的單元測(cè)試項(xiàng)目,命名為"被測(cè)試項(xiàng)目名+Tests"
測(cè)試類的命名為“被測(cè)試的類+Tests”
測(cè)試函數(shù)的命名按照 :**[被測(cè)方法]_ [測(cè)試場(chǎng)景]_[預(yù)期行為]** 格式命名
- 方法名——被測(cè)試的方法
- 測(cè)試場(chǎng)景——能產(chǎn)生預(yù)期行為的條件
- 預(yù)期行為——在給定條件下,期望被測(cè)試方法產(chǎn)生什么結(jié)果
當(dāng)然在VS中也可以在想要測(cè)試的函數(shù)上右鍵,創(chuàng)建單元測(cè)試,彈出如下窗口,直接點(diǎn)擊確定即可,即可生成默認(rèn)的單元測(cè)試代碼模版
這里先使用默認(rèn)自帶的MSTest框架,使用默認(rèn)的命名格式,會(huì)自動(dòng)生成相應(yīng)的測(cè)試項(xiàng)目和測(cè)試函數(shù)格式。
編寫單元測(cè)試的代碼,一般按照以下四步編寫:
Arrange:配置測(cè)試對(duì)象
TestCase:準(zhǔn)備測(cè)試案例
Act:操作測(cè)試對(duì)象
Assert:對(duì)操作斷言
//注意 [TestClass]和[TestClass()],[TestMethod()]和[TestMethod]寫法等價(jià) namespace ClassLib.Tests {[TestClass()]//通過(guò)標(biāo)注該特性標(biāo)簽表明該類為測(cè)試類public class CalculatorTests{[TestMethod()]//通過(guò)標(biāo)注該特性標(biāo)簽表明該函數(shù)為測(cè)試函數(shù)public void DoubleValueTest_DoubleValue_ReturnTrue(){//Arrange:準(zhǔn)備,實(shí)例化一個(gè)帶測(cè)試的類Calculator obj = new Calculator();//Test Case:設(shè)計(jì)測(cè)試案例int value = 2;int expected = 4;//Act:執(zhí)行int actual = obj.DoubleValue(value);//Assert:斷言Assert.AreEqual(expected, actual);}} }點(diǎn)擊測(cè)試-->運(yùn)行-->所有測(cè)試
或點(diǎn)擊測(cè)試-->窗口-->測(cè)試資源管理器-->運(yùn)行所有測(cè)試
上面運(yùn)行顯示測(cè)試通過(guò)顯示的是綠色的標(biāo)志,若是測(cè)試不通過(guò)則會(huì)則顯示紅色標(biāo)志,在單元測(cè)試中有一種“紅綠燈”的概念(你是使用其他的單元測(cè)試框架也是同樣的紅綠標(biāo)志)。
在測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的流程中,就是“紅燈-->修改-->綠燈-->重構(gòu)-->綠燈”的開(kāi)發(fā)流程。
注意:我是使用的不是VS Enterprise版本故無(wú)法直接查看代碼的測(cè)試覆蓋率,可以使用插件OpenCover或NCover等其他工具查看單元測(cè)試的覆蓋率。
上面只是演示了怎么進(jìn)行一次單元測(cè)試,但是實(shí)際中我們的測(cè)試案例不能僅僅一個(gè),所以要添加多個(gè)測(cè)試,以提高到測(cè)試的完備性
若是對(duì)需要大量測(cè)試案例的,可以把測(cè)試數(shù)據(jù)存放在專門的用于測(cè)試使用的數(shù)據(jù)庫(kù)中,在測(cè)試時(shí)通過(guò)連接數(shù)據(jù)庫(kù),使用數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行測(cè)試
依舊是上面的示例,把大量的測(cè)試案例存放在數(shù)據(jù)庫(kù)
Id Input Expected -------------------- ----------- ----------- 1 2 4 2 6 12 3 13 26 4 0 0 5 -2 -4單元測(cè)試的代碼如下
public TestContext TestContext { get; set; }//注意為了獲取數(shù)據(jù)庫(kù)的數(shù)據(jù),我們要自定義一個(gè)TestContext屬性 [TestMethod()] [DataSource("System.Data.SqlClient",@"server=.;database=db_Tome1;uid=sa;pwd=shanzhiming",//數(shù)據(jù)庫(kù)連接字符串"tb_szmUnitTestDemo",//測(cè)試數(shù)據(jù)存放的表DataAccessMethod.Sequential)]//對(duì)表中的數(shù)據(jù)測(cè)試的順序,可以是順序的,也可以是隨機(jī)的,這里是我們選擇順序 public void DoubleValueTest_DoubleValue_ReturnTrue() {//ArrangeCalculator target = neCalculator();//TestCaseint value = Convert.ToInt(TestContext.DataR["Input"]);int expected Convert.ToInt(TestContext.DataR["Expected"]);//Actint actual target.DoubleValu(value);//AssertAssert.AreEqual(expected, actual); }說(shuō)明:
特性標(biāo)簽[TestClass]?[TestMethod]
MSTest框架通過(guò)標(biāo)簽識(shí)別并加載測(cè)試
[TestClass]用來(lái)標(biāo)識(shí)包含一個(gè)MSTest自動(dòng)好測(cè)試的類,
[TestMethod]用來(lái)標(biāo)識(shí)需要被調(diào)用的自動(dòng)化測(cè)試的方法
特性標(biāo)簽[DataSource]標(biāo)識(shí)用來(lái)測(cè)試的數(shù)據(jù)源,其的參數(shù)如下:
-
第一個(gè)參數(shù)是providername,即使用的數(shù)據(jù)源的命名空間,其實(shí)我們也是可是使用Excel表格的(菜單“項(xiàng)目”-->添加新的數(shù)據(jù)源……)參考:CSDN:vs2015數(shù)據(jù)驅(qū)動(dòng)的單元測(cè)試
providername值參考:
-
"system.data.sqlclient"?----說(shuō)明使用的是mssqlserver數(shù)據(jù)庫(kù)
-
"system.data.sqllite"?----說(shuō)明使用的是sqllite數(shù)據(jù)庫(kù)
-
"system.data.oracleclient"?----說(shuō)明使用的是oracle數(shù)據(jù)庫(kù)或
-
"mysql.data.mysqlclient"?----說(shuō)明使用的是mysql數(shù)據(jù)庫(kù)
-
-
第二個(gè)參數(shù)是connectionString,我習(xí)慣是這樣寫:
@"server=.;database=數(shù)據(jù)庫(kù);uid=用戶ID;pwd=密碼"
但是推薦這樣寫:
@"Data Source=localhost;Initial Catalog=數(shù)據(jù)庫(kù);User ID=用戶ID;Password=密碼"
-
第三個(gè)參數(shù)是tablename,選擇使用的數(shù)據(jù)庫(kù)中的哪張表
-
第四個(gè)參數(shù)確定對(duì)表中的數(shù)據(jù)測(cè)試的順序.
可以是順序的:DataAccessMethod.Sequential,
可以是隨機(jī)的:DataAccessMethod.Random
?
?
4.單元測(cè)試框架特性標(biāo)簽
在MSTest單元測(cè)試框架中主要有以下的一些特性標(biāo)簽:
(參考)
| [TestClass] | 定義一個(gè)測(cè)試類,里面可以包含很多測(cè)試函數(shù)和初始化、銷毀函數(shù)(以下所有標(biāo)簽和其他斷言)。 |
| [TestMethod] | 定義一個(gè)獨(dú)立的測(cè)試函數(shù)。 |
| [ClassInitialize] | 定義一個(gè)測(cè)試類初始化函數(shù),每當(dāng)運(yùn)行測(cè)試類中的一個(gè)或多個(gè)測(cè)試函數(shù)時(shí),這個(gè)函數(shù)將會(huì)在測(cè)試函數(shù)被調(diào)用前被調(diào)用一次(在第一個(gè)測(cè)試函數(shù)運(yùn)行前會(huì)被調(diào)用)。 |
| [ClassCleanup] | 定義一個(gè)測(cè)試類銷毀函數(shù),每當(dāng)測(cè)試類中的選中的測(cè)試函數(shù)全部運(yùn)行結(jié)束后運(yùn)行(在最后一個(gè)測(cè)試函數(shù)運(yùn)行結(jié)束后運(yùn)行)。 |
| [TestInitialize] | 定義測(cè)試函數(shù)初始化函數(shù),每個(gè)測(cè)試函數(shù)運(yùn)行前都會(huì)被調(diào)用一次。 |
| [TestCleanup] | 定義測(cè)試函數(shù)銷毀函數(shù),每個(gè)測(cè)試函數(shù)執(zhí)行完后都會(huì)被調(diào)用一次。 |
| [AssemblyInitialize] | 定義測(cè)試Assembly初始化函數(shù),每當(dāng)這個(gè)Assembly中的有測(cè)試函數(shù)被運(yùn)行前,會(huì)被調(diào)用一次(在Assembly中第一個(gè)測(cè)試函數(shù)運(yùn)行前會(huì)被調(diào)用)。 |
| [AssemblyCleanup] | 定義測(cè)試Assembly銷毀函數(shù),當(dāng)Assembly中所有測(cè)試函數(shù)運(yùn)行結(jié)束后,運(yùn)行一次。(在Assembly中所有測(cè)試函數(shù)運(yùn)行結(jié)束后被調(diào)用) |
| [Ignore] | 跳過(guò)(忽略)該測(cè)試函數(shù) |
| [TestCategory("測(cè)試類別")] | 給測(cè)試自定義分類,便于有選擇的運(yùn)行指定類別的單元測(cè)試 |
說(shuō)明:
-
使用[ClassInitialize]和[ClassCleanup]標(biāo)簽特性
可以在測(cè)試之前或之后方便地控制測(cè)試的初始化和清理,從而確保所有的測(cè)試都是使用新的未更改的狀態(tài)。
注意,這是很有必要的,可以有效的防止測(cè)試失敗是因?yàn)闇y(cè)試之間的依賴性導(dǎo)致失敗。
注意兩個(gè)標(biāo)簽特性需要放在一個(gè)無(wú)返回值的靜態(tài)方法上,
且標(biāo)注[ClassInitialize]特性的方法的參數(shù)是:TestContext testcontext
示例:比如說(shuō)在一個(gè)測(cè)試類初始化一個(gè)測(cè)試對(duì)象,并在測(cè)試完成后釋放,代碼如下:
?
?
5.單元測(cè)試中的斷言Assert
斷言是什么?可以從字面理解是“十分肯定的說(shuō)”,在編程中可以通過(guò) 不同的斷言來(lái)測(cè)試方法實(shí)際運(yùn)行的結(jié)果和你期望的結(jié)果是否一致。
斷言是單元測(cè)試最基本的組成部分,Assert類的靜態(tài)方法提供了不同形式的多種斷言。
MStest中Assert的常用靜態(tài)方法:(參考):
| Assert.AreEqual() | 驗(yàn)證值相等 |
| Assert.AreNotEqual() | 驗(yàn)證值不相等 |
| Assert.AreSame() | 驗(yàn)證引用相等 |
| Assert.AreNotSame() | 驗(yàn)證引用不相等 |
| Assert.Inconclusive() | 暗示條件還未被驗(yàn)證 |
| Assert.IsTrue() | 驗(yàn)證條件為真 |
| Assert.IsFalse() | 驗(yàn)證條件為假 |
| Assert.IsInstanceOfType() | 驗(yàn)證實(shí)例匹配類型 |
| Assert.IsNotInstanceOfType() | 驗(yàn)證實(shí)例不匹配類型 |
| Assert.IsNotNull() | 驗(yàn)證條件為NULL |
| Assert.IsNull() | 驗(yàn)證條件不為 NULL |
| Assert.Fail() | 驗(yàn)證失敗 |
針對(duì)字符串的斷言,使用StringAssert的靜態(tài)方法:
注意可以根據(jù)VS的只能提示自行查看StringAssert的所有靜態(tài)方法,或是查看StringAssert的定義,可以查看其所有的靜態(tài)方法
詳細(xì)使用可參考
| StringAssert.AreEqualIgnoringCase(string expected,string actual) | 用于斷言 兩個(gè)字符串在不區(qū)分大小寫情況下是否相等,需要提供兩個(gè)參 數(shù),第一個(gè)是期待的結(jié)果,第二個(gè)是實(shí)際結(jié)果. |
| StringAssert.Contains() | 用于斷言一個(gè)字符串是否包含另一字符串,其中第一個(gè)參數(shù)為被包含的字符串,第二個(gè)為實(shí)際字符串 |
| StringAssert.StartsWith() | 斷言字符串是否以某(幾)字符開(kāi)始, 第一個(gè)參數(shù)為開(kāi)頭的字符串 ,第二個(gè)為實(shí)際字符串 |
| StringAssert.EndsWith() | 斷言字符串是否以某(幾)字符結(jié)束 |
| StringAssert.Matches() | 斷言字符串是否符合特定的正則表達(dá)式 |
針對(duì)集合的斷言,使用CollectionAssert的靜態(tài)方法:
注意可以根據(jù)VS的只能提示自行查看CollectionAssert所有的靜態(tài)方法,或是查看CollectionAssert的定義,可以查看其所有的靜態(tài)方法
詳細(xì)使用可參考
| CollectionAssert.AllItemsAreNotNull | 斷言集合里的元素全部不是Null,也即集合不包含null元素,這個(gè)方法只有一個(gè)參數(shù),傳入我們要判斷的集合即可 |
| CollectionAssert.AllItemsAreUnique | 斷言集合里面的元素全部是惟一的,即集合里沒(méi)有重復(fù)元素. |
| CollectionAssert.AreEqual | 用于斷言兩個(gè)集合是否相等 |
| CollectionAssert.AreEquivalent | 用來(lái)判斷兩個(gè)集合的元素是否等價(jià),如果兩個(gè)集合元素類型相同,個(gè)數(shù)也相同,即視為等價(jià),與上面的AreEqual方法相比,它不關(guān)心順序 |
| CollectionAssert.Contains | 斷言集合是否包含某一元素 |
| CollectionAssert.IsEmpty | 斷言某一集合是空集合,即元素個(gè)數(shù)為0 |
| CollectionAssert.IsSubsetOf | 判斷一個(gè)集合是否為另一個(gè)集合的子集,這兩個(gè)集合不必是同一類集合(可以一個(gè)是array,一個(gè)是list),只要一個(gè)集合的元素完全包含在另一個(gè)集合中,即認(rèn)為它是另一個(gè)集合的子集 |
?
?
6.單元測(cè)試中驗(yàn)證預(yù)期的異常
若是程序中在某種特定的條件下有異常拋出,為了進(jìn)行單元測(cè)試,我們?cè)O(shè)計(jì)指定的測(cè)試案例,期望在該測(cè)試案例程序拋出異常,并檢驗(yàn)其是否拋出異常。
簡(jiǎn)單示例:
/// <summary> /// 計(jì)算從from到to的所有整數(shù)的和 /// </summary> public int Sum(int from, int to) {if (from > to){throw new ArgumentException("參數(shù)from必須小于to");}int sum = 0;for (int i = from; i <= to; i++){sum += i;}return sum; }在程序中,若是參數(shù)from?>to則拋出異常new ArgumentException("參數(shù)from必須小于to");
為了檢驗(yàn)該程序在該條件下是否真的會(huì)拋出異常,可以創(chuàng)造測(cè)試案例from=100 > to=50
期望Sum()函數(shù)代碼中執(zhí)行:throw new ArgumentException("參數(shù)from必須小于to");,所以我們要測(cè)試期望拋出的異常ArgumentException。
使用標(biāo)簽[ExpectedException(typeof(“拋出的異常對(duì)象”))]
單元測(cè)試代碼:
//異常測(cè)試,添加ExpectedException [TestMethod] [ExpectedException(type(ArgumentException))] public void SumTest_ArgumentException_TrowException() {Calculator bjCalcultor = new Calculator();int from=100,to=50;calc.Sum(from, to); }因?yàn)槌绦驋伋隽宋覀兤谕漠惓?#xff0c;所以該測(cè)試通過(guò)。如若程序沒(méi)有拋出該異常則測(cè)試失敗。
?
?
7.單元測(cè)試中針對(duì)狀態(tài)的間接測(cè)試
-
基于狀態(tài)的測(cè)試(也稱狀態(tài)驗(yàn)證),是指在方法執(zhí)行之后,通過(guò)檢查被測(cè)系統(tǒng)及其協(xié)作者(依賴項(xiàng))的狀態(tài)來(lái)檢測(cè)該方法是否正確工作。
-
簡(jiǎn)單示例:
下面的方法isLastFilenameValid(string filename)在運(yùn)行后會(huì)改變類中屬性wasLastFileNameValid的值
-
單元測(cè)試函數(shù):
該測(cè)試是測(cè)試isLastFilenameValid(),
因?yàn)樵摵瘮?shù)是把結(jié)果賦值給類中屬性wasLastFileNameValid,
所以此處驗(yàn)證的是Calculator類中屬性wasLastFileNameValid是否符合我們的期望,
而不是簡(jiǎn)單的驗(yàn)證isLastFilenameValid()的返回值是否符合我們的期望。
?
?
8.單元測(cè)試在MVC模式中的實(shí)現(xiàn)
參考
-
因?yàn)镸VC模式中的Controller類中的Action的返回值是和普通類的方法不一樣的,
Action的返回值是ActionResult類型的,其子類又有許多,
具體怎么實(shí)現(xiàn)對(duì)MVC模式的單元測(cè)試呢?請(qǐng)看一個(gè)簡(jiǎn)單的示例:
代碼背景:在一個(gè)MVC項(xiàng)目中的HomeController控制器中有一個(gè)Action是Index()
首先先定義一個(gè)Person類其中有Id和Name兩個(gè)屬性
Action如下:
public class HomeController : Controller{// GET: Homepublic ActionResult Index(){return View("Index",new Person { Id = 001, Name = "shanzm" });}}對(duì)上面的HomeController中的Index()進(jìn)行一個(gè)簡(jiǎn)單的單元測(cè)試
新建一個(gè)單元測(cè)試項(xiàng)目(或者在創(chuàng)建MVC項(xiàng)目的時(shí)候選中單元測(cè)試的按鈕,則自動(dòng)生成一個(gè)單元測(cè)試項(xiàng)目)
注意一定要先安裝MVC的程序集,NuGet:Install-Package Microsoft.AspNet.Mvc -Version 5.2.3
[TestMethod()]public void Index_Index_ReturnTrue(){//Arrage:準(zhǔn)備測(cè)試對(duì)象HomeController hcont = new HomeController();//Act:執(zhí)行測(cè)試函數(shù)ViewResult result =(ViewResult)hcont.Index();var viewName = result.ViewName;Person model = (Person)result.Model;//Assert:斷言符合期望Assert.IsTrue(viewName == "Index" && model.Id == 001 && model.Name == "shanzm"&& );}
說(shuō)明:
如果View()函數(shù)沒(méi)指定視圖,而是使用默認(rèn)的視圖,則視圖名為空,所以如果名稱不寫的時(shí)候我們可以斷言ViewName是空。
注意在Action中的ViewBag傳遞的數(shù)據(jù)在單元測(cè)試中需要通過(guò)ViewData方式獲取(因?yàn)閂iewBag是對(duì)ViewData的動(dòng)態(tài)封裝,在同一個(gè)Action中二者數(shù)據(jù)相通,此乃ASP.NET MVC的基礎(chǔ),不詳述)
其實(shí)呀,MVC模式作為UI層,有許多東西其實(shí)是很難(但不是不可以)模擬對(duì)象去進(jìn)行單元測(cè)試的,一般其實(shí)不推薦做過(guò)多的單元測(cè)試。(注意不是不做,是不做過(guò)多過(guò)復(fù)雜的單元測(cè)試)
?
?
8.單元測(cè)試相關(guān)參考
書籍:.NET 單元測(cè)試的藝術(shù)
書籍:單元測(cè)試之道C#版
微軟:dotnet文檔
博客園:對(duì)比MS Test與NUnit Test框架
博客園:.net持續(xù)集成測(cè)試篇之Nunit文件斷言、字符串?dāng)嘌约凹蠑嘌?/p>
博客園:.netcore持續(xù)集成測(cè)試篇之MVC層單元測(cè)試
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的关于C#程序的单元测试的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 外卖大战又要来了!京东零售CEO辛利军:
- 下一篇: C#.NET Thread多线程并发编程