简体   繁体   English

在 XCUITests 中,如何等待两个 ui 元素中的任何一个存在

[英]In XCUITests, how to wait for existence of either of two ui elements

Looking at XCTWaiter().wait(...) I believe we can wait for multiple expectations to become true using this code看看 XCTWaiter().wait(...) 我相信我们可以使用这段代码等待多个期望成为现实

let notHittablePredicate = NSPredicate(format: "hittable == false")
let myExpectation = XCTNSPredicateExpectation(predicate: notHittablePredicate, object: element)
let result = XCTWaiter().wait(for: [myExpectation], timeout: timeout)
//for takes array of expectations

But this uses like AND among the supplied expectations.但这在提供的期望中使用 AND 之类的。 Is there a way to do OR among the supplied expectations.有没有办法在提供的期望中做 OR。

Like i have a use case at login that after tapping submit, i want to wait for one of two elements.就像我在登录时有一个用例一样,在点击提交后,我想等待两个元素之一。 First element is "You are already logged in on another device. If you continue any unsaved data on your other device will be lost?".第一个元素是“您已经在另一台设备上登录。如果继续,您另一台设备上的任何未保存数据将丢失?”。 And second element is the main screen after login.第二个元素是登录后的主屏幕。 So any one can appear.所以任何人都可以出现。 Currently I'm first waiting for first element until timeout occurs and then for the second element.目前我首先等待第一个元素,直到超时发生,然后等待第二个元素。 But I want to optimize time here and move on as soon as any of two elements exist==true.但是我想在这里优化时间并在两个元素中的任何一个存在时继续前进==真。 Then i'll check if element1 exists then tap YES and then wait for main screen otherwise just assert existence of element2.然后我将检查 element1 是否存在,然后点击 YES,然后等待主屏幕,否则只需断言 element2 的存在。

Please comment if something isn't clear in the question.如果问题中有不清楚的地方,请发表评论。 Thanks谢谢

Inspired by http://masilotti.com/ui-testing-tdd/ , you don't have to rely on XCTWaiter .http://masilotti.com/ui-testing-tdd/ 的启发,您不必依赖XCTWaiter You can simply run a loop and test whether one of them exists.您可以简单地运行一个循环并测试其中一个是否存在。

/// Waits for either of the two elements to exist (i.e. for scenarios where you might have
/// conditional UI logic and aren't sure which will show)
///
/// - Parameters:
///   - elementA: The first element to check for
///   - elementB: The second, or fallback, element to check for
/// - Returns: the element that existed
@discardableResult
func waitForEitherElementToExist(_ elementA: XCUIElement, _ elementB: XCUIElement) -> XCUIElement? {
    let startTime = NSDate.timeIntervalSinceReferenceDate
    while (!elementA.exists && !elementB.exists) { // while neither element exists
        if (NSDate.timeIntervalSinceReferenceDate - startTime > 5.0) {
            XCTFail("Timed out waiting for either element to exist.")
            break
        }
        sleep(1)
    }

    if elementA.exists { return elementA }
    if elementB.exists { return elementB }
    return nil
}

then you could just do:那么你可以这样做:

let foundElement = waitForEitherElementToExist(elementA, elementB)
if foundElement == elementA {
    // e.g. if it's a button, tap it
} else {
    // element B was found
}

lagoman's answer is absolutely correct and great.拉戈曼的回答是绝对正确和伟大的。 I needed wait on more than 2 possible elements though, so I tweaked his code to support an Array of XCUIElement instead of just two.不过,我需要等待超过 2 个可能的元素,所以我调整了他的代码以支持XCUIElement Array而不是两个。

@discardableResult
func waitForAnyElement(_ elements: [XCUIElement], timeout: TimeInterval) -> XCUIElement? {
    var returnValue: XCUIElement?
    let startTime = Date()
    
    while Date().timeIntervalSince(startTime) < timeout {
        if let elementFound = elements.first(where: { $0.exists }) {
            returnValue = elementFound
            break
        }
        sleep(1)
    }
    return returnValue
}

which can be used like可以像这样使用

let element1 = app.tabBars.buttons["Home"]
let element2 = app.buttons["Submit"]
let element3 = app.staticTexts["Greetings"]
foundElement = waitForAnyElement([element1, element2, element3], timeout: 5)

// do whatever checks you may want
if foundElement == element1 {
     // code
}

NSPredicate supports OR predicates too. NSPredicate支持 OR 谓词。

For example I wrote something like this to ensure my application is fully finished launching before I start trying to interact with it in UI tests.例如,我写了这样的东西,以确保我的应用程序在我开始尝试在 UI 测试中与其交互之前完全完成启动。 This is checking for the existence of various landmarks in the app that I know are uniquely present on each of the possible starting states after launch.这是检查应用程序中是否存在各种地标,我知道这些地标在启动后的每个可能的起始状态中都是唯一存在的。

