XCUITest | iOS — Locate element uniquely with multiple duplicate identifiers
Case Study:
While it is a standard practice to name & identify each iOS app’s element with an unique accessibility identifier, sometimes, there may be situations that someone else in some other team beyond our reach has named 2 components with same identifier names with same parents in hierarchy where we may not have access to change it.
Problem:
In such situations, How to handle and locate element uniquely when 2 elements have the same identifier?
If we look at a Debug View Hierarchy of an iOS app with above situation, it would look something similar to below block of code:
Example 1:
Cell, identifier: 'UniqueWidgetCell'
Button, identifier: 'root', label: 'Get your refund fast'
StaticText, identifier: 'header_title', label: 'Get your refund fast'
StaticText, identifier: ‘body_description', label: 'Have refund with us’
StaticText, identifier: ‘body_button', label: 'Go to Home’
Image, identifier: 'image'
Button, identifier: 'root', label: ‘Explore us’
StaticText, identifier: 'header_title', label: 'New offers'
StaticText, identifier: 'body_description', label: 'Start getting offers’
StaticText, identifier: 'body_button', label: 'Explore us’
Image, identifier: 'image'
where, we would notice that only the labels are unique and identifiers are duplicated for both parents and children.
Solution
Let us assume, we need to locate the “New offers” text from the 2nd button provided, our locator may look something like below:
let newOfferLabel = app
.buttons["root"]
.children(matching: .staticText)
.matching(identifier: "header_title")
.element(boundBy: 1)
.label
XCTAssertEqual(newOfferLabel, "New offers")
where, we can make use of components such as children & descendent; although having duplicate identifier names, can be further uniquely identified by their indices which is called using boundBy in .element(boundBy: 1)
Similarly, let us look into an another example which is slightly lesser complex than above.
Example 2:
Button, identifier: 'root', label: ‘Go to Bangalore’
Button, identifier: 'root', label: ‘Explore more’
where, if we have to locate the 2nd button from the above 2 buttons having the same identifiers for both, then the approach would be something like provided below:
let exploreButton = app
.buttons
.matching(identifier: "root")
.element(boundBy: 1)
.label
XCTAssertEqual(exploreButton, "Explore more")
here, we are filtering the app’s buttons and matching with identifier with root, which is still a duplicate value and then segregating further by calling the index value of the 2nd button specifically by using element(boundBy: 1).