單元測試

  • 測試導航(test navigator)

當進行測試時您访,你會經(jīng)常使用到Xcode的測試導航(test navigator)沿侈。
測試導航(test navigator)是workspace的一部分芽突,使創(chuàng)建纬乍、管理、運行和評估測試更加加單粒褒。通過點擊導航選擇欄中的圖標進入測試斤寂,該圖標在問題導航和調(diào)試導航之間耿焊。
![查看測試用例](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午4.45.25.png)
注意:Xcode 測試目標(target)生成的測試包(test bundle)顯示在測試導航(test navigator)處。

![測試目標2](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午4.46.15.png)

綠色為測試通過遍搞、紅色為不通過

![運行測試2](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午4.46.41.png)

灰色菱形代表性能測試 這也就能說明這里為什么沒有通過或是失敗的斷言點擊此處會出現(xiàn)性能測試結(jié)果面板罗侯,此面板允許設置性能基準以及編輯基線和最大參數(shù)
![測試結(jié)果面板](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午4.46.50.png)

關于測試文件方法

  • 所有的測試用例方法保證以test開頭
  • setUp在測試用例之前調(diào)用的初始化方法tearDown釋放測試用例資源在每個測試用例完成后調(diào)用testExample例子testPerformanceExample測試性能的例子,在block內(nèi)寫測試性能的代碼溪猿,設置baseline(基準)和stddev(標準偏差)來判斷方法是否能通過性能測試钩杰。
- (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.
    }];
}

斷言

XCTest

XCTFail(format…) 生成一個失敗的測試纫塌;

XCTAssertNil(a1, format...)為空判斷,a1為空時通過讲弄,反之不通過措左;

XCTAssertNotNil(a1, format…)不為空判斷,a1不為空時通過避除,反之不通過怎披;

XCTAssert(expression, format...)當expression求值為TRUE時通過;

XCTAssertTrue(expression, format...)當expression求值為TRUE時通過瓶摆;

XCTAssertFalse(expression, format...)當expression求值為False時通過凉逛;

XCTAssertEqualObjects(a1, a2, format...)判斷相等,[a1 isEqual:a2]值為TRUE時通過群井,其中一個不為空時状飞,不通過;

XCTAssertNotEqualObjects(a1, a2, format...)判斷不等蝌借,[a1 isEqual:a2]值為False時通過昔瞧;

XCTAssertEqual(a1, a2, format...)判斷相等(當a1和a2是 C語言標量指蚁、結(jié)構(gòu)體或聯(lián)合體時使用,實際測試發(fā)現(xiàn)NSString也可以)菩佑;

XCTAssertNotEqual(a1, a2, format...)判斷不等(當a1和a2是 C語言標量、結(jié)構(gòu)體或聯(lián)合體時使用)凝化;

XCTAssertEqualWithAccuracy(a1, a2, accuracy, format...)判斷相等稍坯,(double或float類型)提供一個誤差范圍,當在誤差范圍(+/-accuracy)以內(nèi)相等時通過測試搓劫;

XCTAssertNotEqualWithAccuracy(a1, a2, accuracy, format...) 判斷不等瞧哟,(double或float類型)提供一個誤差范圍,當在誤差范圍以內(nèi)不等時通過測試枪向;

XCTAssertThrows(expression, format...)異常測試勤揩,當expression發(fā)生異常時通過;反之不通過秘蛔;(很變態(tài)) XCTAssertThrowsSpecific(expression, specificException, format...) 異常測試陨亡,當expression發(fā)生specificException異常時通過;反之發(fā)生其他異成钤保或不發(fā)生異常均不通過负蠕;

XCTAssertThrowsSpecificNamed(expression, specificException, exception_name, format...)異常測試,當expression發(fā)生具體異常倦畅、具體異常名稱的異常時通過測試遮糖,反之不通過;

XCTAssertNoThrow(expression, format…)異常測試叠赐,當expression沒有發(fā)生異常時通過測試欲账;

XCTAssertNoThrowSpecific(expression, specificException, format...)異常測試屡江,當expression沒有發(fā)生具體異常、具體異常名稱的異常時通過測試赛不,反之不通過盼理;

XCTAssertNoThrowSpecificNamed(expression, specificException, exception_name, format...)異常測試,當expression沒有發(fā)生具體異常俄删、具體異常名稱的異常時通過測試宏怔,反之不通過

