[iOS-Practice] 單元測(cè)試

關(guān)于單元測(cè)試

在計(jì)算機(jī)編程中俏蛮,單元測(cè)試(英語(yǔ):Unit Testing)又稱為模塊測(cè)試, 是針對(duì)程序模塊的最小單位來進(jìn)行正確性檢驗(yàn)的測(cè)試工作瘦材。程序單元是應(yīng)用的最小可測(cè)試部件。在過程化編程中拷恨,一個(gè)單元就是單個(gè)程序超陆、函數(shù)、過程等喇澡;對(duì)于面向?qū)ο缶幊萄刚ぃ钚卧褪欠椒ǎɑ悾ǔ悾┣缇痢⒊橄箢惗链妗⒒蛘吲缮悾ㄗ宇悾┲械姆椒ā?-- 維基百科

《你應(yīng)該知道的單元測(cè)試》這篇文章對(duì)單元測(cè)試的基礎(chǔ)思想做了很好的總結(jié)。
ObjC 中國(guó)的期刊在第15期也討論了“測(cè)試”這個(gè)專題呕屎。

XCTest

XCTest是蘋果公司提供的一個(gè)非常簡(jiǎn)單并且直接集成在 Xcode 中的測(cè)試框架让簿。當(dāng)工程創(chuàng)建時(shí),Xcode 會(huì)自動(dòng)為我們創(chuàng)建一個(gè)名為ProjectNameTests的路徑并添加一個(gè)測(cè)試用例模板文件ProjectNameTests.m(如果創(chuàng)建時(shí)未添加秀睛,之后可以通過添加 target 的方式增加測(cè)試 bundle)尔当。通過這個(gè)模板文件,我們可以了解XCTest框架的使用方法蹂安。

#import <XCTest/XCTest.h>

@interface UnitTestDemoTests : XCTestCase

@end

@implementation UnitTestDemoTests

- (void)setUp {
    [super setUp];
    // 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.
    [super tearDown];
}

- (void)testExample {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}

- (void)testPerformanceExample {
    // This is an example of a performance test case.
    [self measureBlock:^{
        // Put the code you want to measure the time of here.
    }];
}

@end

首先椭迎,我們的測(cè)試用例類要繼承自XCTestCase類锐帜,其中[setUp]方法會(huì)在每個(gè)測(cè)試方法前執(zhí)行,而[tearDown]方法會(huì)在每個(gè)測(cè)試方法后執(zhí)行畜号,真正的測(cè)試方法必須以 testXXX 的格式命名缴阎,且不能有參數(shù)。

測(cè)試時(shí)弄兜,快捷鍵command + u可以一次執(zhí)行所有的測(cè)試药蜻,也可以點(diǎn)擊每個(gè)測(cè)試方法旁的播放按鈕執(zhí)行單獨(dú)的測(cè)試。

實(shí)踐

那么替饿,我們?cè)鯓觼韺懸粋€(gè)測(cè)試用例呢语泽?測(cè)試用例的意義在于,驗(yàn)證某個(gè)類的某個(gè)行為在某種上下文中是否能得到預(yù)期的結(jié)果视卢。通常踱卵,我們可以根據(jù) Given-When-Then 模式來組織我們的測(cè)試用例,將測(cè)試用例拆分成三個(gè)部分据过。

  • Given:準(zhǔn)備測(cè)試功能的上下文惋砂,包括測(cè)試方法需要的參數(shù)等。
  • When:執(zhí)行真正要測(cè)試的代碼绳锅。
  • Then:根據(jù)功能執(zhí)行的結(jié)果斷言測(cè)試是否通過西饵。

例:

- (void)testThatItDoesURLEncoding { 
    // given
    NSString *searchQuery = @"$content$amp;?@"; HTTPRequest *request = [HTTPRequest requestWithURL:@"/search?q=%@", searchQuery]; 
    // when
    NSString *encodedURL = request.URL;
    // then
    XCTAssertEqualObjects(encodedURL, @"/search?q=%24%26%3F%40");
}

在 Then 階段,XCTest框架提供了多個(gè)斷言宏供我們使用:

//生成一個(gè)失敗的測(cè)試
XCTFail(format…)
//為空判斷鳞芙,a1為空時(shí)通過眷柔,反之不通過
XCTAssertNil(a1, format...) 
//不為空判斷,a1不為空時(shí)通過原朝,反之不通過
XCTAssertNotNil(a1, format…)
//當(dāng)expression求值為TRUE時(shí)通過
XCTAssert(expression, format...)
//當(dāng)expression求值為TRUE時(shí)通過
XCTAssertTrue(expression, format...) 
//當(dāng)expression求值為False時(shí)通過
XCTAssertFalse(expression, format...) 
//判斷相等驯嘱,[a1 isEqual:a2]值為TRUE時(shí)通過
XCTAssertEqualObjects(a1, a2, format...) 
//判斷不等,[a1 isEqual:a2]值為False時(shí)通過
XCTAssertNotEqualObjects(a1, a2, format...) 
//判斷相等(當(dāng)a1和a2是 C語(yǔ)言標(biāo)量喳坠、結(jié)構(gòu)體或聯(lián)合體時(shí)使用,實(shí)際測(cè)試發(fā)現(xiàn)NSString也可以)
XCTAssertEqual(a1, a2, format...) 
//判斷不等(當(dāng)a1和a2是 C語(yǔ)言標(biāo)量鞠评、結(jié)構(gòu)體或聯(lián)合體時(shí)使用)
XCTAssertNotEqual(a1, a2, format...) 
//判斷相等,(double或float類型)提供一個(gè)誤差范圍壕鹉,當(dāng)在誤差范圍(+/-accuracy)以內(nèi)相等時(shí)通過測(cè)試
XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...) 
//判斷不等剃幌,(double或float類型)提供一個(gè)誤差范圍,當(dāng)在誤差范圍以內(nèi)不等時(shí)通過測(cè)試
XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...) 
 //異常測(cè)試晾浴,當(dāng)expression發(fā)生異常時(shí)通過
XCTAssertThrows(expression, format...)
 //異常測(cè)試锥忿,當(dāng)expression發(fā)生specificException異常時(shí)通過;反之發(fā)生其他異车±撸或不發(fā)生異常均不通過
XCTAssertThrowsSpecific(expression, specificException, format...)
 //異常測(cè)試敬鬓,當(dāng)expression發(fā)生具體異常、具體異常名稱的異常時(shí)通過測(cè)試,反之不通過
XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)
XCTAssertNoThrow(expression, format…) //異常測(cè)試钉答,當(dāng)expression沒有發(fā)生異常時(shí)通過測(cè)試础芍;
//異常測(cè)試,當(dāng)expression沒有發(fā)生具體異常数尿、具體異常名稱的異常時(shí)通過測(cè)試仑性,反之不通過
XCTAssertNoThrowSpecific(expression, specificException, format...) 
//異常測(cè)試,當(dāng)expression沒有發(fā)生具體異常右蹦、具體異常名稱的異常時(shí)通過測(cè)試诊杆,反之不通過
XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...) 

另外,如果多個(gè)測(cè)試用例類需要一些相同的初始化條件何陆,我們可以實(shí)現(xiàn)一個(gè)XCTestCase類的派生類作為基類晨汹。在這個(gè)類中實(shí)現(xiàn)一些公共的方法和屬性。之后贷盲,測(cè)試用例類直接繼承自這個(gè)基類淘这,要注意在[setUp]方法和[tearDown]方法中調(diào)用 super 的實(shí)現(xiàn)。

網(wǎng)絡(luò)請(qǐng)求的測(cè)試

由于單元測(cè)試是在主線程中進(jìn)行的巩剖,因此如果只是在網(wǎng)絡(luò)請(qǐng)求異步響應(yīng)的方法中執(zhí)行斷言铝穷,那么測(cè)試在異步操作返回結(jié)果前就已經(jīng)結(jié)束了,無法達(dá)到測(cè)試的目的佳魔。對(duì)于異步操作的測(cè)試曙聂,XCTest框架提供了這樣一種機(jī)制,首先在測(cè)試方法中關(guān)聯(lián)一個(gè)代表期望的XCTestExpectation實(shí)例鞠鲜,然后在測(cè)試方法結(jié)束前執(zhí)行方法[- waitForExpectationsWithTimeout:handler:]筹陵,該方法會(huì)執(zhí)行一個(gè) run loop 直到所有的期望實(shí)例執(zhí)行了方法[- fulfill](即期望達(dá)成)或者達(dá)到超時(shí)時(shí)間。例如 AFNetworking 中的一個(gè)測(cè)試:

