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

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

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

意见/建议吗?


当前回答

下面的代码只适用于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循环。

在我目前的公司,我们是这样做的:创建一个XCUIElement表达式期望(以创建一个通用的等待方法)。我们以以下方式来确保它是可维护的(有很多不同的期望,并且不想为此创建很多方法/特定的谓词。

斯威夫特5

基本方法

该表达式用于形成一个动态谓词值。我们可以从谓词创建xctnspredicateexpect,然后将其传递给XCTWaiter以显式等待。如果结果不是已完成,则失败并返回一条可选消息。

@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 })

Keypaths

然后将其包装在一个方法中,其中keyPath和match值构成表达式。

@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)

然后可以包装该方法,其中匹配值对于我发现的最常见的用例始终为真。

使用

app.buttons["my_button"].wait(until: \.exists)
app.buttons["my_button"].wait(until: \.isHittable)

我写了一篇关于它的文章,并获得了完整的扩展文件:https://sourcediving.com/clean-waiting-in-xcuitest-43bab495230f

异步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测试的更多细节可以在我的博客上找到。

下面的代码只适用于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];