我正在尝试使用Xcode 7 beta 2中提供的新UI测试编写一个测试用例。该应用程序有一个登录屏幕,它调用服务器登录。由于这是一个异步操作,因此存在与此相关的延迟。

在进行进一步的步骤之前,是否有一种方法可以在XCTestCase中引起延迟或等待机制?

没有合适的文档,我浏览了类的头文件。找不到任何与此相关的信息。

意见/建议吗?


当前回答

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];

斯威夫特

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)

or

let element = app.staticTexts["Name of your element"]
self.waitForElementToAppear(element, timeout: 10)

其他回答

这将创建一个延迟,而不会使线程进入睡眠状态或在超时时抛出错误:

let delayExpectation = XCTestExpectation()
delayExpectation.isInverted = true
wait(for: [delayExpectation], timeout: 5)

因为期望颠倒了,它会悄悄超时。

异步UI测试在Xcode 7 Beta 4中引入。要等待文本“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测试的更多细节可以在我的博客上找到。

根据XCUIElement的API, .exists可以用来检查查询是否存在,所以下面的语法在某些情况下是有用的!

let app = XCUIApplication()
app.launch()

let label = app.staticTexts["Hello, world!"]
while !label.exists {
    sleep(1)
}

如果你有信心你的期望最终会得到满足,你可以试着运行这个。应该注意的是,如果等待太长,崩溃可能是可取的,在这种情况下,应该使用@Joe Masilotti的帖子中的waitForExpectationsWithTimeout(_,handler:_)。

基于@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

编辑:

我突然想到在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循环。