我正在尝试使用Xcode 7 beta 2中提供的新UI测试编写测试用例。该应用程序具有登录屏幕,其中它调用服务器进行登录。由于它是异步操作,因此会出现延迟。
在继续执行后续步骤之前,是否有一种方法可以在XCTestCase中引入延迟或等待机制?
目前没有适当的文档可用,并且我已经查看了类的头文件。未能找到与此相关的任何内容。
有什么想法/建议吗?
我正在尝试使用Xcode 7 beta 2中提供的新UI测试编写测试用例。该应用程序具有登录屏幕,其中它调用服务器进行登录。由于它是异步操作,因此会出现延迟。
在继续执行后续步骤之前,是否有一种方法可以在XCTestCase中引入延迟或等待机制?
目前没有适当的文档可用,并且我已经查看了类的头文件。未能找到与此相关的任何内容。
有什么想法/建议吗?
此外,您可以仅仅睡觉:
sleep(10)
由于UITests在另一个进程中运行,所以这个方法可行。我不知道它是否可取,但它有效。
usleep
。 - mxcl在 Xcode 7 Beta 4 中引入了异步 UI 测试。要等待一个文本为“Hello, world!”的标签出现,您可以执行以下操作:
let app = XCUIApplication()
app.launch()
let label = app.staticTexts["Hello, world!"]
let exists = NSPredicate(format: "exists == 1")
expectationForPredicate(exists, evaluatedWithObject: label, handler: nil)
waitForExpectationsWithTimeout(5, handler: nil)
关于UI测试的更多细节可以在我的博客上找到。
waitForExpectationsWithTimeout
会自动使您的测试失败,这非常不幸。 - JedidjaiOS 11 / Xcode 9
<#yourElement#>.waitForExistence(timeout: 5)
这是对该网站所有自定义实现的一个很好的替代品!
一定要看看我在这里的回答:https://dev59.com/WKnka4cB1Zd3GeqPLkgh#48937714。在那里,我描述了一种等待请求的替代方案,可以大大减少测试运行的时间!
Xcode 9 引入了使用 XCTWaiter 进行测试的新技巧。
测试用例显式等待。
wait(for: [documentExpectation], timeout: 10)
服务员实例委托给测试
XCTWaiter(delegate: self).wait(for: [documentExpectation], timeout: 10)
服务员类返回结果
let result = XCTWaiter.wait(for: [documentExpectation], timeout: 10)
switch(result) {
case .completed:
//all expectations were fulfilled before timeout!
case .timedOut:
//timed out before all of its expectations were fulfilled
case .incorrectOrder:
//expectations were not fulfilled in the required order
case .invertedFulfillment:
//an inverted expectation was fulfilled
case .interrupted:
//waiter was interrupted before completed or timedOut
}
Xcode 9之前
Objective C
- (void)waitForElementToAppear:(XCUIElement *)element withTimeout:(NSTimeInterval)timeout
{
NSUInteger line = __LINE__;
NSString *file = [NSString stringWithUTF8String:__FILE__];
NSPredicate *existsPredicate = [NSPredicate predicateWithFormat:@"exists == true"];
[self expectationForPredicate:existsPredicate evaluatedWithObject:element handler:nil];
[self waitForExpectationsWithTimeout:timeout handler:^(NSError * _Nullable error) {
if (error != nil) {
NSString *message = [NSString stringWithFormat:@"Failed to find %@ after %f seconds",element,timeout];
[self recordFailureWithDescription:message inFile:file atLine:line expected:YES];
}
}];
}
使用方法
XCUIElement *element = app.staticTexts["Name of your element"];
[self waitForElementToAppear:element withTimeout:5];
Swift
func waitForElementToAppear(element: XCUIElement, timeout: NSTimeInterval = 5, file: String = #file, line: UInt = #line) {
let existsPredicate = NSPredicate(format: "exists == true")
expectationForPredicate(existsPredicate,
evaluatedWithObject: element, handler: nil)
waitForExpectationsWithTimeout(timeout) { (error) -> Void in
if (error != nil) {
let message = "Failed to find \(element) after \(timeout) seconds."
self.recordFailureWithDescription(message, inFile: file, atLine: line, expected: true)
}
}
}
使用方法
let element = app.staticTexts["Name of your element"]
self.waitForElementToAppear(element)
或者let element = app.staticTexts["Name of your element"]
self.waitForElementToAppear(element, timeout: 10)
自 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
}
另一个技巧是编写一个wait
函数,感谢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)
}
这将在不使线程进入睡眠状态或在超时时引发错误的情况下创建延迟:
let delayExpectation = XCTestExpectation()
delayExpectation.isInverted = true
wait(for: [delayExpectation], timeout: 5)
由于预期被反转,它将会静默超时。
根据@Ted的回答,我已经使用了这个扩展程序:
extension XCTestCase {
// Based on https://dev59.com/BV0Z5IYBdhLWcg3wrR8r#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
Xcode测试等待
在我的情况下,sleep
会产生副作用,所以我使用了 wait
let _ = XCTWaiter.wait(for: [XCTestExpectation(description: "Hello World!")], timeout: 2.0)
编辑:
在 Xcode 7b4 中,UI测试现在具有expectationForPredicate:evaluatedWithObject:handler:
方法。
原始内容:
另一种方法是旋转运行循环一段时间。只有在您知道需要等待多长时间时才真正有用。
Obj-C:
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:<<time to wait in seconds>>]]
Swift:
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>))
如果需要测试一些条件以便继续测试,则此方法并不是特别有用。要运行条件检查,请使用while
循环。
@discardableResult
func wait(
until expression: @escaping (XCUIElement) -> Bool,
timeout: TimeInterval = 15,
message: @autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line
) -> Self {
if expression(self) {
return self
}
let predicate = NSPredicate { _, _ in
expression(self)
}
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: nil)
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
if result != .completed {
XCTFail(
message().isEmpty ? "expectation not matched after waiting" : message(),
file: file,
line: line
)
}
return self
}
用法
app.buttons["my_button"].wait(until: { $0.exists })
app.buttons["my_button"].wait(until: { $0.isHittable })
然后我们将其包装在一个方法中,其中关键路径和匹配
值形成表达式。
@discardableResult
func wait<Value: Equatable>(
until keyPath: KeyPath<XCUIElement, Value>,
matches match: Value,
timeout: TimeInterval = 15,
message: @autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line
) -> Self {
wait(
until: { $0[keyPath: keyPath] == match },
timeout: timeout,
message: message,
file: file,
line: line
)
}
使用方法
app.buttons["my_button"].wait(until: \.exists, matches: true)
app.buttons["my_button"].wait(until: \.isHittable, matches: false)
那么你可以包装该方法,在其中match
的值始终为true
,适用于我发现最常见的用例。
用法
app.buttons["my_button"].wait(until: \.exists)
app.buttons["my_button"].wait(until: \.isHittable)
我写了一篇关于这个的文章,你可以在那里找到完整的扩展文件:https://sourcediving.com/clean-waiting-in-xcuitest-43bab495230f
NSThread.sleepForTimeInterval(1)
应该有效。 - Kametrixom