在計(jì)算機(jī)編程中卒落,單元測(cè)試(英語(yǔ):Unit Testing)又稱(chēng)為模塊測(cè)試, 是針對(duì)程序模塊的最小單位來(lái)進(jìn)行正確性檢驗(yàn)的測(cè)試工作羡铲。程序單元是應(yīng)用的最小可測(cè)試部件。在過(guò)程化編程中儡毕,一個(gè)單元就是單個(gè)程序也切、函數(shù)、過(guò)程等腰湾;對(duì)于面向?qū)ο缶幊汤资眩钚卧褪欠椒ǎɑ?lèi)(超類(lèi))费坊、抽象類(lèi)倒槐、或者派生類(lèi)(子類(lèi))中的方法。? --? 維基百科
有了單元測(cè)試以后附井,我們就沒(méi)必要為了測(cè)試某個(gè)小模塊去編譯我們的程序讨越,然后去等待模擬器啟動(dòng)然后到你需要驗(yàn)證的模塊去两残。這樣做也是可以的啦!可是把跨,你的項(xiàng)目很大磕昼,編譯等老半天,你的電腦沒(méi)那么快节猿,那就夠你等的了。我還記得我工作的第一個(gè)公司漫雕,給我用的是一個(gè)性能堪憂的Mac mini滨嘱,每次修改代碼后Command + R后都得等上一分多,簡(jiǎn)直不能忍受敖洹太雨!是的,我沒(méi)能忍受魁蒜,沒(méi)過(guò)多久我就離職了囊扳。扯淡這么半天,就是為了說(shuō)明單元測(cè)試能節(jié)約我們的時(shí)間兜看,提高開(kāi)發(fā)效率锥咸,對(duì)于項(xiàng)目越大的效果越明顯。
XCTest
XCode4.x時(shí)代Xcode集成的是OCUnit细移,到了XCode5.x時(shí)代就升級(jí)為了XCTest,并且到了XCode7時(shí)代還有了進(jìn)行UI測(cè)試的能力搏予。除了官方自帶的,還有一些比較出名的第三方的測(cè)試框架弧轧,如:GHUnit,KiWi雪侥,OCMock,Specta等,當(dāng)然本文不討論這些第三方框架。怎么知道我們的項(xiàng)目有沒(méi)有加上單元測(cè)試精绎,用Xcode打開(kāi)你的項(xiàng)目速缨,看文件導(dǎo)航欄有沒(méi)有類(lèi)似下圖的兩個(gè)文件夾(TestDemo是工程名)。
其實(shí)在我們新建工程的時(shí)候就可以為我們的工程選擇是否帶上單元測(cè)試代乃,如下圖:
如果你的項(xiàng)目沒(méi)有上面說(shuō)的兩個(gè)文件旬牲,你可以通過(guò)新建一個(gè)Target的方式添加,如下圖:
在test下選擇你項(xiàng)目沒(méi)有的便可:
在這2個(gè)文件夾目錄下分別都有2個(gè)文件搁吓,一個(gè).m文件和一個(gè)plist文件引谜。并且.m文件有4個(gè)方法,如下圖:
項(xiàng)目名+Test的.m文件里面默認(rèn)有4個(gè)方法擎浴,這個(gè)文件里面主要做一些邏輯的測(cè)試员咽。項(xiàng)目名+UITest的.m文件里默認(rèn)有3個(gè)方法。這個(gè)文件里面主要做一些UI的測(cè)試贮预。說(shuō)了這么半天贝室,該如何寫(xiě)單元測(cè)試呢契讲?在講解如何寫(xiě)測(cè)試方法前,先說(shuō)說(shuō)默認(rèn)的方法是干什么的吧滑频!
//TestDemoTest.m- (void)setUp {? ? [supersetUp];//每個(gè)test方法執(zhí)行前調(diào)用捡偏,在這個(gè)測(cè)試用例里進(jìn)行一些通用的初始化工作// Put setup code here. This method is called before the invocation of each test method in the class.}- (void)tearDown {// Put teardown code here. This method is called after the invocation of each test method in the class.[supertearDown];//每個(gè)test方法執(zhí)行后調(diào)用}- (void)testExample {//測(cè)試方法樣例// This is an example of a functional test case.// Use XCTAssert and related functions to verify your tests produce the correct results.}- (void)testPerformanceExample {//這個(gè)方法主要是做性能測(cè)試的,所謂性能測(cè)試峡迷,主要就是評(píng)估一段代碼的運(yùn)行時(shí)間银伟。該方法就是性能測(cè)試方法的樣例。// This is an example of a performance test case.[selfmeasureBlock:^{// Put the code you want to measure the time of here.}];}
測(cè)試用例方法非常簡(jiǎn)單绘搞,從testExample這個(gè)方法我們大概知道怎么寫(xiě)了吧彤避!方法名只需要以test開(kāi)頭,是的夯辖,就是這么簡(jiǎn)單×鹪ぃ現(xiàn)在我們模擬登錄這個(gè)功能來(lái)寫(xiě)一個(gè)登錄模塊的測(cè)試用例吧,Demo代碼在GitHub,在User這個(gè)模型類(lèi)里面一個(gè)方法叫isChinese的蒿褂,是用來(lái)判斷字符串里面是否有中文的圆米。
#import@interfaceUser:NSObject@property(nonatomic,copy)NSString*userName;@property(nonatomic,copy)NSString*passWord;/**
*? 判斷字符串中是否有中文
*/- (BOOL)isChinese:(NSString*)string;@end
現(xiàn)在我們通過(guò)Xcode的File->New->File->Source選擇Unit Test Case Class來(lái)新建一個(gè)UserTests,注意要繼承XCTestCase類(lèi)。
接下來(lái)我們?yōu)閁ser類(lèi)寫(xiě)一個(gè)測(cè)試isChinese方法的測(cè)試方法啄栓,叫做testIsChinese,測(cè)試用例具體如下:
UserTests.m
這樣娄帖,你只要點(diǎn)擊測(cè)試方法旁邊的那個(gè)菱形的按鈕就可以運(yùn)行該測(cè)試方法啦!通過(guò)測(cè)試會(huì)變成綠色的對(duì)勾昙楚,失敗會(huì)變成紅色的叉叉块茁。到這里測(cè)試用例你就會(huì)寫(xiě)了。也許你會(huì)在意那些斷言桂肌,這樣的斷言有18個(gè)数焊,如下:
XCTFail(format…)//生成一個(gè)失敗的測(cè)試;XCTAssertNil(a1, format...)//為空判斷崎场,a1為空時(shí)通過(guò)佩耳,反之不通過(guò);XCTAssertNotNil(a1, format…)//不為空判斷谭跨,a1不為空時(shí)通過(guò)干厚,反之不通過(guò);XCTAssert(expression, format...)//當(dāng)expression求值為T(mén)RUE時(shí)通過(guò)螃宙;XCTAssertTrue(expression, format...)//當(dāng)expression求值為T(mén)RUE時(shí)通過(guò)蛮瞄;XCTAssertFalse(expression, format...)//當(dāng)expression求值為False時(shí)通過(guò);XCTAssertEqualObjects(a1, a2, format...)//判斷相等谆扎,[a1 isEqual:a2]值為T(mén)RUE時(shí)通過(guò)挂捅,其中一個(gè)不為空時(shí),不通過(guò)堂湖;XCTAssertNotEqualObjects(a1, a2, format...)//判斷不等闲先,[a1 isEqual:a2]值為False時(shí)通過(guò)状土;XCTAssertEqual(a1, a2, format...)//判斷相等(當(dāng)a1和a2是 C語(yǔ)言標(biāo)量、結(jié)構(gòu)體或聯(lián)合體時(shí)使用,實(shí)際測(cè)試發(fā)現(xiàn)NSString也可以)伺糠;XCTAssertNotEqual(a1, a2, format...)//判斷不等(當(dāng)a1和a2是 C語(yǔ)言標(biāo)量蒙谓、結(jié)構(gòu)體或聯(lián)合體時(shí)使用);XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)//判斷相等训桶,(double或float類(lèi)型)提供一個(gè)誤差范圍累驮,當(dāng)在誤差范圍(+/-accuracy)以內(nèi)相等時(shí)通過(guò)測(cè)試;XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...)//判斷不等舵揭,(double或float類(lèi)型)提供一個(gè)誤差范圍谤专,當(dāng)在誤差范圍以內(nèi)不等時(shí)通過(guò)測(cè)試;XCTAssertThrows(expression, format...)//異常測(cè)試琉朽,當(dāng)expression發(fā)生異常時(shí)通過(guò);反之不通過(guò)稚铣;XCTAssertThrowsSpecific(expression, specificException, format...)//異常測(cè)試箱叁,當(dāng)expression發(fā)生specificException異常時(shí)通過(guò);反之發(fā)生其他異程枰剑或不發(fā)生異常均不通過(guò)耕漱;XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)//異常測(cè)試,當(dāng)expression發(fā)生具體異常抬伺、具體異常名稱(chēng)的異常時(shí)通過(guò)測(cè)試螟够,反之不通過(guò);XCTAssertNoThrow(expression, format…)//異常測(cè)試峡钓,當(dāng)expression沒(méi)有發(fā)生異常時(shí)通過(guò)測(cè)試妓笙;XCTAssertNoThrowSpecific(expression, specificException, format...)//異常測(cè)試,當(dāng)expression沒(méi)有發(fā)生具體異常能岩、具體異常名稱(chēng)的異常時(shí)通過(guò)測(cè)試寞宫,反之不通過(guò);XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)//異常測(cè)試拉鹃,當(dāng)expression沒(méi)有發(fā)生具體異常辈赋、具體異常名稱(chēng)的異常時(shí)通過(guò)測(cè)試,反之不通過(guò)
UI測(cè)試
用代碼寫(xiě)UI測(cè)試比較麻煩膏燕,但是蘋(píng)果在Xcode中為我們提供了錄制的功能钥屈。錄制是怎么一回事呢?當(dāng)你打開(kāi)時(shí)這個(gè)功能時(shí)坝辫,測(cè)試代碼會(huì)隨著你在設(shè)備或模擬器上操作自動(dòng)創(chuàng)建篷就。這么一來(lái)就省事多了。現(xiàn)在近忙,我們?cè)赥estDemoUITests.m文件中寫(xiě)一個(gè)方法testLogin作為測(cè)試登錄流程操作的UI測(cè)試方法腻脏。然后把光標(biāo)放在方法體內(nèi)鸦泳,然后點(diǎn)擊紅色的那個(gè)錄制按鈕,如下:
當(dāng)你點(diǎn)擊了錄制后永品,程序就會(huì)自動(dòng)啟動(dòng)做鹰,這時(shí)候你在程序的所有操作都會(huì)生成想用的代碼在你所選擇的方法體內(nèi)。我錄制了一個(gè)GIF鼎姐,你可以看一下钾麸,非常的好用:
接下來(lái)我們看看里面的代碼:
//XCUIApplication 這是應(yīng)用的代理,他能夠把你的應(yīng)用啟動(dòng)起來(lái)炕桨,并且每次都在一個(gè)新進(jìn)程中饭尝。XCUIApplication*app = [[XCUIApplicationalloc] init];//XCUIElement 這是 UI 元素的代理。元素都有類(lèi)型和唯一標(biāo)識(shí)献宫≡科剑可以結(jié)合使用來(lái)找到元素在哪里,如當(dāng)前界面上的一個(gè)輸入框XCUIElement*usernameTextField = app.textFields[@"username:"];[usernameTextField tap];[usernameTextField typeText:@"xiaofei"];XCUIElement*passwordTextField = app.textFields[@"password:"];[passwordTextField tap];[passwordTextField tap];[passwordTextField typeText:@"12345"];[[[[[[[app childrenMatchingType:XCUIElementTypeWindow] elementBoundByIndex:0] childrenMatchingType:XCUIElementTypeOther].elementchildrenMatchingType:XCUIElementTypeOther].elementchildrenMatchingType:XCUIElementTypeOther].elementchildrenMatchingType:XCUIElementTypeOther].elementtap];[app.buttons[@"login"] tap];
有了這些代碼姊途,我們就可以對(duì)它進(jìn)行一些處理了涉瘾,比如:
//XCUIApplication 這是應(yīng)用的代理,他能夠把你的應(yīng)用啟動(dòng)起來(lái)捷兰,并且每次都在一個(gè)新進(jìn)程中立叛。XCUIApplication*app = [[XCUIApplicationalloc] init];//XCUIElement 這是 UI 元素的代理。元素都有類(lèi)型和唯一標(biāo)識(shí)贡茅∶厣撸可以結(jié)合使用來(lái)找到元素在哪里,如當(dāng)前界面上的一個(gè)輸入框XCUIElement*usernameTextField = app.textFields[@"username:"];[usernameTextField tap];[usernameTextField typeText:@"xiaofei"];XCUIElement*passwordTextField = app.textFields[@"password:"];[passwordTextField tap];[passwordTextField tap];[passwordTextField typeText:@"12345"];[[[[[[[app childrenMatchingType:XCUIElementTypeWindow] elementBoundByIndex:0] childrenMatchingType:XCUIElementTypeOther].elementchildrenMatchingType:XCUIElementTypeOther].elementchildrenMatchingType:XCUIElementTypeOther].elementchildrenMatchingType:XCUIElementTypeOther].elementtap];[app.buttons[@"login"] tap];//登錄成功后的控制器的title為loginSuccess顶考,只需判斷控制器的title時(shí)候一樣便可判斷登錄是否成功XCTAssertEqualObjects(app.navigationBars.element.identifier,@"loginSuccess");
如果你想一次跑完所有的測(cè)試方法赁还,快捷鍵cmd+u即可。跑起來(lái)后的面板主要如下圖所示:
總結(jié)
這只是蘋(píng)果官方集成在Xcode中的簡(jiǎn)單框架驹沿,優(yōu)點(diǎn)就是簡(jiǎn)單秽浇,缺點(diǎn)也是簡(jiǎn)單。當(dāng)然它的用法也絕非如此甚负,有很多還待開(kāi)發(fā)柬焕。蘋(píng)果官方也有一個(gè)Demo,地址點(diǎn)擊這里
文/張飛_(簡(jiǎn)書(shū)作者)
原文鏈接:http://www.reibang.com/p/07cfc17916e8
著作權(quán)歸作者所有梭域,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)斑举,并標(biāo)注“簡(jiǎn)書(shū)作者”。