- layout: post
- title: "Xcode 7智能測(cè)試化工具XCTest學(xué)習(xí)"
- subtitle: "Xcode 7智能測(cè)試化工具XCTest學(xué)習(xí)"
- date: 2015-11-6
- author: "Scenery"
tags:- iOS
- iOS UI測(cè)試
- 單元測(cè)試
- XCTestCase
- 電子商務(wù)
Xcode 7智能測(cè)試化工具XCTest學(xué)習(xí)
1. 單元測(cè)試簡(jiǎn)介
1.1、Unit Testing: 單元測(cè)試
測(cè)試這個(gè)詞很容易理解,那么什么是單元(Unit)呢?一個(gè)單元指的就是應(yīng)用程序中可以測(cè)試的最小單元。一組源代碼可以測(cè)試,一般要求有明確的輸入與輸出。因此一般來(lái)說(shuō)源代碼中明確的包含輸入輸出的每一個(gè)方法被認(rèn)為一個(gè)測(cè)試的單元(一個(gè)case)。注意,這里的輸出并不局限于方法的返回值對(duì)輸入?yún)?shù)的改變,也包括方法在執(zhí)行過(guò)程中改變的任何數(shù)據(jù)。
單元測(cè)試在程序里面可以理解一個(gè)模塊一個(gè)方法,在每個(gè)可能存在的模塊都進(jìn)行測(cè)試,確保每個(gè)模塊都沒(méi)有問(wèn)題,從而提高整體程序的質(zhì)量。
1.2、單元測(cè)試的目的
單元測(cè)試的目的是將程序中所有的源代碼,隔離成最小的可測(cè)試單元,以確保每個(gè)單元的正確性,如果每個(gè)單元都能保證正確,就能保證應(yīng)用程序整體相當(dāng)程度的正確性。另一方面測(cè)試腳本本身就是被測(cè)試代碼的實(shí)際使用代碼,這對(duì)于開(kāi)發(fā)者理解被測(cè)試單元的使用是用幫助的。
測(cè)試是分黑盒測(cè)試和白盒測(cè)試(概念此處不在解釋),單元測(cè)試其實(shí)就是一種白盒測(cè)試,開(kāi)發(fā)者對(duì)現(xiàn)有已經(jīng)實(shí)現(xiàn)的模塊自己寫(xiě)對(duì)應(yīng)測(cè)試腳本進(jìn)行測(cè)試,這中間還包含測(cè)試用例的設(shè)計(jì)。相對(duì)來(lái)說(shuō)還是由開(kāi)發(fā)者自己來(lái)完成白盒測(cè)試,然后在交由測(cè)試團(tuán)隊(duì)進(jìn)行黑盒測(cè)試,這樣也更加有助于提升測(cè)試流程的完整性,最終提高產(chǎn)品的質(zhì)量。
單元測(cè)試的內(nèi)容:
- 單元測(cè)試的測(cè)試目的
- 模塊接口測(cè)試
- 局部數(shù)據(jù)結(jié)構(gòu)測(cè)試
- 路徑測(cè)試
- 錯(cuò)誤處理測(cè)試
- 邊界測(cè)試
在現(xiàn)有的開(kāi)發(fā)工作中,我們一般都會(huì)忽略掉單元測(cè)試的重要性,功能開(kāi)發(fā)完成以后開(kāi)發(fā)者拿到現(xiàn)有的測(cè)試用例,直接針對(duì)每條用例進(jìn)行手工的測(cè)試,測(cè)試通過(guò)就進(jìn)行提測(cè),之后測(cè)試人員還是重復(fù)手工測(cè)試的流程、數(shù)據(jù)的mock、專(zhuān)項(xiàng)測(cè)試等,這樣以來(lái)白盒測(cè)試的流程有時(shí)間份量會(huì)變的很低,開(kāi)發(fā)人員不知道自己模塊代碼的覆蓋路問(wèn)題,更多的時(shí)間可能某些代碼一直到到上線都從來(lái)沒(méi)有跑過(guò),以至于到了真實(shí)環(huán)境下會(huì)產(chǎn)生一些意想不到的問(wèn)題,這樣以來(lái)風(fēng)險(xiǎn)極高,整體來(lái)說(shuō)單元測(cè)試還是至關(guān)重要的。
下面介紹一下Xcode7 中現(xiàn)有的一些測(cè)試工具:
2. Xcode7中的UnitTest
2.1、XCTest介紹
本文主要基本Xcode7來(lái)講解,至于xcode 新功能的歷史各位自己去趴去,此處只講解如何使用,廢話少說(shuō),下面直接入正題。
最新的Xcode7中是包含了UITest UnitTest工具的,這個(gè)可以在你創(chuàng)建工程的時(shí)間勾選對(duì)應(yīng)的選項(xiàng),也可以直接通過(guò)addTarget的形式來(lái)完成,
2.1、XCTestCase簡(jiǎn)介
如果項(xiàng)目創(chuàng)建的時(shí)間勾選了UnitTest(從名字上看就是Apple提供的官方的一個(gè)單元測(cè)試工具) ,我們可以看到工程里面是多了一個(gè)目錄,默認(rèn)多了一個(gè)類(lèi), 如圖:
XCTest時(shí)Apple官方提供一個(gè)測(cè)試工具,一個(gè)內(nèi)置的測(cè)試框架,從工程里面可以看到,一個(gè)“應(yīng)用名稱(chēng)”的group,我們直接可以使用commond+R 來(lái)遠(yuǎn)行,一個(gè)測(cè)試的target我們可以使用commond+U來(lái)遠(yuǎn)行測(cè)試target,在測(cè)試target的目錄下會(huì)有一個(gè)默認(rèn)的“應(yīng)用名稱(chēng)”+Test的類(lèi),這個(gè)類(lèi)只有.m沒(méi)有.h,繼承于XCTestCase,使用commond+U即可運(yùn)行。
默認(rèn)測(cè)試類(lèi)里面有以下方法:
//方法在XCTestCase的測(cè)試方法調(diào)用之前調(diào)用,可以在測(cè)試之前創(chuàng)建在test case方法中需要用到的一些對(duì)象等
- (void)setUp ;
//當(dāng)測(cè)試全部結(jié)束之后調(diào)用tearDown方法,法則在全部的test case執(zhí)行結(jié)束之后清理測(cè)試現(xiàn)場(chǎng),釋放資源刪除不用的對(duì)象等
- (void)tearDown ;
//測(cè)試代碼執(zhí)行性能
- (void)testPerformanceExample
2.2、XCTestCase使用
XCTestCase的初始化不是用戶(hù)控制的,開(kāi)發(fā)者無(wú)需手動(dòng)針對(duì)XCTestCase的subclass進(jìn)行 alloc 、init或者調(diào)用靜態(tài)方法初始化的操作,針對(duì)一個(gè)功能塊的單元測(cè)試(針對(duì)某個(gè)class),只需要單獨(dú)給為這個(gè)類(lèi)創(chuàng)建一個(gè)繼承于XCTestCase,在這個(gè)文件內(nèi)實(shí)現(xiàn)上述基本函數(shù)以后(一半系統(tǒng)會(huì)默認(rèn)創(chuàng)建這三個(gè)函數(shù)),其實(shí)的邏輯只需要開(kāi)發(fā)者自行定義以“test"開(kāi)頭的函數(shù),然后在內(nèi)部實(shí)現(xiàn)自己針對(duì)某個(gè)函數(shù)、返回?cái)?shù)值結(jié)果、操作等的測(cè)試腳本即可,commond+U執(zhí)行的時(shí)間,單元測(cè)試會(huì)自動(dòng)執(zhí)行這些test打頭的函數(shù),當(dāng)函數(shù)頭上出現(xiàn)藍(lán)色的標(biāo)記則表明測(cè)試通過(guò),否則直接報(bào)紅色錯(cuò)誤。
XCTest測(cè)試范疇:
- 基本邏輯測(cè)試處理測(cè)試
- 異步加載數(shù)據(jù)測(cè)試
- 數(shù)據(jù)mock測(cè)試
XCTest常用基本測(cè)試工具
XCTest常用的一些判斷工具都是以XCT開(kāi)頭的,如:
//斷言,最基本的測(cè)試,如果expression為true則通過(guò),否則打印后面格式化字符串
XCTAssert(expression, format...)
//Bool測(cè)試:
XCTAssertTrue(expression, format...)
XCTAssertFalse(expression, format...)
//相等測(cè)試
XCTAssertEqual(expression1, expression2, format...)
XCTAssertNotEqual(expression1, expression2, format...)
//double float 對(duì)比數(shù)據(jù)測(cè)試使用
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, format...)
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, format...)
//Nil測(cè)試,XCTAssert[Not]Nil斷言判斷給定的表達(dá)式值是否為nil
XCTAssertNil(expression, format...)
XCTAssertNotNil(expression, format...)
//失敗斷言
XCTFail(format...)
XCTest異步測(cè)試
Xcode單元測(cè)試中加入的最令人興奮的功能也許就是類(lèi)XCTestExpression類(lèi)帶入的異步測(cè)試了。現(xiàn)在測(cè)試可以等待指定長(zhǎng)度的時(shí)間,一直到某些條件符合的時(shí)候在開(kāi)始測(cè)試。而不用再寫(xiě)很多的GCD代碼控制。
要使用異步測(cè)試,首先用方法expectationWithDescription創(chuàng)建一個(gè)expection
let expectation = expectationWithDescription("...")
之后,在方法的最后添加方法waitForExpectationsWithTimeout,指定等待超時(shí)的時(shí)間和指定時(shí)間內(nèi)條件無(wú)法滿(mǎn)足時(shí)執(zhí)行的closure。
waitForExpectationsWithTimeout(10) { (error) in
// ...
}
剩下的就是在異步測(cè)試剩下的回調(diào)函數(shù)中告訴expectation條件已經(jīng)滿(mǎn)足。
expectation.fulfill()
如果在測(cè)試中有多個(gè)expectation,則每個(gè)expectation都必須fulfill,否則測(cè)試不通過(guò)。
- (void)testFetchRequestWithMockedManagedObjectContext
{
MockNSManagedObjectContext *mockContext = [[MockNSManagedObjectContext alloc] initWithConcurrencyType:0x00];
let mockContext = MockNSManagedObjectContext()
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"User"];
let fetchRequest = NSFetchRequest(entityName: "User")
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"email ENDSWITH[cd] apple.com"];
fetchRequest.predicate = NSPredicate(format: "email ENDSWITH[cd] %@", "apple.com")
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.resultType = NSFetchRequestResultType.DictionaryResultType
var error: NSError?
NSError *error = nil;
NSArray *results = [mockContext executeFetchRequest:fetchRequest error:&error];
let results = mockContext.executeFetchRequest(fetchRequest, error: &error)
XCTAssertNil(error, @"error應(yīng)該為nil");
XCTAssertEqual(results.count, 2, @"fetch request應(yīng)該只返回一個(gè)結(jié)構(gòu)");
NSDictionary * result = results[0];
XCTAssertEqual(result[@"name"], @"張三", @"name應(yīng)該是張三");
NSLog(@"email : %@",result[@"email"]);
XCTAssertEqual(result[@"email"], @"zhangsaan@apple.com", @"email應(yīng)該是zhangsan@apple.com");
}
XCTest Mock
數(shù)據(jù)mock
2.3、Xcode7 Code Coverage介紹
談的單元測(cè)試此處不免要聊起一個(gè)新的概念,代碼覆蓋率,最早最一次聽(tīng)到這個(gè)詞的時(shí)間覺(jué)得很可笑(難道這個(gè)也需要統(tǒng)計(jì)),故名思義 代碼覆蓋率 = 實(shí)際執(zhí)行的代碼行數(shù) / 整個(gè)工程總代碼行數(shù),直白來(lái)講就是這樣一個(gè)數(shù)值,上述談過(guò),單元測(cè)試的目的除了講程序分成各個(gè)最小的單元獨(dú)立去測(cè)試確保正確以外,還有一個(gè)就是代碼覆蓋率問(wèn)題,如果說(shuō)發(fā)到線上的產(chǎn)品有相當(dāng)一部分代碼從來(lái)都沒(méi)有執(zhí)行過(guò),這個(gè)問(wèn)題是相當(dāng)危險(xiǎn)的(問(wèn)題大家可以各自猜測(cè),相信這個(gè)問(wèn)題不是很陌生)。
Code Coverage工具使用
下面說(shuō)下Xcode中代碼覆蓋率的問(wèn)題,Xcode7以前代碼覆蓋率統(tǒng)計(jì)比較麻煩,Xcode7以后Apple推出了更為人性化的工具,既然學(xué)習(xí)就學(xué)習(xí)最為流行的,過(guò)去的麻煩就當(dāng)隨之過(guò)去吧,何必留在痛苦回憶里(有興趣的可以參考 Apple DOC Xcode代碼覆蓋率測(cè)試工具)
注: 傳統(tǒng)統(tǒng)計(jì)覆蓋率的方法會(huì)做一部分Xcode配置,最終打出一個(gè)叫“插莊包”的包,這個(gè)包僅為做統(tǒng)計(jì)使用,如果要上生產(chǎn)環(huán)境,切忌將配置一定要關(guān)掉在從新打包上傳,否則后患無(wú)窮...
Xcode7 提供了一個(gè)內(nèi)置的Code Coverage工具組件,廢話不說(shuō),下面看使用方法:
1、首先需要在product->scheme->Edit Scheme里面將Code Coverage模式打開(kāi),選中為debug模式,如圖:
2、打開(kāi)Code Coverage模式之后,打開(kāi)某個(gè)測(cè)試類(lèi),commond+U 運(yùn)行,如果測(cè)試通過(guò),測(cè)試腳本的函數(shù)頭上會(huì)出現(xiàn)一個(gè)綠色的標(biāo)志(相反如何哪一個(gè)方法測(cè)試沒(méi)有通過(guò),則會(huì)提示一個(gè)紅色錯(cuò)誤),如下:
3、打開(kāi)Xcode左邊窗口的Report Navigator,找到 Project Log,選擇最近一次的log選項(xiàng),最近一次是剛才的一個(gè)Test Log,選擇中這個(gè)Log實(shí)例,可以看到一下界面,
如圖:
然后在tab中選中 Coverage,此時(shí)你可以看到大致的代碼執(zhí)行覆蓋情況,如果指示條是滿(mǎn)的則代表該類(lèi)代碼全部跑過(guò)一遍。
4、雙擊你想要查看的類(lèi),此處選擇查看UATrackDao,打開(kāi)后既可以看到剛剛的測(cè)試中有哪些代碼是執(zhí)行過(guò)的,那些代碼時(shí)未執(zhí)行的,橘黃色的代表還未執(zhí)行的,執(zhí)行過(guò)的每一行后面會(huì)有一個(gè)序號(hào)代表這行代碼在剛才的測(cè)試過(guò)程中執(zhí)行的次數(shù)。如果有未執(zhí)行的,可根據(jù)具體的情況調(diào)整對(duì)應(yīng)的測(cè)試腳本,繼續(xù)測(cè)試,最終確保每一行代碼都能正確執(zhí)行,如圖:
3. Xcode7中的UITest
UnitTest簡(jiǎn)介
任何軟件開(kāi)發(fā)中,自動(dòng)化UI測(cè)試都是很重要的(UI自動(dòng)化測(cè)試的好處此處就不再多說(shuō)了),iOS平臺(tái)在以往是通過(guò)UIAutomation來(lái)完成自動(dòng)化UI測(cè)試的,它的測(cè)試用例是javascript寫(xiě)的(Instruments中提供了該功能),這個(gè)過(guò)程深?yuàn)W繁瑣,需要自行編寫(xiě)對(duì)應(yīng)的測(cè)試腳本,速度慢,學(xué)習(xí)成本高(關(guān)于Automation自動(dòng)化測(cè)試概念大家可以查看相關(guān)的資料,Automation自動(dòng)化測(cè)試在各大平臺(tái)都有應(yīng)用,在大型的軟件開(kāi)發(fā)測(cè)試過(guò)程的確的確可以節(jié)省大量的手工測(cè)試人員,大大提高軟件測(cè)試的成本與效率,在最新Xcode7本文推薦使用Apple提供的最新的工具UITest)。
Apple在Xcode 6中又新增了UnitTest之外,到了Xcode 7 Apple從新提供了一個(gè)新的框架UITest,這個(gè)主要是用來(lái)測(cè)試UI的,UnitTest涌來(lái)測(cè)試功能邏輯代碼,UITest專(zhuān)門(mén)用來(lái)測(cè)試UI。
Xcode 7已經(jīng)集成了UITest,UITest允許你找到UI元素并與之交戶(hù),還能檢查UI的屬性和狀態(tài),并且UITest也已經(jīng)集成在Xcode 的測(cè)試報(bào)告了,可以和單元測(cè)試一起執(zhí)行,和UnitTest一樣我們可以在檢查UI的時(shí)間執(zhí)行斷言。
創(chuàng)建UITest target,同樣會(huì)生成一個(gè)“項(xiàng)目名稱(chēng)”+UITest的group,UITest target可以在創(chuàng)建工程的時(shí)間勾選,也可以在工程中手動(dòng)添加,在 “項(xiàng)目名稱(chēng)”+UITest 分組下,我們可以看到系統(tǒng)會(huì)幫我們默認(rèn)生成一個(gè)UI測(cè)試類(lèi),這個(gè)類(lèi)也同樣是繼承于XCTestCase的。由此可見(jiàn),在iOS中無(wú)論是單元測(cè)試還是UI測(cè)試,都是基于XCTestCase的。
UnitTest UI測(cè)試
創(chuàng)建模態(tài)視圖,我們選擇從第一個(gè)VC通過(guò)點(diǎn)擊按鈕的形式push到第二個(gè)VC
創(chuàng)建UITest target,我們對(duì)上述UI進(jìn)行測(cè)試 如圖選項(xiàng):
打開(kāi)UATrackDemoUiTest.m,創(chuàng)建 - (void)testUI,同時(shí)將光標(biāo)留在函數(shù)內(nèi)
點(diǎn)擊下面的紅色按鈕,開(kāi)始recorder操作,程序運(yùn)行起來(lái)后,點(diǎn)擊界面上的按鈕,程序會(huì)push到一個(gè)新的頁(yè)面,這個(gè)時(shí)間會(huì)看到到剛才的鼠標(biāo)光標(biāo)處自動(dòng)生成了一部分代碼,重復(fù)操作,每次都會(huì)生成新的代碼,如圖:
從新點(diǎn)擊小紅點(diǎn)按鈕,此時(shí)結(jié)束recorder操作,commond+U 運(yùn)行測(cè)試,此時(shí)剛才的一連串動(dòng)作會(huì)一步一步連續(xù)執(zhí)行下來(lái):
此處聲明: 第一次點(diǎn)擊紅色的recorder按鈕,然后手動(dòng)操作會(huì)自動(dòng)生成測(cè)試腳本,第二次commond+U是進(jìn)行測(cè)試UI
UnitTest工具拓展
XCTest一共提供了三種UI測(cè)試對(duì)象
- XCUIApplication 當(dāng)前測(cè)試應(yīng)用target
- XCUIElementQuery 定位查詢(xún)當(dāng)前UI中xctuielement的一個(gè)類(lèi)
- XCUIElement UI測(cè)試中任何一個(gè)item項(xiàng)都被抽象成一個(gè)XCUIElement類(lèi)型
當(dāng)我們獲取了錄制生成的代碼以后,根據(jù)UITest提供的三種對(duì)象,我可以在此來(lái)對(duì)測(cè)試代碼進(jìn)行修改,調(diào)試
UITest中同樣適用以下斷言等:
XCTAssert(expression, format...)
//Bool測(cè)試:
XCTAssertTrue(expression, format...)
XCTAssertFalse(expression, format...)
//相等測(cè)試
XCTAssertEqual(expression1, expression2, format...)
XCTAssertNotEqual(expression1, expression2, format...)
//double float 對(duì)比數(shù)據(jù)測(cè)試使用
XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, format...)
XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, format...)
//Nil測(cè)試,XCTAssert[Not]Nil斷言判斷給定的表達(dá)式值是否為nil
XCTAssertNil(expression, format...)
XCTAssertNotNil(expression, format...)
//失敗斷言
XCTFail(format...)
.....
關(guān)于Xcode 7 UnitTest的問(wèn)題就講到此處,希望有興趣的同學(xué)大家共同交流...
4. 小結(jié)
1.總結(jié)現(xiàn)有問(wèn)題,分享心得
Xcode6的內(nèi)置工具終于足夠的好了。也就是說(shuō)即使是很大的APP也沒(méi)有必要為了單元測(cè)試的代碼覆蓋率而排斥Xcode內(nèi)置的測(cè)試工具。無(wú)論什么樣的測(cè)試,XCTest的各種斷言、expectation和性能測(cè)試都足夠應(yīng)對(duì)。但是無(wú)論多好的工具,都需要用好才行。
如果你在測(cè)試iOS或者OS X的APP,開(kāi)始為自動(dòng)添加的測(cè)試類(lèi)添加一些斷言并按下Command+U。你一定會(huì)發(fā)現(xiàn)感覺(jué)這些工具讓你的測(cè)試方便不少 。
WWDC2015 What's New in Xcode
WWDC2015 UI Testing in Xcode
WWDC2015 Testing in Xcode 6
Mattt Thompson Blog
使用Xcode自帶的單元測(cè)試
method-swizzling
Parse 開(kāi)源代碼