extension XCTestCase {
  func waitForLaunchToFinish(app: XCUIApplication) {
    let loginScreenPredicate = NSPredicate { _, _ in
      app.logInButton.exists
    }

    let tabBarPredicate = NSPredicate { _, _ in
      app.tabBar.exists
    }

    let helpButtonPredicate = NSPredicate { _, _ in
      app.helpButton.exists
    }

    let predicate = NSCompoundPredicate(
      orPredicateWithSubpredicates: [
        loginScreenPredicate,
        tabBarPredicate,
        helpButtonPredicate,
      ]
    )

    let finishedLaunchingExpectation = expectation(for: predicate, evaluatedWith: nil, handler: nil)
    wait(for: [finishedLaunchingExpectation], timeout: 30)
  }
}

In the console while the test is running there's a series of repeated checks for the existence of the various buttons I want to check for, with a variable amount of time between each check.在测试运行时的控制台中,有一系列重复检查我想要检查的各种按钮是否存在,每次检查之间的时间可变。

t = 13.76s Wait for com.myapp.name to idle t = 13.76s 等待 com.myapp.name 空闲

t = 18.15s Checking existence of "My Tab Bar" Button t = 18.15s 检查“我的标签栏”按钮是否存在

t = 18.88s Checking existence of "Help" Button t = 18.88s 检查“帮助”按钮是否存在

t = 20.98s Checking existence of "Log In" Button t = 20.98s 检查“登录”按钮是否存在

t = 22.99s Checking existence of "My Tab Bar" Button t = 22.99s 检查“我的标签栏”按钮是否存在

t = 23.39s Checking existence of "Help" Button t = 23.39s 检查“帮助”按钮是否存在

t = 26.05s Checking existence of "Log In" Button t = 26.05s 检查“登录”按钮是否存在

t = 32.51s Checking existence of "My Tab Bar" Button t = 32.51s 检查“我的标签栏”按钮是否存在

t = 16.49s Checking existence of "Log In" Button t = 16.49s 检查“登录”按钮是否存在

And voila, now instead of waiting for each element individually I can do it concurrently.瞧,现在我可以同时进行,而不是单独等待每个元素。

This is very flexible of course, since you can add as many elements as you want, with whatever conditions you want.这当然非常灵活,因为您可以根据需要添加任意数量的元素,无论您想要什么条件。 And if you want a combination of OR and AND predicates you can do that too with NSCompoundPredicate .如果你想要 OR 和 AND 谓词的组合,你也可以用NSCompoundPredicate做到这NSCompoundPredicate This can easily be adapted into a more generic function that accepts an array of elements like so:这可以很容易地适应一个更通用的函数,它接受像这样的元素数组:

func wait(for elements: XCUIElement...) { … }

Could even pass a parameter that controls whether it uses OR or AND.甚至可以传递一个参数来控制它是使用 OR 还是 AND。

Hey other alternative that works for us.嘿,其他对我们有用的替代方案。 I hope help others too.我也希望能帮助别人。

 XCTAssert(
            app.staticTexts["Hello Stack"]
                .waitForExistence(timeout: 10) || app.staticTexts["Hi Stack"]
                .waitForExistence(timeout: 10)
        )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在XCUITests中,如何等待多个ui元素的存在? - In XCUITests, how to wait for existence of several ui elements? iOS Swift UI 测试 (XCUITests) 稳定性问题 - 快速通道扫描 - iOS Swift UI Tests (XCUITests) stability problem - fastlane scan 有没有办法组合两个UI元素? - Is there a way to combine two UI elements? 如何将两个 UI 元素约束到同一个地方 - Swift iOS - How to Constraint Two UI Elements to same place - Swift iOS 如何不冻结UI,并等待响应? - How to not freeze the UI, and wait for response? 如何快速等待ui委托 - how to wait for ui delegate in swift (iOS)如何在两个ViewController之间的过渡中为UI元素(例如标签和图像)的过渡设置动画? - (iOS) How can i animate the traslation of UI elements, like labels and images in the transition between two viewcontrollers? 如何通过Autolayout的视觉格式语言垂直堆叠两个UI元素 - How to vertically stack two UI elements via Autolayout's visual format language 使用ReactiveCocoa进行循环绑定,如何将一个值绑定到两个输入UI元素 - Cyclic Bindings with ReactiveCocoa, How to bind one value to two input UI elements 有没有办法在两个 viewController 之间共享 UI 元素? - There's a way to share UI elements between two viewControllers?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM