27

I am writing tests for my app and need to find the button "View 2 more offers" there are multiple of these buttons on my page but I would just like to click on one. When I try this, an error comes saying "Multiple matches found" So the question is, what ways can I go around this so my test will search and tap on only one of the buttons called "View 2 more offers".

Here is my current code

let accordianButton = self.app.buttons["View 2 more offers"]
    if accordianButton.exists {
        accordianButton.tap()
    }
    sleep(1)
}
Julien Quere
  • 2,407
  • 16
  • 21
Billy Boyo
  • 819
  • 2
  • 12
  • 22
  • 1
    This isn't XCTest, it's XCUITest. – gran_profaci Sep 12 '16 at 11:00
  • Can you check the hierarchy and see if multiple elements are present? Also, have you made the UIButton accessible and given it the same accessibility label as that on the UIButtonLabel? – gran_profaci Sep 12 '16 at 11:01
  • @gran_profaci , hi thanks for the help, yes multiple elements are present and using the label name causes the same error. Do you know if there is a way so it selects the first button of the element type? – Billy Boyo Sep 12 '16 at 11:11

4 Answers4

43

You should use a more elaborated way to query your button, since there is more than one button who's matching it.

    // We fetch all buttons matching "View 2 more offers" (accordianButtonsQuery is a XCUIElementQuery)
    let accordianButtonsQuery = self.app.buttons.matchingIdentifier("View 2 more offers")
    // If there is at least one
    if accordianButtonsQuery.count > 0 {
        // We take the first one and tap it
        let firstButton = accordianButtonsQuery.elementBoundByIndex(0)
        firstButton.tap()
    }

Swift 4:

    let accordianButtonsQuery = self.app.buttons.matching(identifier: "View 2 more offers")
    if accordianButtonsQuery.count > 0 {
        let firstButton = accordianButtonsQuery.element(boundBy: 0)
        firstButton.tap()
    }
Liron Yahdav
  • 10,152
  • 8
  • 68
  • 104
Julien Quere
  • 2,407
  • 16
  • 21
  • 1
    This has fixed the solution! Thanks! – Billy Boyo Sep 12 '16 at 12:55
  • You're welcome. If this answer solved your problem, please [mark it as accepted](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) (by clicking on the checkmark beside it). – Julien Quere Sep 12 '16 at 15:14
  • If your tests have started failing after updating to XCode 8.3, this will be because of a previous bug that has been [fixed by Apple](https://developer.apple.com/library/content/releasenotes/DeveloperTools/RN-Xcode/Chapters/Introduction.html#//apple_ref/doc/uid/TP40001051-CH1-SW160). "Fixed a bug in the handling of XCTestExpectations that waited on predicates evaluated against XCUIElements. As a result, some tests which were previously succeeding despite ambiguous (incorrect) queries will start to fail." – Dave Barker Apr 06 '17 at 10:03
11

There are a couple of ways to go about solving this issue.

Absolute Indexing

If you absolutely know the button will be the second one on the screen you can access it by index.

XCUIApplication().buttons.element(boundBy: 1)

However, any time the button moves on the screen, or other buttons are added, you might have to update the query.

Accessibility Update

If you have access to the production code you can change the accessibilityTitle on the button. Change it something more specific than the title text and then access the button via test using the new title. This property only shows up for testing and won't be presented to the user when reading off the screen.

More Specific Query

If the two buttons are nested inside of other UI elements you can write a more specific query. Say, for example, that each button is inside of a table view cell. You can add accessibility to the table cells then query for the button.

let app = XCUIApplication()
app.cells["First Cell"].buttons["View 2 more offers"].tap()
app.cells["Second Cell"].buttons["View 2 more offers"].tap()
Joe Masilotti
  • 16,815
  • 6
  • 77
  • 87
  • 2
    Thank you for the documentation, you were correct in myself needing to specify the query more. BTW good job on the blog, been helping myself lots recently while learning the testing! – Billy Boyo Sep 12 '16 at 13:25
11

Xcode 9 introduces a firstMatch property to solve this issue:

app.staticTexts["View 2 more offers"].firstMatch.tap()
Xavier Lowmiller
  • 1,381
  • 1
  • 15
  • 24
  • 1
    I also used to think this but somewhere I read that firstMatch is NOT for this purpose and referred to firstMatch's doc https://developer.apple.com/documentation/xctest/xcuielementtypequeryprovider/2902250-firstmatch :) – Hasaan Ali Dec 20 '17 at 06:55
  • I love this solution because for some reason I was receiving the error even though I had only one element. – David J Oct 29 '18 at 01:29
1

You should use matching, then element, like

let predicate = NSPredicate(format: "identifier CONTAINS 'Cat'")
let image = app.images.matching(predicate).element(boundBy: 0)
onmyway133
  • 45,645
  • 31
  • 257
  • 263