在Xcode UI测试的测试用例中延迟/等待

Tej*_* HS 172 ios ios9 xcode7-beta2 xcode-ui-testing xctwaiter

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

在继续进一步的步骤之前,有没有办法在XCTestCase中引起延迟或等待机制?

没有适当的文档可用,我浏览了类的Header文件.无法找到与此相关的任何内容.

有什么想法/建议吗?

mxc*_*xcl 216

另外,你可以睡觉:

sleep(10)
Run Code Online (Sandbox Code Playgroud)

由于UITests在另一个进程中运行,因此可行.我不知道它是多么可取,但它确实有效.

  • 我见过的最好的答案:)我会加+ 100票如果我能:) (13认同)
  • 我喜欢NSThread.sleepForTimeInterval(0.2),因为您可以指定亚秒延迟.(sleep()接受一个整数参数;只有一秒的倍数是可能的). (7认同)
  • @GrahamPerks,是的,虽然还有:'usleep` (5认同)
  • 有一段时间我们需要延迟的方式,不想让它失败!谢谢 (2认同)
  • 我希望有一个更好的答案,但如果你不想导致失败,这似乎是现在唯一的方法. (2认同)
  • 即使使用Joe Masilotti的解决方案,我仍然有一些奇怪的失败 - 我怀疑XCode/UITest错误 - 在尝试测试webView时.添加一些sleep()调用修复了这个问题. (2认同)
  • 这确实是一个糟糕的建议。如果你有几个,你的集成测试将永远持续下去。 (2认同)
  • 这不是一个糟糕的建议(您不了解UITesting的工作原理),但是即使这是一个糟糕的建议,有时也无法制定出有效的期望(系统会警告任何人?),所以这就是您的全部。 (2认同)

Joe*_*tti 159

在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)
Run Code Online (Sandbox Code Playgroud)

有关UI测试的更多详细信息,请访问我的博客.

  • 不幸的是,没有办法接受超时发生并继续前进 - "waitForExpectationsWithTimeout"将自动失败你的测试,这是非常不幸的. (18认同)

Ted*_*Ted 72

Xcode 9 引入了XCTWaiter的新技巧

测试用例明确等待

wait(for: [documentExpectation], timeout: 10)
Run Code Online (Sandbox Code Playgroud)

服务员实例委托测试

XCTWaiter(delegate: self).wait(for: [documentExpectation], timeout: 10)
Run Code Online (Sandbox Code Playgroud)

Waiter类返回结果

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
}
Run Code Online (Sandbox Code Playgroud)

样品用法

在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];
        }
    }];
}
Run Code Online (Sandbox Code Playgroud)

用法

XCUIElement *element = app.staticTexts["Name of your element"];
[self waitForElementToAppear:element withTimeout:5];
Run Code Online (Sandbox Code Playgroud)

迅速

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)
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

用法

let element = app.staticTexts["Name of your element"]
self.waitForElementToAppear(element)
Run Code Online (Sandbox Code Playgroud)

要么

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

资源


bla*_*acx 69

iOS 11/Xcode 9

<#yourElement#>.waitForExistence(timeout: 5)
Run Code Online (Sandbox Code Playgroud)

这是此站点上所有自定义实现的绝佳替代品!

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

  • 是的,这仍然是我在使用 `XCTestCase` 时所采用的方法,它的作用就像一个魅力。我不明白为什么像 `sleep(3)` 这样的方法在这里投票如此之高,因为它人为地延长了测试时间,并且当你的测试套件增长时真的没有选择。 (2认同)
  • 我的答案得到如此高的评价是因为我九年前写的,当时它是唯一的选择。在某些情况下它仍然是唯一的选择。您并不总是有一个元素需要等待其存在。我给你投了赞成票,希望这能让你的脾气冷静一点。 (2认同)

onm*_*133 28

从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
}
Run Code Online (Sandbox Code Playgroud)

另一个技巧是编写一个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)
  }
}
Run Code Online (Sandbox Code Playgroud)

并使用它

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)
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*ngs 10

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

}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它

let element = app.staticTexts["Name of your element"]
waitFor(object: element) { $0.exists }
Run Code Online (Sandbox Code Playgroud)

它还允许等待元素消失,或者任何其他属性改变(通过使用适当的块)

waitFor(object: element) { !$0.exists } // Wait for it to disappear
Run Code Online (Sandbox Code Playgroud)


enm*_*ler 9

编辑:

实际上我刚刚想到,在Xcode 7b4中,UI测试现在已经有了 expectationForPredicate:evaluatedWithObject:handler:

原版的:

另一种方法是将运行循环旋转一段时间.如果您知道需要等待多长时间(估计),那么它真的很有用

OBJ-C: [[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循环.


Rya*_*yan 9

我们在当前公司的做法是创建一个 XCUIElement 表达式期望(以创建通用的等待方法)。我们按照以下方式进行操作,以确保它是可维护的(有很多期望变化,并且不想创建大量方法/特定谓词来做到这一点。

雨燕5

基本方法

该表达式用于形成动态谓词值。我们可以XCTNSPredicateExpectation从谓词创建 's,然后将其传递XCTWaiter给显式等待。如果结果不是completed,那么我们会失败并显示一条可选消息。

@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
}
Run Code Online (Sandbox Code Playgroud)

用法

app.buttons["my_button"].wait(until: { $0.exists })
app.buttons["my_button"].wait(until: { $0.isHittable })
Run Code Online (Sandbox Code Playgroud)

键路径

然后我们将其包装在一个方法中,其中 keyPath 和matchvalue 形成表达式。

@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
    )
}
Run Code Online (Sandbox Code Playgroud)

用法

app.buttons["my_button"].wait(until: \.exists, matches: true)
app.buttons["my_button"].wait(until: \.isHittable, matches: false)
Run Code Online (Sandbox Code Playgroud)

然后您可以包装该方法,其中该match值始终true适用于我发现最常见的用例。

用法

app.buttons["my_button"].wait(until: \.exists)
app.buttons["my_button"].wait(until: \.isHittable)
Run Code Online (Sandbox Code Playgroud)

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


小智 8

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

let delayExpectation = XCTestExpectation()
delayExpectation.isInverted = true
wait(for: [delayExpectation], timeout: 5)
Run Code Online (Sandbox Code Playgroud)

因为期望被反转,它会安静地超时。


yoA*_*ex5 6

在我的情况下sleep产生了副作用,所以我使用了wait

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