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

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

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

意见/建议吗?


当前回答

此外,你可以只睡觉:

sleep(10)

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

其他回答

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

   let app = XCUIApplication()
    app.launch()

     //Find the button in the UI 
    let SettingsButton =
        app.navigationBars["HomeView"].buttons["Settings"]
    XCTAssertTrue(settingButton.waitForExistence(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
}

另一个技巧是写一个等待函数,感谢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)
}

iOS 11 / Xcode 9

<#yourElement#>.waitForExistence(timeout: 5)

这是一个伟大的替代所有自定义实现在这个网站!

一定要看看我的答案:https://stackoverflow.com/a/48937714/971329。在这里,我描述了一种替代等待请求的方法,它将大大减少测试运行的时间!

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

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

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