I have a UITableview with custom view that appears in the viewForHeaderInSection
delegate method. It appears and functions fine when running the app.
While doing UITests, I’ve noticed that this custom view identifier doesn’t appear in the view hierarchy (unless they’re on screen OR nearly on screen). As a result, I cannot UITest headers that are further down in the tableview.
I’ve found this article from a few years ago https://tiyachows.medium.com/xcuitest-and-its-nuances-3db6fac6f5dc and it says
If the header element is offscreen and then later scrolled into view. The app hierarchy is not refreshed with the id of that header and the UI tests will not be able to access that element.
which doesn’t give me confidence that I’m able to UITest my headers.
Is this a known issue? Are there any known work arounds besides manually scrolling to a header?
3
Answers
This is not an issue per se to have to work around it. Afaik this is a good practice when developing mobile applications not to load all the elements that can be presented on an app page to save memory. Since you are doing UI tests you should try to simulate what a real user would do with your interactions – in this case if the element is offscreen it is recommended that you scroll to it. Imagine that the page cannot be scrolled for some reason – a test will never catch that if you are able to interact with and validate elements offscreen directly.
You’ll need to manually scroll to them. When using a standard table, XCUITest is smart enough to scroll to items not yet visible, but once you get custom you’ll need to get custom with your actions.
I’d suggest a
scrollTo(label: String, maxScrolls: Int)
function. If you see the thing, you’re done! If you don’t, scroll again unless you’ve hit your max threshold (you don’t want to scroll forever if something doesn’t exist in the table at all).If you’re tapping once you reach your item you should know this can be flaky; depending on scroll speed and screen size, you may run into a case where something is barely on screen, resulting in it being visible to XCUITest, but not able to be tapped on yet (barely appearing on screen). To account for this case I check if something is tappable before attempting . If it isn’t (and has already passed the visible check) I’ll perform a tiny, tiny, tiny scroll. If you’re not tapping, ignore this paragraph.
Yes. I encountered this too when I started working on XCTest.
The solution is to do the following:
scrollsIntoView
function (extension).for _ in 0...maxSwipes
and then check if item exists && isHittable. If not keep scrolling andswipeCount += 1
. If so, return true. If we don’t find the element in the maxSwipes, then return false. Returning a bool allows you to check if you see something in only a few swipes and pivot to a different action if it’s not there without failing the test. But you could also assert against the bool value directly in the test case.Also worth pointing out that the swipe direction is the opposite of the scroll direction.
Good luck!