我正在尝试使用Xcode 7 beta 2中提供的新UI测试编写一个测试用例。该应用程序有一个登录屏幕,它调用服务器登录。由于这是一个异步操作,因此存在与此相关的延迟。
在进行进一步的步骤之前,是否有一种方法可以在XCTestCase中引起延迟或等待机制?
没有合适的文档,我浏览了类的头文件。找不到任何与此相关的信息。
意见/建议吗?
我正在尝试使用Xcode 7 beta 2中提供的新UI测试编写一个测试用例。该应用程序有一个登录屏幕,它调用服务器登录。由于这是一个异步操作,因此存在与此相关的延迟。
在进行进一步的步骤之前,是否有一种方法可以在XCTestCase中引起延迟或等待机制?
没有合适的文档,我浏览了类的头文件。找不到任何与此相关的信息。
意见/建议吗?
当前回答
let app = XCUIApplication()
app.launch()
//Find the button in the UI
let SettingsButton =
app.navigationBars["HomeView"].buttons["Settings"]
XCTAssertTrue(settingButton.waitForExistence(timeout: 10))
其他回答
基于@Ted的回答,我使用了这个扩展:
extension XCTestCase {
// Based on https://stackoverflow.com/a/33855219
func waitFor<T>(object: T, timeout: TimeInterval = 5, file: String = #file, line: UInt = #line, expectationPredicate: @escaping (T) -> Bool) {
let predicate = NSPredicate { obj, _ in
expectationPredicate(obj as! T)
}
expectation(for: predicate, evaluatedWith: object, handler: nil)
waitForExpectations(timeout: timeout) { error in
if (error != nil) {
let message = "Failed to fulful expectation block for \(object) after \(timeout) seconds."
let location = XCTSourceCodeLocation(filePath: file, lineNumber: line)
let issue = XCTIssue(type: .assertionFailure, compactDescription: message, detailedDescription: nil, sourceCodeContext: .init(location: location), associatedError: nil, attachments: [])
self.record(issue)
}
}
}
}
你可以这样用
let element = app.staticTexts["Name of your element"]
waitFor(object: element) { $0.exists }
它还允许等待元素消失或任何其他属性更改(通过使用适当的块)
waitFor(object: element) { !$0.exists } // Wait for it to disappear
下面的代码只适用于Objective C。
- (void)wait:(NSUInteger)interval {
XCTestExpectation *expectation = [self expectationWithDescription:@"wait"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(interval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:interval handler:nil];
}
只需调用这个函数,如下所示。
[self wait: 10];
这将创建一个延迟,而不会使线程进入睡眠状态或在超时时抛出错误:
let delayExpectation = XCTestExpectation()
delayExpectation.isInverted = true
wait(for: [delayExpectation], timeout: 5)
因为期望颠倒了,它会悄悄超时。
编辑:
我突然想到在Xcode 7b4中,UI测试现在有 expectationForPredicate: evaluatedWithObject:处理程序:
原:
另一种方法是在一段时间内旋转运行循环。只有当你知道你需要等待多长时间时才有用
Obj-C: [NSRunLoop currentRunLoop] runMode: nsdefaultrunopmode befordate:[NSDate datetimeintervalcenow: << <time wait in sec >>]
迅速: NSRunLoop.currentRunLoop().runMode(nsdefuultrunloopmode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>)))
如果您需要测试某些条件才能继续测试,那么这并不是非常有用。要运行条件检查,请使用while循环。
从Xcode 8.3开始,我们可以使用XCTWaiter http://masilotti.com/xctest-waiting/
func waitForElementToAppear(_ element: XCUIElement) -> Bool {
let predicate = NSPredicate(format: "exists == true")
let expectation = expectation(for: predicate, evaluatedWith: element,
handler: nil)
let result = XCTWaiter().wait(for: [expectation], timeout: 5)
return result == .completed
}
另一个技巧是写一个等待函数,感谢John Sundell给我演示了它
extension XCTestCase {
func wait(for duration: TimeInterval) {
let waitExpectation = expectation(description: "Waiting")
let when = DispatchTime.now() + duration
DispatchQueue.main.asyncAfter(deadline: when) {
waitExpectation.fulfill()
}
// We use a buffer here to avoid flakiness with Timer on CI
waitForExpectations(timeout: duration + 0.5)
}
}
然后像这样使用它
func testOpenLink() {
let delegate = UIApplication.shared.delegate as! AppDelegate
let route = RouteMock()
UIApplication.shared.open(linkUrl, options: [:], completionHandler: nil)
wait(for: 1)
XCTAssertNotNil(route.location)
}