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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31182637/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 06:41:14  来源:igfitidea点击:

Delay/Wait in a test case of Xcode UI testing

iosios9xcode-ui-testingxcode7-beta2xctwaiter

提问by Tejas HS

I am trying to write a test case using the new UI Testing available in Xcode 7 beta 2. The App has a login screen where it makes a call to the server to login. There is a delay associated with this as it is an asynchronous operation.

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

Is there a way to cause a delay or wait mechanism in the XCTestCase before proceeding to further steps?

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

There is no proper documentation available and I went through the Header files of the classes. Was not able to find anything related to this.

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

Any ideas/suggestions?

任何想法/建议?

采纳答案by Joe Masilotti

Asynchronous UI Testing was introduced in Xcode 7 Beta 4. To wait for a label with the text "Hello, world!" to appear you can do the following:

异步 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)

More details about UI Testingcan be found on my blog.

更多关于 UI 测试的细节可以在我的博客上找到。

回答by mxcl

Additionally, you can just sleep:

此外,您可以睡觉:

sleep(10)

Since the UITests run in another process, this works. I don't know how advisable it is, but it works.

因为 UITests 在另一个进程中运行,所以这是有效的。我不知道它有多可取,但它有效。

回答by Ted

Xcode 9introduced new tricks with XCTWaiter

Xcode 9引入了XCTWaiter 的新技巧

Test case waits explicitly

测试用例显式等待

wait(for: [documentExpectation], timeout: 10)

Waiter instance delegates to test

Waiter 实例委托进行测试

XCTWaiter(delegate: self).wait(for: [documentExpectation], timeout: 10)

Waiter class returns result

服务员类返回结果

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
}

sample usage

示例用法

Before Xcode 9

在 Xcode 9 之前

Objective C

目标 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];
        }
    }];
}

USAGE

用法

XCUIElement *element = app.staticTexts["Name of your element"];
[self waitForElementToAppear:element withTimeout:5];

Swift

迅速

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

USAGE

用法

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)

SOURCE

来源

回答by blackjacx

iOS 11 / Xcode 9

iOS 11 / Xcode 9

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

This is a great replacement for all the custom implementations on this site!

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

Be sure to have a look at my answer here: https://stackoverflow.com/a/48937714/971329. There I describe an alternative to waiting for requests which will greatly reduce the time your tests are running!

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

回答by onmyway133

As of Xcode 8.3, we can use XCTWaiterhttp://masilotti.com/xctest-waiting/

从 Xcode 8.3 开始,我们可以使用XCTWaiterhttp://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
}

Another trick is to write a waitfunction, credit goes to John Sundell for showing it to me

另一个技巧是编写一个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)
  }
}

and use it like

并像使用它一样

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

回答by Ben Lings

Based on @Ted's answer, I've used this extension:

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

}

You can use it like this

你可以像这样使用它

let element = app.staticTexts["Name of your element"]
waitFor(object: element) { 
waitFor(object: element) { !
- (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];
}
.exists } // Wait for it to disappear
.exists }

It also allows for waiting for an element to disappear, or any other property to change (by using the appropriate block)

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

[self wait: 10];

回答by enmiller

Edit:

编辑:

It actually just occurred to me that in Xcode 7b4, UI testing now has expectationForPredicate:evaluatedWithObject:handler:

实际上我突然想到在 Xcode 7b4 中,UI 测试现在有 expectationForPredicate:evaluatedWithObject:handler:

Original:

原来的:

Another way is to spin the run loop for a set amount of time. Really only useful if you know how much (estimated) time you'll need to wait for

另一种方法是将运行循环旋转一段时间。只有当您知道需要等待多少(估计)时间时才真正有用

Obj-C: [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow: <<time to wait in seconds>>]]

对象-C: [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow: <<time to wait in seconds>>]]

Swift: NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>))

迅速: NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: <<time to wait in seconds>>))

This is not super useful if you need to test some conditions in order to continue your test. To run conditional checks, use a whileloop.

如果您需要测试某些条件以继续测试,这不是很有用。要运行条件检查,请使用while循环。

回答by arango_86

The following code just works with Objective C.

以下代码仅适用于 Objective C。

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

Just make call to this function as given below.

只需调用此函数,如下所示。

let app = XCUIApplication()
app.launch()

if let label = app.staticTexts["Hello, world!"] {
label.waitForExistence(timeout: 5)
}

回答by yoAlex5

In my case sleepcreated side effect so I used wait

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

##代码##

回答by zdravko zdravkin

sleep will block the thread

sleep 会阻塞线程

"No run loop processing occurs while the thread is blocked."

“当线程被阻塞时,不会发生运行循环处理。”

you can use waitForExistence

你可以使用 waitForExistence

##代码##