【问题标题】:UITesting Xcode 7: How to tell if XCUIElement is visible?UITesting Xcode 7:如何判断 XCUIElement 是否可见?
【发布时间】:2015-12-29 17:41:24
【问题描述】:

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

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

谢谢

【问题讨论】:

    标签: swift automation xcode7 ui-testing xcode-ui-testing


    【解决方案1】:

    不幸的是,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 将返回 true。此外,我们必须检查目标元素的框架是否大于 0 x 0(有时也是如此)——然后我们可以检查该框架是否在主窗口内。

    那么我们需要一个上下滚动可控量的方法:

    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 值将更改滚动操作的比例 - 请注意,如果您离屏幕边缘太近,您将拉出操作系统菜单之一。

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

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

    【讨论】:

    • 这可行,但我必须使用不同的方法获取主窗口: func mainWindow() -> XCUIElement { return XCUIApplication().windows.elementBoundByIndex(0) } 另外,你命名的 ' scrollUp' 将更传统地命名为 'scrollDown'
    • 好点isoiphone! XCUIApplication.mainWindow() 是我忘记添加到此代码示例的扩展,我已将答案编辑为更完整,并为清晰起见交换了 scrollUp 和 scrollDown。
    【解决方案2】:

    由于tableview底部有一些XCUIElements(表尾视图),所以在UI测试中将tableview一直滚动到底部的方式,假设你的tableview有很多数据,是tap() .

    .swipeUp() 也可以完成这项工作,但问题是当您的测试数据很大时,它需要永远滑动,而 .tap() 则直接跳转到 tableView 的底部。

    特别是:

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

    【讨论】:

      【解决方案3】:

      您应该检查isHittable 属性。

      如果视图没有隐藏,则对应的 XCUIElement 是可点击的。但有一个警告。 “View 1”可以被“View 2”覆盖,但是“View 1”对应的元素可以被点击。

      【讨论】:

      【解决方案4】:

      我必须做的是在我的 UI 测试代码中向上或向下滑动。你试过吗?

      XCUIApplication().swipeUp()
      

      或者您也可以执行 WhatUIElement.swipUp() ,它将相对于该元素向上/向下滚动。

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

      【讨论】:

      • 你好,Narine。是的,我做了 swipeUp() 和向下,但无法知道元素何时在视图中.. :-( 正如你所说,希望他们将来会想出一种方法来解决这个问题。
      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 2016-07-29
        • 2011-02-16
        • 2014-05-03
        • 1970-01-01
        • 2010-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多