概述
Session-409的主題為[What’s New in Testing]梳星,主要包括:
-
Enhancement
優(yōu)化了macOS、xcodebuild在UT中的性能 -
New Asynchronous Testing API
提供新的接口和新的等待方式膨更,讓異步測試更加靈活 -
Multiple App Testing
編寫測試代碼着裹,實現在多個App間自動跳轉幢尚、測試 -
Implements For UITesting
改進UITest茫负,優(yōu)化element選取的算法 -
New Technologies
新的Activities磁浇、attachments、screenshot技術
Enhancement
- Xcode8:XCUISiriTest用于便攜SiriKit的Unit Test朽褪;支持Touchbar的UITest
- Xcode9:改良了swift4的接口,基于block的 Teardown API
- 改進macOS的菜單欄的UI test
- 使用命令行進行測試无虚,直接啟動core simulator進行測試缔赠,不會再看到在測試過程中啟動模擬器了
- 持續(xù)集成場景下,Xcodebuild支持多個設備平行測試友题,Xcode server可以取代macOS server嗤堰,進行持續(xù)化集成,App測試
-
對于有國際化需求的App度宦,在Xcode9的scheme中可以選擇語言和地區(qū)踢匣。應用場景:寫UI Test case生成不同國家對應的截圖,用于App Store的圖片介紹界面戈抄,可以節(jié)約大量的時間离唬,否則只能統一是用一種展示方式:
New Asynchronous Testing API
新老API比較
早在Xcode6中就已經有了異步測試API,使用上也比較簡單划鸽,性能方面也算可以输莺,但是有幾個缺點:
等待超時會被視為測試失敗,在某些情況下我們想對超時的情況做一些特殊的邏輯處理時裸诽,老API無法滿足這個需求
等待代碼中需要持有XCTestExpectation對象嫂用,等待的代碼往往與測試邏輯的代碼混雜在一起,想要把測試代碼與支持代碼變得困難
[[Downloader new] downloadWithCompletion:^(BOOL success, NSError *err) {
XCTAssert(success, @"failed");
[_exception fulfill];
}];
// 隱式等待丈冬,內部可能持有了expectation對象
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
NSLog(@"timeout");
}];
- 需要等到所有處于活躍XCTestExpectation對象全部執(zhí)行
fulfill
后才會返回 - 老的設計模式不支持嵌套waiting
所以嘱函,Apple推出了新的API: XCTWaiter,它將waiting
埂蕊、XCTestCase
往弓、XCTestExpectation
解耦疏唾,除此之外:
- 指明等待的XCTestExpectation對象,
[XCTWaiter waitForExpectations:@[donwloadException] timeout:0.5 enforceOrder:NO];
- delegate回調, 返回結果XCTWaiterResult亮航,允許指定順序
XCTWaiterResult result = [[XCTWaiter alloc] initWithDelegate:self] waitForExpectations:@[donwloadException] timeout:0.5 enforceOrder:YES];
- 支持嵌套
新的異步測試API
- (void)test_asyncDownload_newAPI {
Downloader *loader = [Downloader new];
// download 時間為1s
XCTestExpectation *donwloadException = [[XCTestExpectation alloc] initWithDescription:@"download"];
[loader downloadWithCompletion:^(BOOL success, NSError *err) {
[donwloadException fulfill];
}];
// upload 時間為3s
XCTestExpectation *uploadException = [[XCTestExpectation alloc] initWithDescription:@"upload"];
[loader uploadWithCompletion:^(BOOL success, NSError *err) {
[uploadException fulfill];
}];
XCTWaiterResult result = [XCTWaiter waitForExpectations:@[donwloadException, uploadException] timeout:4 enforceOrder:NO];
XCTAssert(result == XCTWaiterResultCompleted, @"failed: %ld", (long)result);
}
Multiple app test
Xcode9允許通過Bundle ID 和 fileURL(macOS)來創(chuàng)建一個XCUIApplication對象荸实,以便于多個App測試。launch
和activate
可用來將APP從后臺拉到前臺缴淋,區(qū)別在于:如果app在運行中准给,activate不會打斷app。
'' - (void)testMutipleApp {
'' XCUIApplication *readApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.sohu.TestRead"];
'' // 啟動read app
'' [readApp launch];
''
'' XCUIApplication *writeApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.sohu.TestWrite"];
'' // 激活
'' [writeApp activate];
''
'' XCUIElement *writeTextField = writeApp.textFields[@"Input"];
'' [writeTextField tap];
'' [writeTextField typeText:@"Using Testing New API To Do Multiple App UI Test"];
'' [writeApp.buttons[@"return"] tap];
'' [writeApp.buttons[@"Send"] tap];
''
'' // back to read
'' [writeApp.alerts[@"success"].buttons[@"OK"] tap];
'' [writeApp.statusBars.buttons[@"Return to TestRead"] tap];
''
'' // 等待readApp變成活躍狀態(tài)
'' NSPredicate *readAppPredicate = [NSPredicate predicateWithFormat:@"state == %d", XCUIApplicationStateRunningForeground];
'' XCTNSPredicateExpectation *expection = [[XCTNSPredicateExpectation alloc] initWithPredicate:readAppPredicate object:readApp];
'' expection.expectationDescription = @"Waiting read app to become active";
'' XCTWaiterResult result = [XCTWaiter waitForExpectations:@[expection] timeout:5];
''
'' NSLog(@"wait result:%ld", (long)result);
''
'' XCUIElement *contentLabel = readApp.staticTexts[@"Using Testing New API To Do Multiple App UI Test"];
'' XCTAssert(contentLabel.exists);
'' }
Implements For UITesting
UITesting往往通過獲取到某個UI控件才能進行相關測試重抖,與voiceover相似露氮,需要根據控件顯示的內容、語義來查找钟沛,涉及生成快照畔规,快照解包和進程間的通訊等等。Xcode9之前恨统,采用的是快照(snapshots)技術來實現叁扫,在使用過程中,當當前屏幕下有大量控件時畜埋,例如有上千行的tableviewCell莫绣,從內存和耗費的時間兩個維度來看,耗費的時間和內存占用有些大悠鞍,甚至會有超時導致測試失敗对室、內存占用過大導致閃退的情況出現…于是就有了FirstMatch
First Match
First Match:就像字面上的意思一個,只要有一個匹配到了就會馬上return咖祭。
這樣的話掩宜,在寫查詢代碼的時候需要多考慮考慮怎么發(fā)揮FirstMatch的功能。
假設現在要查找navigationBar上的返回按鈕:
app.buttons.firstMatch
顯然不是一個好的寫法么翰,這樣得到的element可能不是你想要查詢的牺汤;
app.buttons[@"Done"].firstMatch
這樣寫好多了,縮小了范圍硬鞍,
而最好的寫法則是app.navigationBars.buttons[@"Done"].firstMatch
New Technologies
Activities
用于將散落的Testing語句整理在一個Group中
attachments
測試報告中可以包含更多的信息慧瘤,如:截屏
screenshots
新增XCUIScreenshotProviding
,遵循了這個協議的固该,即可調用screenshot
方法獲取屏幕截圖
'' - (void)testNewTechnoligies {
''
'' // 創(chuàng)建Launch Activity
'' [XCTContext runActivityNamed:@"Launch" block:^(id<XCTActivity> _Nonnull activity) {
'' XCUIApplication *app = [[XCUIApplication alloc] init];
'' app.launchArguments = @[@"-StartFromSlate", @"YES"];
'' [app launch];
'' }];
''
'' // 創(chuàng)建ScreenShots Activity
'' [XCTContext runActivityNamed:@"ScreenShots" block:^(id<XCTActivity> _Nonnull activity) {
'' // 生成主屏幕截圖
'' XCUIScreenshot *screenShot = [[XCUIScreen mainScreen] screenshot];
'' // 將截屏添加到附件中
'' XCTAttachment *attachment = [XCTAttachment attachmentWithScreenshot:screenShot];
'' // 確保測試成功后attachment不會被自動刪除, 這個同樣可以在Xcode的中設置
'' attachment.lifetime = XCTAttachmentLifetimeKeepAlways;
'' // attachment添加到activity中
'' [activity addAttachment:attachment];
'' }];
'' }
測試報告中锅减,生成了兩個activity,screenshots activity還包含一張屏幕截圖: