Summary
I have filed feedback for this item, but I am curious whether this is an intended change or if there's a better way to address the behavior change - FB11764272
.
UITests' XCTest behavior has changed with Xcode 14+ when looking for off-screen elements in a List
component.
The Change
Below is how the behavior worked on the last two major versions of Xcode:
- Xcode 13: Off-screen elements are visible to the UITest process and will scroll to those elements for interaction. e.g. calling
tap()
on an off-screen element.- This is how UIKit TableViews work, even for cells that haven't been dequeued yet.
- Xcode 14: Off-screen elements are NOT visible to the UITest process by default and will cause a test failure if an interaction is attempted. e.g. calling
tap()
on an off-screen element.- This change is more in line with how Lazy Stacks worked in SwiftUI for Xcode 13.
The documentation for XCTUIElement
's tap()
method says the following:
https://developer.apple.com/documentation/xctest/xcuielement/1618666-tap
If the element exists within a scrollable view but is offscreen, XCTest will attempt to scroll the element onscreen before performing the tap.
Also, while an old post (7 years ago), this Developer Forums Post thread #16810 has the following response from an Apple Frameworks Engineer: https://developer.apple.com/forums/thread/16810
You are not supposed to have to scroll manually. Interacting with an element not currently visible in the scroll view is expected to implicitly first scroll the element to be visible without requiring effort. If that's not working for you, please file a bug report (using the Report Bugs link at the bottom of this page). Thanks!
This was true for SwitUI Lists in Xcode 13 but isn't the case with Xcode 14+ (and it appears to continue to not work for Lazy Stacks within ScrollViews in SwiftUI).
Current Workaround
As my team's entire app is written in SwiftUI and we have an extensive UITest suite, we'll need to find every usage of an interaction API, like tap()
, and wrap that with a custom method that first scrolls the entire length of the scroll view (at worst) waiting for the intended element to exist.
This involves a lot of work from a development perspective but also adds a lot of time to run through our extensive UITest suite for an already long-running suite.
Wrap Up
Was this change intentional? If so, what's the suggested way of scrolling to off-screen elements when UI testing a SwiftUI application or is the workaround described above the expected path forward?