特別注意下XCTAssertEqualObjects和XCTAssertEqual。
XCTAssertEqualObjects(a1, a2, format...)的判斷條件是[a1 isEqual:a2]是否返回一個YES畴椰。XCTAssertEqual(a1, a2, format...)的判斷條件是a1 == a2是否返回一個YES臊诊。對于后者,如果a1和a2都是基本數(shù)據(jù)類型變量斜脂,那么只有a1 == a2才會返回YES抓艳。

示例如下:

  • 斷言:
- (void)testExample {
    //設置變量和預期效果
    NSUInteger a = 10;NSUInteger b = 15;
    NSUInteger c = 24;
    //執(zhí)行方法得到
    NSUInteger sum = [self sum:a b:b];
    //斷言判定實際值和預期是否符合
    XCTAssertEqual(sum, c,@"測試不通過");
}
-(NSUInteger)sum:(NSUInteger)a b:(NSUInteger)b{
    return a+b;
}
  • 性能

關于性能測試:設置baseline(基準)和stddev(標準偏差)來判斷方法是否能通過性能測試。
就比如說關于網(wǎng)絡請求通常我們給出一個標準帚戳,多長時間內(nèi)請求到結(jié)果玷或,能夠接受的偏差是多少。那么就可以設置一個基準盒偏差來驗證這個請求片任。

  • 異步

官方文檔中給出

- (void)testDocumentOpening
{
    // 創(chuàng)建一個expectation對象
    XCTestExpectation *documentOpenExpectation = [self expectationWithDescription:@"document open"];

    NSURL *URL = [[NSBundle bundleForClass:[self class]]
                              URLForResource:@"TestDocument" withExtension:@"mydoc"];
    UIDocument *doc = [[UIDocument alloc] initWithFileURL:URL];
    [doc openWithCompletionHandler:^(BOOL success) {
        XCTAssert(success);

        // assert成功后 便會調(diào)用expectation的fulfill方法偏友,來觸發(fā)下面的handler
        [documentOpenExpectation fulfill];
    }];

    // 在case最后使用這一方法,此時單測程序會阻塞到這里对供;除非達到了超時時間(秒單位)或者是回調(diào)函數(shù)中調(diào)用了fulfill位他,單測程序才會結(jié)束
    // 若是超時情況,則認為case失敳 鹅髓;若通過expectation的fulfill觸發(fā),則case通過
    [self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
        [doc closeWithCompletionHandler:nil];
    }];
}
  - (void)testAsynExample {
    XCTestExpectation *exp = [self expectationWithDescription:@"這里可以是操作出錯的原因描述京景。窿冯。。"];
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperationWithBlock:^{
        //模擬這個異步操作需要2秒后才能獲取結(jié)果确徙,比如一個異步網(wǎng)絡請求
        sleep(2);
        //模擬獲取的異步操作后醒串,獲取結(jié)果,判斷異步方法的結(jié)果是否正確
        XCTAssertEqual(@"a", @"a");
        //如果斷言沒問題米愿,就調(diào)用fulfill宣布測試滿足
        [exp fulfill];
    }];

    //設置延遲多少秒后厦凤,如果沒有滿足測試條件就報錯
    [self waitForExpectationsWithTimeout:3 handler:^(NSError * _Nullable error) {
        if (error) {
            NSLog(@"Timeout Error: %@", error);
        }
    }];
}

這個測試肯定是通過的,因為設置延遲為3秒育苟,而異步操作2秒就除了一個正確的結(jié)果较鼓,并宣布了條件滿足 [exp fulfill],但是當我們把延遲改成1秒,這個測試用例就不會成功博烂,錯誤原因是 expectationWithDescription:@"這里可以是操作出錯的原因描述香椎。。禽篱。

異步測試除了使用 expectationWithDescription以外畜伐,還可以使用 expectationForPredicateexpectationForNotification

下面這個例子使用expectationForPredicate 測試方法,代碼來自于AFNetworking躺率,用于測試backgroundImageForState方法

- (void)testThatBackgroundImageChanges {
    XCTAssertNil([self.button backgroundImageForState:UIControlStateNormal]);
    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(UIButton  * _Nonnull button, NSDictionary<NSString *,id> * _Nullable bindings) {
            return [button backgroundImageForState:UIControlStateNormal] != nil;
    }];

    [self expectationForPredicate:predicate
              evaluatedWithObject:self.button
                          handler:nil];
    [self waitForExpectationsWithTimeout:20 handler:nil];
}

利用謂詞計算玛界,button是否正確的獲得了backgroundImage,如果正確20秒內(nèi)正確獲得則通過測試悼吱,否則失敗慎框。
expectationForNotification 方法 ,該方法監(jiān)聽一個通知,如果在規(guī)定時間內(nèi)正確收到通知則測試通過。

- (void)testAsynExample1 {
    [self expectationForNotification:(@"監(jiān)聽通知的名稱xxx") object:nil handler:nil];
    [[NSNotificationCenter defaultCenter]postNotificationName:@"監(jiān)聽通知的名稱xxx" object:nil];

    //設置延遲多少秒后后添,如果沒有滿足測試條件就報錯
    [self waitForExpectationsWithTimeout:3 handler:nil];
}

這個例子也可以用expectationWithDescription實現(xiàn),只是多些很多代碼而已笨枯,但是這個可以幫助你更好的理解 expectationForNotification 方法和 expectationWithDescription 的區(qū)別。同理遇西,expectationForPredicate方法也可以使用expectationWithDescription實現(xiàn)馅精。

func testAsynExample1() {
    let expectation = expectationWithDescription("監(jiān)聽通知的名稱xxx")
    let sub = NSNotificationCenter.defaultCenter().addObserverForName("監(jiān)聽通知的名稱xxx", object: nil, queue: nil) { (not) -> Void in
        expectation.fulfill()
    }
    NSNotificationCenter.defaultCenter().postNotificationName("監(jiān)聽通知的名稱xxx", object: nil)
    waitForExpectationsWithTimeout(1, handler: nil)
    NSNotificationCenter.defaultCenter().removeObserver(sub)
}

測試失敗的調(diào)試工具

  • 斷點調(diào)試

添加測試斷點

![斷點調(diào)試](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午5.50.42.png)

當測試方法發(fā)布故障斷言時,此斷點停止測試運??行粱檀。這樣可以通過在測試代碼中設置的斷點停止執(zhí)行洲敢,快速找到存在問題的位置。
![測試斷點](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午5.51.48.png)

使用命令行運行測試

使用Xcode命令行工具梧税,你可以使用腳本自動構(gòu)建和測試你的項目沦疾。

  • 例如:

測試本地OS X的MyApp
xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=OS X,arch=x86_64'

模擬器上運行称近,使用模擬器可以應對不同的外形因素和操作系統(tǒng)版本:
例如 > xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone,0S=7.0'

測試機上運行:
> xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp -destination 'platform=iOS,name=Development test6s

destination參數(shù)可以連接在一起第队,一個命令在指定共享配置目標上執(zhí)行測試。例如刨秆,下面的命令鏈將前面三個例子結(jié)合在一起變成一個命令:

xcodebuild test -project MyAppProject.xcodeproj -scheme MyApp-destination 'platform=OS X,arch=x86_64'-destination 'platform=iOS,name=Development iPod touch'-destination 'platform=Simulator,name=iPhone,OS=9.0'

如果測試失敗凳谦,xcodebuild返回非零退出代碼。

關于命令行運行測試衡未,你需要知道一些基本要素尸执。關于xcodebuild的詳細信息,在終端應用窗口使用man:

man xcodebuild

輔助工具

![輔助編輯器](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午6.06.43.png)

Test Callers category 缓醋。如果你剛剛修復導致應用測試失敗的方法如失,也許你希望檢查其他測試中調(diào)用的該方法是否運行成功。在源代碼編輯器中有問題的方法中送粱,打開assistant editor并從菜單中選擇 Test Classes category褪贵。在彈窗菜單中可以導航到任何調(diào)用它的測試方法中,運行這些測試方法確保回歸測試脆丁。

Test Classes category世舰。它與Test Callers category.類似,但顯示的是有測試方法的類的列表槽卫,可以導航到你正在編輯的類跟压。

代碼覆蓋

代碼覆蓋率是Xcode7的功能,可以在視覺上看到和衡量你的代碼測試覆蓋率歼培。有了代碼覆蓋率震蒋,你可以確定測試是否符合你的預期。
啟用代碼覆蓋率

Xcode的代碼覆蓋率由LLVM支持的測試操作躲庄。當你啟用代碼覆蓋率喷好,LLVM基于方法和函數(shù)調(diào)用的頻率來收集覆蓋數(shù)據(jù)。代碼覆蓋率選項可以收集單元測試和UI測試正確性和性能數(shù)據(jù)读跷,

編輯scheme的測試操作可以啟用代碼覆蓋率梗搅。

  1. 在scheme編輯菜單中選擇Edit Scheme。
    ![選擇Edit Scheme](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午6.12.41.png)
  2. 選擇測試操作效览。
  3. 啟用代碼覆蓋復選框以收集覆蓋數(shù)據(jù)无切。
    ![啟用代碼覆蓋](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午6.13.52.png)

注意:收集代碼覆蓋率數(shù)據(jù)會導致性能損耗。無論損耗是否顯著丐枉,它均會影響執(zhí)行代碼的線性方式哆键,因此在測試運行中啟用代碼覆蓋率,性能結(jié)果依然具有可比性瘦锹。然而籍嘹,當你正在認真評估測試程序性能時,你應該考慮是否啟用代碼覆蓋率弯院。

  • 代碼覆蓋率如何符合測試

代碼覆蓋率是用來衡量測試價值的工具辱士。它回答了以下問題

當你運行測試時,什么代碼真正運行听绳?
多少測試才算足夠颂碘?
換句話說,你是否設計足夠的測試確保你所有的代碼都檢查了正確性和性能椅挣?
代碼的哪部分沒有被測試头岔?
在測試運行完成后,Xcode采用LLVM覆蓋數(shù)據(jù)并在報告導航中創(chuàng)建覆蓋率報告鼠证,參見覆蓋率面板峡竣。它顯示了測試的摘要信息,源文件和源文件中的方法列表以及每個文件中的覆蓋百分比量九。
![代碼覆蓋率](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午6.15.54.png)

源代碼編輯器展示了文件中代碼的行數(shù)并高亮未執(zhí)行的代碼适掰。它高亮需要覆蓋的代碼區(qū)域而非已經(jīng)覆蓋的區(qū)域。

例如,將指針放在-[Calculator input:]方法上攻谁,將顯示一個按鈕稚伍,索引到源代碼。
![索引代碼](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午6.17.12.png)

覆蓋注釋在右邊顯示戚宦,顯示了在測試中代碼某個特定部分被執(zhí)行的次數(shù)个曙。例如:
![顯示執(zhí)行次數(shù)](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午6.18.00.png)
input:方法,在測試中被頻繁調(diào)用受楼。然而垦搬,有部分方法并未被調(diào)用。在源代碼編輯器中有明顯的標記艳汽,如圖:
![提示](http://ogpt2m9nl.bkt.clouddn.com/屏幕快照 2017-03-26 下午6.18.07.png)

官方文檔:關于Xcode測試

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猴贰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子河狐,更是在濱河造成了極大的恐慌米绕,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馋艺,死亡現(xiàn)場離奇詭異栅干,居然都是意外死亡,警方通過查閱死者的電腦和手機捐祠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門碱鳞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踱蛀,你說我怎么就攤上這事窿给。” “怎么了率拒?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵崩泡,是天一觀的道長。 經(jīng)常有香客問我俏橘,道長允华,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任寥掐,我火速辦了婚禮,結(jié)果婚禮上磷蜀,老公的妹妹穿的比我還像新娘召耘。我一直安慰自己,他們只是感情好褐隆,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布污它。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衫贬。 梳的紋絲不亂的頭發(fā)上德澈,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音固惯,去河邊找鬼梆造。 笑死,一個胖子當著我的面吹牛葬毫,可吹牛的內(nèi)容都是我干的镇辉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼贴捡,長吁一口氣:“原來是場噩夢啊……” “哼忽肛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起烂斋,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屹逛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后汛骂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煎源,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年香缺,在試婚紗的時候發(fā)現(xiàn)自己被綠了手销。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡图张,死狀恐怖锋拖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祸轮,我是刑警寧澤兽埃,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站适袜,受9級特大地震影響柄错,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苦酱,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一售貌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疫萤,春花似錦颂跨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽池颈。三九已至,卻和暖如春钓丰,著一層夾襖步出監(jiān)牢的瞬間躯砰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工携丁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留琢歇,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓则北,卻偏偏與公主長得像矿微,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尚揣,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

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