iOS 單元測試和 UI 測試快速入門

前言

平時寫完業務代碼的時候都會去本身測試一遍,後面每次有修改都須要重複測,不論是一個業務流程仍是一個工具類,其實均可以經過測試框架來幫助咱們完成測試,特別是一些頻繁修改的代碼,更須要嚴謹的測試。在淺淺地對自動化測試有一些瞭解時,以爲寫測試代碼挺耗時間,但其實對後期的幫助是很是大的,能夠根據本身的實際狀況來決定哪些地方須要加入自動化測試。git

本文內容適合剛接觸 iOS 自動化測試的同窗,基本內容來自於各年 WWDC 的多個 Sessions,本文代碼部分基於個人一個學習 Demo,喜歡的能夠了解一下。本文介紹的大體內容包括:github

  • 單元測試
  • UI 測試
  • 拓展 Tips
  • 工程可測性

1、單元測試

1.1 加入測試 Target

在新建項目時,勾選Include Unit TestsInclude UI Tests,便可爲項目添加單元測試和 UI 測試。api

在添加測試代碼時,你須要遵照一些最基本的規則:xcode

  • 全部的測試類須要繼承XCTestCase安全

    @interface TTTestCase : XCTestCase
    複製代碼
  • 測試方法命名以 test 開始bash

    - (void)testThatMyFunctionWorks
    複製代碼
  • 用 Assertion API 進行驗證是否經過網絡

    XCTAssertEqual(value, expectedValue)
    複製代碼

1.2 啓動測試

單元測試的結構:app

  • step1:準備輸入
  • step2:運行正在測試的代碼
  • step3:驗證輸出
// 準備輸入
NSString *dateString = @"2000-01-01";

// 須要測試的方法
BOOL isToday = [TTDateFormatter isTodayWithDateString:dateString];

// 驗證輸出
XCTAssert(isToday, @"isToday false");
複製代碼

以上三個部分的代碼準備完成後便可開始測試,啓動的方式有不少種,能夠根據你的實際狀況選擇如下方式:框架

  • 代碼編輯器邊欄菱形按鈕,測試單個用例
  • Test 導航欄,測試單個用例
  • 快捷鍵⌘ + U測試所有用例
  • 使用命令行工具 xcodebuild 能夠測試單個用例,也能夠測試所有用例。

123

1.3 性能測試

性能測試經過度量代碼塊執行所消耗的時間長短,來衡量是否經過測試。異步

1.3.1 如何進行性能測試

相關 API :

  • measureBlock:

    - (void)testPerformanceOfMyFunction {
    
        [self measureBlock:^{
            // Do that thing you want to measure.
            MyFunction();
        }];
    }
    複製代碼
  • measureMetrics:automaticallyStartMeasuring:forBlock:

    - (void)testMyFunction2_WallClockTime {
        [self measureMetrics:[self class].defaultPerformanceMetrics automaticallyStartMeasuring:NO forBlock:^{
    
            // Do setup work that needs to be done for every iteration but you don't want to measure before the call to -startMeasuring SetupSomething(); [self startMeasuring]; // Do that thing you want to measure. MyFunction(); [self stopMeasuring]; // Do teardown work that needs to be done for every iteration but you don't want to measure after the call to -stopMeasuring
            TeardownSomething();
        }];
    }
    複製代碼

1.3.2 設置基準線

全部的性能測試須要設置一個Baseline來驗證是否經過測試,沒有設置的會提示No baseline average for Time

1.2

咱們能夠經過點擊measureBlock:方法左邊菱形圓心 icon ,來設置Baseline,設置以後須要點擊save保存。以後再執行測試用例時,若是成功,左邊的icon會從圓心變成一個 ✅。

1.2

1.4 異步測試

何時須要使用異步測試:

  • 打開文檔
  • 在後臺線程中執行的服務和網絡活動
  • 執行動畫
  • UI 測試時

1.4.1 異步測試 XCTestExpectation

