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

时间:2017-12-19 05:15:26

标签: swift xcode xcode9 xcode-ui-testing xcuitest

查看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

但是这在所提供的期望中使用了AND。有没有办法在提供的期望中做OR。

就像我在登录时有一个用例,在点击提交后,我想等待两个元素中的一个。第一个元素是“您已经登录到另一台设备。如果您继续在其他设备上保存任何未保存的数据,将会丢失?”。第二个元素是登录后的主屏幕。所以任何人都可以出现。目前我首先等待第一个元素,直到超时,然后是第二个元素。但我希望在这里优化时间,并在两个元素中的任何一个存在时继续前进== true。然后我将检查element1是否存在,然后点击YES然后等待主屏幕,否则只断言element2的存在。

如果问题不明确,请发表评论。谢谢

4 个答案:

答案 0 :(得分:3)

http://masilotti.com/ui-testing-tdd/的启发,您不必依赖XCTWaiter。您可以简单地运行一个循环并测试其中一个是否存在。

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

然后你可以这样做:

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

答案 1 :(得分:0)

拉各曼的答案是绝对正确和伟大的。不过,我需要等待2个以上的可能元素,因此我调整了他的代码以支持Array中的XCUIElement而不是两个。

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

可以像

一样使用
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
}

答案 2 :(得分:0)

NSPredicate 也支持 OR 谓词。

例如,我写了这样的东西,以确保我的应用程序在我开始尝试在 UI 测试中与其交互之前完全完成启动。这是在检查应用中是否存在各种标志,我知道这些标志在启动后的每个可能的启动状态中都是独一无二的。

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

在测试运行时,在控制台中,有一系列重复检查我想要检查的各种按钮是否存在,每次检查之间的时间可变。

<块引用>

t = 13.76s 等待 com.myapp.name 空闲

t = 18.15s 检查“我的标签栏”按钮是否存在

t = 18.88s 检查“帮助”按钮是否存在

t = 20.98s 检查“登录”按钮是否存在

t = 22.99s 检查“我的标签栏”按钮是否存在

t = 23.39s 检查“帮助”按钮是否存在

t = 26.05s 检查“登录”按钮是否存在

t = 32.51s 检查“我的标签栏”按钮是否存在

t = 16.49s 检查“登录”按钮是否存在

瞧,现在我可以同时进行,而不是单独等待每个元素。

这当然非常灵活,因为您可以根据需要添加任意数量的元素,无论您想要什么条件。如果您想要组合 OR 和 AND 谓词,您也可以使用 NSCompoundPredicate 来实现。这可以很容易地改编成一个更通用的函数,它接受像这样的元素数组:

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

甚至可以传递一个参数来控制它是使用 OR 还是 AND。

答案 3 :(得分:0)

嘿,其他对我们有用的替代方法。我也希望能帮助到其他人。

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