我正在尝试使用Xcode 7 beta 2中提供的新UI测试编写测试用例。该应用程序有一个登录屏幕,在该屏幕上可以调用服务器进行登录。由于这是异步操作,因此存在延迟。
在进行进一步的步骤之前,是否有办法在XCTestCase中引起延迟或等待机制?
没有适当的文档,我浏览了这些类的Header文件。找不到与此相关的任何内容。
有什么想法/建议吗?
dispatch_after
,dispatch_queue
东西)
我正在尝试使用Xcode 7 beta 2中提供的新UI测试编写测试用例。该应用程序有一个登录屏幕,在该屏幕上可以调用服务器进行登录。由于这是异步操作,因此存在延迟。
在进行进一步的步骤之前,是否有办法在XCTestCase中引起延迟或等待机制?
没有适当的文档,我浏览了这些类的Header文件。找不到与此相关的任何内容。
有什么想法/建议吗?
dispatch_after
,dispatch_queue
东西)
Answers:
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)
waitForExpectationsWithTimeout
将自动使您的测试失败,这是非常不幸的。
app.launch()
似乎只是重新启动应用程序。有必要吗?
此外,您可以睡觉:
sleep(10)
由于UITest在另一个进程中运行,因此可以正常工作。我不知道这是多么可取,但可以。
usleep
iOS 11 / Xcode 9
<#yourElement#>.waitForExistence(timeout: 5)
这是该网站上所有自定义实现的绝佳替代品!
请确保在这里查看我的答案:https : //stackoverflow.com/a/48937714/971329。我在那里描述了一种等待请求的替代方法,它将大大减少测试的运行时间!
XCTestCase
,它就像一种魅力。我不明白为什么sleep(3)
在这里投票之类的方法如此之高,因为它人为地延长了测试时间,并且当您的测试套件增长时,实际上是没有选择的。
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之前
目标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];
迅速
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)
}
基于@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."
self.recordFailure(withDescription: message, inFile: file, atLine: line, expected: true)
}
}
}
}
你可以这样使用
let element = app.staticTexts["Name of your element"]
waitFor(object: element) { $0.exists }
它还允许等待元素消失,或等待其他任何属性更改(通过使用适当的块)
waitFor(object: element) { !$0.exists } // Wait for it to disappear
编辑:
实际上,我刚想到在Xcode 7b4中,UI测试现在具有
expectationForPredicate:evaluatedWithObject:handler:
原版的:
另一种方法是将运行循环旋转一定的时间。仅当您知道需要等待多少(估计)时间时,它才有用
对象:
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow: <<time to wait in seconds>>]]
迅速:
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>))
如果您需要测试某些条件以继续测试,则此功能不是超级有用。要运行条件检查,请使用while
循环。
以下代码仅适用于ObjectiveC。
- (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];
根据XCUIElement的API,.exists
可用于检查查询是否存在,因此以下语法在某些情况下可能有用!
let app = XCUIApplication()
app.launch()
let label = app.staticTexts["Hello, world!"]
while !label.exists {
sleep(1)
}
如果您有信心最终可以实现期望,则可以尝试运行此程序。应该注意的是,如果等待时间太长,最好使用崩溃,在这种情况下waitForExpectationsWithTimeout(_,handler:_)
,应使用@Joe Masilotti的帖子。
睡眠会阻塞线程
“在线程被阻塞时,不会发生运行循环处理。”
您可以使用waitForExistence
let app = XCUIApplication()
app.launch()
if let label = app.staticTexts["Hello, world!"] {
label.waitForExistence(timeout: 5)
}
NSThread.sleepForTimeInterval(1)
应该有效