異步測試分爲3個部分: 新建指望等待指望被履行履行指望

  • XCTestExpectation :測試指望,能夠由測試類持有,也能夠本身持有,本身持有測試指望時靈活性更好一些,你能夠選擇等待哪些指望。

    // 測試類持有的初始化方法
    XCTestExpectation *expect1 = [self expectationWithDescription:@"asyncTest1"];
    
    // 本身持有的初始化方法
    XCTestExpectation *expect2 = [[XCTestExpectation alloc] initWithDescription:@"asyncTest3"];
    複製代碼
  • waitForExpectations:timeout: :等待異步的指望代碼執行,根據初始化方式不一樣,等待的方法不一樣。

    // 測試類持有時的等待方法
    [self waitForExpectationsWithTimeout:10.0 handler:nil];
    
    // 本身持有時的等待方法
    [self waitForExpectations:@[expect3] timeout:10.0];
    複製代碼
  • fulfill :履行指望,而且適當加入XCTAssertTrue等斷言,來驗證測試結果。

    XCTestExpectation *expect3 = [[XCTestExpectation alloc] initWithDescription:@"asyncTest3"];
    
    [TTFakeNetworkingInstance requestWithService:apiRecordList completionHandler:^(NSDictionary *response) {
        XCTAssertTrue([response[@"code"] isEqualToString:@"200"]);
        [expect3 fulfill];
    }];
    
    [self waitForExpectations:@[expect3] timeout:10.0];
    複製代碼

1.4.2 異步測試 XCTWaiter

XCTWaiter是 2017 年新增的異步測試方案,能夠經過代理方式來處理異常狀況。

XCTWaiter *waiter = [[XCTWaiter alloc] initWithDelegate:self];
    
XCTestExpectation *expect4 = [[XCTestExpectation alloc] initWithDescription:@"asyncTest3"];
    
[TTFakeNetworkingInstance requestWithService:@"product.list" completionHandler:^(NSDictionary *response) {
	XCTAssertTrue([response[@"code"] isEqualToString:@"200"]);
	expect4 fulfill];
}];

XCTWaiterResult result = [waiter waitForExpectations:@[expect4] timeout:10 enforceOrder:NO];

XCTAssert(result == XCTWaiterResultCompleted, @"failure: %ld", result);
複製代碼

XCTWaiterDelegate:若是委託是XCTestCase實例,下方代理被調用時會報告爲測試失敗。

// 若是有指望超時,則調用。 
- (void)waiter:(XCTWaiter *)waiter didTimeoutWithUnfulfilledExpectations:(NSArray<XCTestExpectation *> *)unfulfilledExpectations;

// 當履行的指望被強制要求按順序履行,但指望以錯誤的順序被履行,則調用。
- (void)waiter:(XCTWaiter *)waiter fulfillmentDidViolateOrderingConstraintsForExpectation:(XCTestExpectation *)expectation requiredExpectation:(XCTestExpectation *)requiredExpectation;

// 當某個指望被標記爲被倒置,則調用。 
- (void)waiter:(XCTWaiter *)waiter didFulfillInvertedExpectation:(XCTestExpectation *)expectation;

// 當 waiter 在 fullfill 和超時以前被打斷,則調用。 
- (void)nestedWaiter:(XCTWaiter *)waiter wasInterruptedByTimedOutWaiter:(XCTWaiter *)outerWaiter;
複製代碼

1.5 查看測試結果

在執行測試用例後,Xcode 會返回給咱們測試結果,能夠經過一下途徑查看:

  • Test 導航欄
  • Issue 導航欄
  • 代碼編輯器左邊欄
  • Report 導航欄

查看測試結果

除此以外,咱們還能夠在 Report 導航欄中查看更加詳細的測試報告:

  • 測試經過/失敗
  • 失敗緣由
  • 性能指標
  • 截屏
  • 嵌套的 activities
  • 測試覆蓋率

1.6 進行單元測試

我新建一個時間工具類,幫助我轉換時間,在使用以前,咱們須要先進行測試,以保證功能完整且正確。

這個工具類有如下 4 個公共方法,

@interface TTDateFormatter : NSDate

+ (NSString *)stringFormatWithDate:(NSDate *)date;

+ (NSDate *)dateFormatWithString:(NSString *)dateString;

+ (BOOL)isTodayWithDateString:(NSString *)dateString;

+ (NSString *)getHowLongAgoWithTimeStamp:(NSTimeInterval)timeStamp;

@end
複製代碼

針對一個工具類的測試咱們能夠新建一個TTDateFormatterTests測試類,繼承一個測試基類。再根據不一樣的方法寫不一樣的測試方法。若是有ifswitch等條件語句致使邏輯分支的代碼,儘可能使各個邏輯分支都能測試到,能夠配合代碼覆蓋率來檢查哪些邏輯分支未測試。

@interface TTDateFormatterTests : TTTestCase
@end

@implementation TTDateFormatterTests

