Reputation: 20566
I'm having a problem where my UI Test says multiple buttons found when using the following code.
app.buttons["Upgrade"].tap()
So I reran my unit test and set a breakpoint right before running that line and hit the record button and clicked the button and it generated the following code.
app.children(matching: .window).element(boundBy: 0).children(matching: .other).element(boundBy: 1).buttons["Upgrade"].tap()
Of course at the top of the test I have let app = XCUIApplication()
.
Any idea why this would be happening?
Sometimes when running p UIApplication.shared.windows
in the debugger it has 2 values in the array. I'm not sure why as I never have multiple windows. The only interactions I have with windows is setting UIApplication.shared.keyWindow?.rootViewController
to different view controllers sometimes, and the following code in didFinishLaunchingWithOptions
.
// Get view controllers ready
self.window = UIWindow(frame: UIScreen.main.bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController: ViewController = mainStoryboard.instantiateViewController(withIdentifier: "FirstView") as! ViewController
// Show view controller
self.window?.rootViewController = mainViewController
self.window?.makeKeyAndVisible()
That is within an if statement and within the else statement I have pretty much the same code except instead of FirstView
it's SecondView
.
Upvotes: 2
Views: 6797
Reputation: 2321
I am seeing similar problems since updating to Xcode 8.3 where the XCUIElementQuery
is returning multiple results when there should be only one. There may be a bug introduced in this build.
https://forums.developer.apple.com/thread/75546 describes the bug and the radar is here: https://openradar.appspot.com/31498932
I've tried the posted workaround by trying to coerce my queries to return the desired object as an XCUIElementQuery
and then using element(boundBy:)
and it worked for me.
It's upsetting how unstable the UI Tests are.
Upvotes: 1
Reputation: 79
I faced same issue and tried multiple things to get out of it. Ultimately i found out out that the issue started happening when i updated my Xcode to 8.3.3 and simulator os to iOS 10.3. Switched my simulator os back to iOS 10.2 and it worked perfectly.
Upvotes: 0
Reputation: 7659
This message appears because there is more than one button on the screen with the accessibilityIdentifier
, accessibilityLabel
or value
, "Upgrade", so it can't work out which one to tap.
The reason that it works when you use the recorded version is because the recording tool has identified that the search needs to be narrowed down to search inside the element of type .other
at index 1
, in order to be sure of which "Upgrade" button to interact with.
It's not a problem with your window(s), but with the uniqueness of your buttons' identifiers, and how you're handling them in your test.
In a situation where the button is only used once on the page of your app in question, it's best to set a unique accessibilityIdentifier
on the UIButton
. Its value should be unique within that page of your app, so make sure you aren't using the same string anywhere else. Then you can access the button unambiguously:
// app code
let upgradeButton: UIButton!
upgradeButton.accessibilityIdentifier = "upgradeButton"
// test code
let app = XCUIApplication()
let upgradeButton = app.buttons["upgradeButton"]
upgradeButton.tap()
In the situation where there are multiple instances of the same upgrade button on screen at the same time (e.g. where the button is part of a repeated pattern on the screen, like if there are lots of products for sale), it's OK for them each to have the same accessibilityIdentifier
, but you should change the way you access the element in your test, using element(boundBy:)
to access items at specified indices:
// app code
let upgradeButton: UIButton!
upgradeButton.accessibilityIdentifier = "upgradeButton"
// test code
let app = XCUIApplication()
let upgradeButton = app.buttons["upgradeButton"].element(boundBy: 1) // second upgrade button
upgradeButton.tap()
In this situation, you could also take the approach of finding the correct container view, and searching for the upgrade button inside it.
// test code
let app = XCUIApplication()
let upgradeButtonContainer = app.descendants(matching: .any).containing(.button, identifier: "upgradeButton").element(boundBy: 1) // second upgrade button-containing element
let upgradeButton = upgradeButtonContainer.buttons["upgradeButton"]
upgradeButton.tap()
Upvotes: 6