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

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

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

意见/建议吗?


当前回答

在我目前的公司,我们是这样做的:创建一个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

其他回答

此外,你可以只睡觉:

sleep(10)

因为uitest在另一个进程中运行,所以这是可行的。我不知道这有多明智,但确实有效。

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

let app = XCUIApplication()
app.launch()

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

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

Xcode测试

在我的情况下,睡眠会产生副作用,所以我使用等待

let _ = XCTWaiter.wait(for: [XCTestExpectation(description: "Hello World!")], timeout: 2.0)

睡眠会阻塞线程

线程阻塞时不发生运行循环处理。

你可以使用waitForExistence

let app = XCUIApplication()
app.launch()

if let label = app.staticTexts["Hello, world!"] {
label.waitForExistence(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测试的更多细节可以在我的博客上找到。