- (void)testDateFormatter {
    NSString *originDateString = @"2018-06-06 20:20:20";
    NSDate *date = [TTDateFormatter dateFormatWithString:originDateString];
    NSString *dateString = [TTDateFormatter stringFormatWithDate:date];
    XCTAssertEqualObjects(dateString, originDateString);
}

- (void)testDateFormatterIsToday {
    NSString *dateString = [TTDateFormatter stringFormatWithDate:[NSDate date]];
    XCTAssertTrue([TTDateFormatter isTodayWithDateString:dateString]);
    XCTAssertFalse([TTDateFormatter isTodayWithDateString:@"2000-01-01"]);
}

- (void)testDateFormatterHowLongAgo {
	// 該方法中包含一個 switch ,要保證 switch 每一個邏輯分支都測試到,因此須要多個測試。
    NSDate *now = [NSDate date];
    NSString *secAgo = [TTDateFormatter getHowLongAgoWithTimeStamp:now.timeIntervalSince1970 - 10 * sec];
    XCTAssertEqualObjects(secAgo, @"10秒前");
    
    NSString *minAgo = [TTDateFormatter getHowLongAgoWithTimeStamp:now.timeIntervalSince1970 - 15 * min];
    XCTAssertEqualObjects(minAgo, @"15分鐘前");
    
    NSString *hourAgo = [TTDateFormatter getHowLongAgoWithTimeStamp:now.timeIntervalSince1970 - 20 * hour];
    XCTAssertEqualObjects(hourAgo, @"20小時前");

    NSString *dayAgo = [TTDateFormatter getHowLongAgoWithTimeStamp:now.timeIntervalSince1970 - 25 * hour];
    XCTAssertEqualObjects(dayAgo, @"1天前");

    NSString *daysAgo = [TTDateFormatter getHowLongAgoWithTimeStamp:now.timeIntervalSince1970 - 50 * hour];
    XCTAssertEqualObjects(daysAgo, @"2天前");

    NSString *longTimeAgo = [TTDateFormatter getHowLongAgoWithTimeStamp:1544002463];
    XCTAssertEqualObjects(longTimeAgo, @"2018-12-05 17:34:23");
}
@end
複製代碼

合理使用測試基類和測試工具類,能夠避免大量重複測試代碼。時間轉換工具類是一個沒有外部依賴的類,當一些對外部有依賴的類須要測試時,能夠嘗試 OCMock ,它能幫助你模擬數據。另外,當你以爲測試框架提供的斷言方法沒法知足你時,也能夠試着使用 OCHamcrest

2、UI 測試

何時須要使用 UI 測試:

  • 單元測試沒法覆蓋時的補充方案
  • 單元測試更精準
  • UI 測試覆蓋面的更全

UI 測試的步驟:

  • step1:與要測試或與邏輯有關的 UI 進行互動
  • step2:驗證 UIelements 屬性和狀態

2.1 UI Recording

經過 UI Recording ,能夠將你操做手機的行爲記錄下來,而且轉換成代碼,能夠幫助你快速生成 UI 測試代碼。

選中 UI 測試類,你能再下方看到一個小紅點,點擊小紅點開始錄製你的交互。

在你進行交互時,Xcode 會自動轉化成代碼,你能夠藉此建立新的測試代碼,也能夠以此拓展已經存在的測試代碼。固然它也不是十分完美,並非總能如你所願,還須要你作一些處理,好比說自動生成的代碼過於繁瑣,你能夠用一些更簡潔的代碼實現。即便這樣,UI Recording 也是很是高效的方式。

2.2 UI 測試相關的類

2.2.1 XCUIApplication

XCUIApplication能夠返回一個應用程序實例,而後你就能夠經過測試代碼啓動應用程序。

// 返回 UI 測試 Target 設置中選中的 Target Application 的實例
- (instancetype)init;

// 根據 bundleId 返回一個應用程序實例
- (instancetype)initWithBundleIdentifier:(NSString *)bundleIdentifier;

// 啓動應用程序
- (void)launch;

// 將應用程序喚醒至前臺,在多程序聯合測試下會用到 
- (void)activate;

// 結束一個正在運行的應用程序
- (void)terminate;
複製代碼

2.2.2 XCUIElement

應用程序中的 UI 控件,控件類型多樣,多是Button,Cell,Window等等。該類實例有不少模擬交互的方法,如tap模擬用戶點擊事件,swipe模擬滑動事件,typeText:模擬用戶輸入內容。

在 UI 測試中咱們須要找到某個空間,能夠經過他們的類型來縮小範圍,好比當前頁面有且只有一個UITextView控件,你能夠經過如下代碼來獲取:

XCUIApplication *app = [[XCUIApplication alloc] init];
[app launch];

// 若是是 Cell 則對應 app.cells 
// firstMatch 返回第一個符合的控件
XCUIElement *textView = app.textViews.firstMatch;

// 模擬用戶在 textView 輸入內容
[textView typeText:@"input string"];
複製代碼

另外還有一種方式經過 Accessibility identifer, label, title 等等方式來定位對應的控件,如尋找一個名爲 Add 的button

// 須要勾選 Accessibility Enabled ,而且在 Label 一欄填入 Add
XCUIElement *addButton = app.buttons[@"add"];
// 模擬用戶點擊按鈕
[addButton tap];
複製代碼

經過類型加 identifier 的方式來定位的控件元素的方式,能夠知足大多數場景。

2.2.3 XCUIElementQuery

XCUIElementQuery是一個用來定位控件元素的類,通常是一組符合篩選條件的元素集合。如app.buttons即返回 XCUIElementQuery 實例,是包含了當前全部的button的集合,你能夠再經過 XCUIElementQuery的方法作下一步的篩選。

XCUIElementQuery 常見定位元素的方法:

  • count:匹配的數量;

    // 當 navigationBars 的 count 等於 1 時,你能夠直接定位到 navigationBar
    app.navigationBars.element 
    複製代碼
  • subscripting:經過 id 來定位

    table.staticTexts["Groceries"] 
    複製代碼
  • index:經過元素的下標來定位

    table.staticTexts.elementAtIndex(0) 
    複製代碼

定位元素除了利用元素類型、Accessibility Identifiers,Predicates 等篩選方法,還能夠結合嵌套的層級關係來幫助定位。

2.3 進行 UI 測試

要進行 UI 測試須要如下幾個步驟:

  • step1:新建一個 UI 測試 Target。
  • step2:使用 UI Recording 或手寫代碼,定位 UI 元素,而且模擬用戶交互事件。
  • step3:加入XCTAssert等斷言邏輯,驗證測試是否經過。
let app = XCUIApplication()
// 啓動 app
app.launch()

// 定位元素
let addButton = app.buttons[「Add」]

// 模擬用戶交互事件
addButton.tap()

// 驗證測試是否經過
XCTTAssertionEqual(app.tables.cells.cout, 1)
複製代碼

大多數 UI 測試都是基於用戶行爲驅動,根據設計好的用戶的操做流程,測試整個流程的結果。我設計了一個簡單的筆記,主要有 3 步操做,分別是建立筆記、展現筆記和刪除筆記,下面一塊兒來看看如何進行測試。

// 測試主流程
- (void)testMainFlow {
	// 啓動 app
    XCUIApplication *app = [[XCUIApplication alloc] init];
    [app launch];
    
    // 添加筆記
    [self addRecordWithApp:app msg:@"今每天氣真好!🌞"];
    [self addRecordWithApp:app msg:@"今天詹姆斯特別給力,帶領球隊走向勝利。✌️"];
	
    while (app.cells.count > 0) {
    	  // 刪除筆記  
        [self deleteFirstRecordWithApp:app];
    }
}

/**
 添加筆記

 @param app app 實例
 @param msg 筆記內容
 */
- (void)addRecordWithApp:(XCUIApplication *)app msg:(NSString *)msg {
	// 暫存當前 cell 數量
    NSInteger cellsCount = app.cells.count;
    
    // 設置一個預期 判斷 app.cells 的 count 屬性會等於 cellsCount+1, 等待直至失敗,若是符合則再也不等待
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"count == %d",cellsCount+1];
    [self expectationForPredicate:predicate evaluatedWithObject:app.cells handler:nil];


	 // 定位導航欄+號按鈕,點擊進入添加筆記頁面 
    XCUIElement *addButton = app.navigationBars[@"Record List"].buttons[@"Add"];
    [addButton tap];
    
    // 測試 未輸入任何內容點擊保存
    [app.navigationBars[@"Write Anything"].buttons[@"Save"] tap];
    
    // 定位文本輸入框 輸入內容
    XCUIElement *textView = app.textViews.firstMatch;
    [textView typeText:msg];
    
    // 保存
    [app.navigationBars[@"Write Anything"].buttons[@"Save"] tap];
        
    // 等待預期
    [self waitShortTimeForExpectations];
}

/**
 刪除最近一個筆記

 @param app app 實例
 */
