UITesting Xcode 7:如何判断XCUIElement是否可见?

UITesting Xcode 7: How to tell if XCUIElement is visible?

我正在使用 UI 测试在 Xcode 7 中自动化应用程序。我有一个带有 XCUI 元素(包括按钮等)的滚动视图。有时 XCUI 元素是可见的,有时它们在滚动视图的上方或下方隐藏得太远(取决于我在滚动视图中的位置)。

有没有办法将项目滚动到视图中或者判断它们是否可见?

谢谢

看起来这是一个已知错误:-(

https://forums.developer.apple.com/thread/9934

我必须做的是在我的 UI 测试代码中实际向上或向下滑动来解决这个问题。你试过这个吗?

XCUIApplication().swipeUp()

或者您也可以执行 WhateverUIElement.swipUp(),它会相对于该元素滚动 up/down。

希望他们能修复自动滚动或自动查找功能,这样我们就不必手动执行此操作。

遗憾的是,Apple 未在 XCUIElement 上提供任何 scrollTo 方法或 .visible 参数。也就是说,您可以添加几个辅助方法来实现其中的一些功能。这是我在 Swift.

中的做法

首先检查元素是否可见:

func elementIsWithinWindow(element: XCUIElement) -> Bool {
    guard element.exists && !CGRectIsEmpty(element.frame) && element.hittable else { return false }
    return CGRectContainsRect(XCUIApplication().windows.elementBoundByIndex(0).frame, element.frame)
}

不幸的是 .exists returns 如果元素已加载但不在屏幕上则为真。此外,我们必须检查目标元素是否有一个比 0 大 0 的框架(有时也是如此)——然后我们可以检查这个框架是否在主 window.

然后我们需要一种方法来向上或向下滚动可控量:

func scrollDown(times: Int = 1) {
    let topScreenPoint = app.mainWindow().coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.05))
    let bottomScreenPoint = app.mainWindow().coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.90))
    for _ in 0..<times {
        bottomScreenPoint.pressForDuration(0, thenDragToCoordinate: topScreenPoint)
    }
}

func scrollUp(times: Int = 1) {
    let topScreenPoint = app.mainWindow().coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.05))
    let bottomScreenPoint = app.mainWindow().coordinateWithNormalizedOffset(CGVector(dx: 0.5, dy: 0.90))
    for _ in 0..<times {
        topScreenPoint.pressForDuration(0, thenDragToCoordinate: bottomScreenPoint)
    }
}  

更改 topScreenPoint 和 bottomScreenPoint 的 CGVector 值将更改滚动操作的比例 - 请注意,如果您离屏幕边缘太近,您将拉出 OS 菜单之一。

使用这两种方法,您可以编写一个循环,以一种方式滚动到给定阈值,直到元素变得可见,然后如果它找不到目标,则以另一种方式滚动:

func scrollUntilElementAppears(element: XCUIElement, threshold: Int = 10) {
    var iteration = 0

    while !elementIsWithinWindow(element) {
        guard iteration < threshold else { break }
        scrollDown()
        iteration++
    }

    if !elementIsWithinWindow(element) { scrollDown(threshold) }

    while !elementIsWithinWindow(element) {
        guard iteration > 0 else { break }
        scrollUp()
        iteration--
    }
}

最后一种方法效率不高,但至少可以让您找到屏幕外的元素。当然,如果您知道您的目标元素在给定测试中总是会高于或低于您的起点,您可以只编写 scrollDownUntilscrollUpUntill 方法,而无需此处的阈值逻辑。 希望这对您有所帮助!

Swift 5 次更新

func elementIsWithinWindow(element: XCUIElement) -> Bool {
    guard element.exists && !element.frame.isEmpty && element.isHittable else { return false }
    return XCUIApplication().windows.element(boundBy: 0).frame.contains(element.frame)
}

func scrollDown(times: Int = 1) {
    let mainWindow = app.windows.firstMatch
    let topScreenPoint = mainWindow.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.05))
    let bottomScreenPoint = mainWindow.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.90))
    for _ in 0..<times {
        bottomScreenPoint.press(forDuration: 0, thenDragTo: topScreenPoint)
    }
}

func scrollUp(times: Int = 1) {
    let mainWindow = app.windows.firstMatch
    let topScreenPoint = mainWindow.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.05))
    let bottomScreenPoint = mainWindow.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.90))
    for _ in 0..<times {
        topScreenPoint.press(forDuration: 0, thenDragTo: bottomScreenPoint)
    }
}  
    
func scrollUntilElementAppears(element: XCUIElement, threshold: Int = 10) {
    var iteration = 0

    while !elementIsWithinWindow(element: element) {
        guard iteration < threshold else { break }
        scrollDown()
        iteration += 1
    }

    if !elementIsWithinWindow(element: element) { 
        scrollDown(times: threshold)
    }

    while !elementIsWithinWindow(element: element) {
        guard iteration > 0 else { break }
        scrollUp()
        iteration -= 1
    }
}

您应该检查 isHittable 属性。

如果view没有被隐藏,对应的XCUIElement是可命中的。但有一个警告。 "View 1"可以被"View 2"重叠,但是"View 1"对应的元素是可以命中的

由于您在 table 视图(table 页脚视图)底部有一些 XCUI 元素,因此滚动 table 查看所有UI 测试底部的方法,假设您的 table 视图有很多数据,是 tap().

.swipeUp() 也可以,但问题是当你的测试数据很大时,滑动需要永远,而不是 .tap() 直接跳到底部 table查看。

更具体地说:

XCUIElementsInTheBottomOrTableFooterView.tap()
XCTAssert(XCUIElementsInTheBottomOrTableFooterView.isHittable, "message")