- (void)testDataTaskDoesReportDownloadProgress {
    NSURLSessionDataTask *task;

    __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Progress should equal 1.0"];
    task = [self.localManager
            dataTaskWithRequest:[self bigImageURLRequest]
            uploadProgress:nil
            downloadProgress:^(NSProgress * _Nonnull downloadProgress) {
                if (downloadProgress.fractionCompleted == 1.0) {
                    [expectation fulfill];
                }
            }
            completionHandler:nil];
    
    [task resume];
    [self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
}

通過上述機(jī)制镊尺,雖然我們可以直接測(cè)試真正的網(wǎng)絡(luò)請(qǐng)求,但是真實(shí)網(wǎng)絡(luò)環(huán)境是非常復(fù)雜的并思,返回的響應(yīng)具有不確定性庐氮,為了達(dá)到單元測(cè)試關(guān)注點(diǎn)單一的目的,我們可能需要模擬確定的網(wǎng)絡(luò)請(qǐng)求響應(yīng)數(shù)據(jù)宋彼。OHHTTPStubs 通過NSURLProtocol實(shí)現(xiàn)了模擬網(wǎng)絡(luò)請(qǐng)求響應(yīng)的功能弄砍,是在對(duì)網(wǎng)絡(luò)請(qǐng)求相關(guān)代碼進(jìn)行單元測(cè)試時(shí),非常好用的工具输涕。這篇文章對(duì)其實(shí)現(xiàn)原理進(jìn)行了介紹:《如何進(jìn)行 HTTP Mock(iOS)》

如果是通過 Cocoapods 來安裝管理 OHHTTPStubs 的話音婶,那么默認(rèn)在 test target 中 import 頭是找不到 pods 中的類庫(kù)的,需要在 test target 的 Build Settings 中設(shè)置 Header Search Paths莱坎,可以復(fù)制產(chǎn)品 target 中對(duì)應(yīng)的值衣式,而其中的 pods 路徑別名也需要在 test target 中設(shè)置。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碴卧,隨后出現(xiàn)的幾起案子弱卡,更是在濱河造成了極大的恐慌,老刑警劉巖住册,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婶博,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡荧飞,警方通過查閱死者的電腦和手機(jī)凡人,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叹阔,“玉大人挠轴,你說我怎么就攤上這事√趸瘢” “怎么了忠荞?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)帅掘。 經(jīng)常有香客問我委煤,道長(zhǎng),這世上最難降的妖魔是什么修档? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任碧绞,我火速辦了婚禮,結(jié)果婚禮上吱窝,老公的妹妹穿的比我還像新娘讥邻。我一直安慰自己,他們只是感情好院峡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布兴使。 她就那樣靜靜地躺著,像睡著了一般照激。 火紅的嫁衣襯著肌膚如雪发魄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天俩垃,我揣著相機(jī)與錄音励幼,去河邊找鬼。 笑死口柳,一個(gè)胖子當(dāng)著我的面吹牛苹粟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播跃闹,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼嵌削,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼毛好!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掷贾,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤睛榄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后想帅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體场靴,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年港准,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旨剥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浅缸,死狀恐怖轨帜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衩椒,我是刑警寧澤蚌父,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站毛萌,受9級(jí)特大地震影響苟弛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阁将,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一膏秫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧做盅,春花似錦缤削、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至图筹,卻和暖如春帅刀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背婿斥。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哨鸭,地道東北人民宿。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像像鸡,于是被迫代替她去往敵國(guó)和親活鹰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哈恰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 如果有測(cè)試大佬發(fā)現(xiàn)內(nèi)容不對(duì),歡迎指正志群,我會(huì)及時(shí)修改着绷。 大多數(shù)的iOS App(沒有持續(xù)集成)迭代流程是這樣的...
    默默_David閱讀 1,652評(píng)論 0 4
  • 簡(jiǎn)介 測(cè)試目的:模擬多種可能性,減少錯(cuò)誤锌云,增強(qiáng)健壯性荠医,提高穩(wěn)定性。 測(cè)試種類:在iOS中的通常分為單元測(cè)試和UI測(cè)...
    i順頌時(shí)宜閱讀 9,098評(píng)論 0 39
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理桑涎,服務(wù)發(fā)現(xiàn)彬向,斷路器,智...
    卡卡羅2017閱讀 134,600評(píng)論 18 139
  • 單元測(cè)試不是一個(gè)小工程攻冷,需要多用些時(shí)間才能做好娃胆,不要希望通過這個(gè)文章就能掌握單元測(cè)試,這只是一個(gè)入門等曼,需要自己動(dòng)手...
    勇不言棄92閱讀 7,788評(píng)論 9 60
  • 文章來自:http://blog.csdn.net/mj813/article/details/52451355 ...
    好大一只鵬閱讀 9,188評(píng)論 2 126