- (void)deleteFirstRecordWithApp:(XCUIApplication *)app {
    NSInteger cellsCount = app.cells.count;
    
    
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"count == %d",cellsCount-1];
    // 設置一個預期 判斷 app.cells 的 count 屬性會等於 cellsCount-1, 等待直至失敗,若是符合則再也不等待
    [self expectationForPredicate:predicate evaluatedWithObject:app.cells handler:nil];

	// 定位到 cell 元素
    XCUIElement *firstCell = app.cells.firstMatch;
    
    // 左滑出現刪除按鈕
    [firstCell swipeLeft];
    
    // 定位刪除按鈕
    XCUIElement *deleteButton = [app.buttons matchingIdentifier:@"Delete"].firstMatch;
    
    // 點擊刪除按鈕
    if (deleteButton.exists) {
        [deleteButton tap];        
    }
    
    // 等待預期
    [self waitShortTimeForExpectations];

}
複製代碼

在上面的邏輯中涉及到異步的請求,咱們能夠經過利用expectationForPredicate:evaluatedWithObject:handler:方法監聽app.cellscount屬性,當知足NSPredicate條件時,expectation至關於自動fullfill。若是一直不知足條件,會一直等待直至超時,除此以外還能夠用通知和 KVO 的方式實現。

測試過程:

3、 拓展 Tips

3.1 多應用聯合測試

多應用聯合測試時,依賴XCUIApplication類的如下 2 個方法:

  • initWithBundleIdentifier:
  • activate

前者能夠根據 BundleId 獲取其餘 App 的實例,讓咱們能夠啓動其餘 App。後者可讓 App 從後臺切換至前臺,在多應用間切換。簡單實現代碼以下:

// 返回 UI 測試 Target 設置中選中的 Target Application 的實例
XCUIApplication *ttApp = [[XCUIApplication alloc] init];

// 使用 BundleId 得到另一個 App 實例
XCUIApplication *anotherApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"Another.App.BundleId"];

// 先啓動咱們的主 App
[ttApp launch];

// 作一系列測試

// 啓動另外一個 App
[anotherApp launch];

// 作一系列測試

// 回到咱們的主 App (在 App 未啓動的狀況下調 activate 會讓 App 啓動)
[ttApp activate];
複製代碼

3.2 邏輯複雜場景下的 Activities

在一些邏輯比較複雜的測試中,咱們能夠藉助XCTContext類來幫咱們把測試邏輯分割成多個小的測試模塊。好比說咱們有一個業務,關聯多個模塊,這個時候咱們能夠用相似下面的代碼來處理:

// 模塊 1
[XCTContext runActivityNamed:@"step1" block:^(id<XCTActivity>  _Nonnull activity) {
    XCTestExpectation *expect1 = [self expectationWithDescription:@"asyncTest1"];

    [TTFakeNetworkingInstance requestWithService:apiRecordSave completionHandler:^(NSDictionary *response) {
        XCTAssertTrue([response[@"code"] isEqualToString:@"200"]);
        [expect1 fulfill];
    }];
    
}];

// 模塊 2
[XCTContext runActivityNamed:@"step2" block:^(id<XCTActivity>  _Nonnull activity) {
    XCTestExpectation *expect2 = [self expectationWithDescription:@"asyncTest2"];

    [TTFakeNetworkingInstance requestWithService:apiRecordDelete completionHandler:^(NSDictionary *response) {
        XCTAssertTrue([response[@"code"] isEqualToString:@"200"]);
        [expect2 fulfill];
    }];
    
}];

[self waitShortTimeForExpectations];
複製代碼

若是測試成功,能夠在 Report 導航欄看到成功信息,它會按照你設置的模塊分別展現測試結果。

若是測試失敗,你能夠看到哪些模塊是成功的,和在哪些模塊中失敗了。

除此以外,你還能夠嘗試多層嵌套,activity 裏面嵌套 activity。

3.3 截屏

在 UI 測試中有 2 種類型支持經過代碼截屏,分別是XCUIElementXCUIScreen

// 獲取一個截屏對象
XCUIScreenshot *screenshot = [app screenshot];

// 實例化一個附件對象 並傳入截屏對象
XCTAttachment *attachment = [XCTAttachment attachmentWithScreenshot:screenshot];

// 附件的存儲策略 若是選擇 XCTAttachmentLifetimeDeleteOnSuccess 則測試成功的狀況會被刪除
attachment.lifetime = XCTAttachmentLifetimeKeepAlways;

// 設置一個名字 方便區分
attachment.name = @"MyScreenshot";

[self addAttachment:attachment];
複製代碼

在測試結束後,能夠在 Report 導航欄中查看截圖:

除此以外 Xcode 提供了自動截圖的功能,能夠幫助咱們在每個交互操做以後自動截圖。此功能會產生大量截圖,須要謹慎使用,通常狀況最好勾選Delete when each test succeeds,須要在 Edit Scheme -> Test -> Options 中開啓。

因此你能夠根據你的需求選擇適當的截圖策略。

3.4 代碼覆蓋率

代碼覆蓋率在 Report 導航欄中查看,它除了能夠幫你統計測試用例覆蓋的代碼百分比,還能夠幫助你發現哪些代碼是沒有被測試用例覆蓋的,須要在 Edit Scheme -> Test -> Options 中開啓。

你還能夠選擇統計哪些 targets 的代碼覆蓋率,all targets表示統計項目內全部 targets 的覆蓋率,some targets須要你手動添加 target ,只統計手動添加的 target 的覆蓋率。

除了在 Report 導航欄中查看代碼覆蓋率的方式,你還能夠藉助蘋果提供的命令行工具xccov來生成代碼覆蓋率報告。值得一提的是,xccov還能輸出 JSON 格式的報告。

3.5 跳過部分測試

在 Xcode 10 中新增功能,在 Edit Scheme -> Test -> Info -> Tests 中能夠經過取消勾選,來選擇跳過部分測試用例。在 target 的 Options 選項中,Automatically includes new tests,選項是默認勾選的,新建的測試文件會自動添加進去。

3.6 測試用例的執行順序

默認狀況下,測試用例執行的順序是按字母順序來執行的,按固定順序執行可能會使一些隱式的依賴關係沒法被發現。如今有了隨機的執行順序,就能夠挖掘出那些隱式的依賴關係。能夠在 Edit Scheme -> Test -> Info -> Tests -> Options 中開啓該功能。

3.7 並行測試

並行測試能夠同時進行多個測試,從而節省大量時間。在測試時會啓動多個模擬器,模擬器之間的數據都是隔離的,能夠在 Edit Scheme -> Test -> Info -> Tests -> Options 中開啓該功能。

對於並行測試的一些建議:

  • 某個測試用例須要消耗大量時間的類,能夠拆分紅多個類並行測試,從而節省時間。
  • 你須要清楚哪些測試在並行執行時是不安全的,避免並行執行這些測試。
  • 性能測試的能夠統一放在一個 Bundle 中,禁用並行執行。

4、工程可測性

單元測試的結構:

  1. 準備輸入
  2. 運行須要測試的代碼
  3. 驗證輸出

可測試代碼的特徵:

  • 避免過多輸入
  • 輸出是可見的
  • 沒有隱藏的狀態

4.1 可測性 Tips

  • 參數化:減小共享單例的引用,測試的方法須要接受參數輸入和明確的輸出。
  • 分離邏輯和結果:抽離邏輯,最終測試代碼儘可能精簡。
  • 平衡單元測試和 UI 測試:單元測試適合測試用戶交互行爲沒法覆蓋的代碼,和小而完整的代碼。UI 測試更適合測試大範圍的功能集合。

4.2 幫助 UI 測試更加完善

  • 用精巧聰明的代碼縮減 UI 測試的代碼量
  • 將複雜的查詢邏輯進行封裝
  • 對常見組合操做的 UI 測試流程進行封裝
  • 避免邏輯混亂和冗餘

4.3 合理使用快捷鍵

  • 避免使用 macOS 的菜單欄
  • 使邏輯上下緊密,避免邏輯分離

4.3 測試代碼的質量

  • 寫測試代碼以前多構思很重要
  • 測試代碼應該支撐你的 App 的拓展
  • 業務代碼中的編碼原則也適用於測試代碼

本節內容根據 WWDC 2017 Session 414:Engineering for Testability 粗略總結得出,若是須要了解更多相關內容能夠查看相關視頻。

總結

掌握這些測試相關 API 並不難,可是好的代碼須要通過完整項目的磨礪和時間的考驗。同時也能夠借鑑一些開源項目的測試代碼,嘗試着爬上巨人的肩膀。

參考

WWDC 2018 Session 403:What's New in Testing

WWDC 2017 Session 414:Engineering for Testability

WWDC 2017 Session 409:What's New in Testing

WWDC 2015 Session 406:UI Testing in Xcode

WWDC 2014 Session 414:Testing in Xcode 6

相關文章
相關標籤/搜索