25

Simple problem. I got button which perform segue to next view controller. I want to write UI XCTest to tell me did it open view controller i wanted.

Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112
Dejan Zuza
  • 359
  • 1
  • 6
  • 14

2 Answers2

47

The UI Testing framework doesn't have access to your applications code which makes class assertions on instances impossible. You are not able to directly tell the class of the controller which is on screen.

However, if you think about your test a little differently you can make a very similar assertion. Write your tests as if you are the user. Your user doesn't care if he/she is looking at a ItemDetailViewController or a ItemListTableViewController so neither should your tests.

The user cares what's on the screen. What's the title? Or, what are the names of these buttons? Following that logic you are rewrite your test to assert based on those items, not the name of the coded class.

For example, if you are presenting your controller in a navigation stack you can assert the title.

let app = XCUIApplication()
app.buttons["View Item"].tap()

XCTAssert(app.navigationBars["Some Item"].exists)

Or, if the screen is presented modally but you know some static text or buttons, use those.

let app = XCUIApplication()
app.buttons["View Item"].tap()

XCTAssert(app.staticTexts["Item Detail"].exists)
XCTAssert(app.buttons["Remove Item"].exists)
Joe Masilotti
  • 16,815
  • 6
  • 77
  • 87
  • 2
    In my case, I have to `sleep(1)` before `app.buttons["View Item"].tap()` for it to work – onmyway133 Apr 04 '16 at 07:32
  • 11
    I feel that there must be a better way of doing this... What happens if when you change text in the app? What happens if you have multiple localisations of the text in the app? It seems that it would require quite a lot of tests if it was localised and a potential maintenance headache if the text needs to change. – Matt Green Oct 12 '16 at 12:39
  • 1
    @MattGreen then you have to assign accessibility identifiers to such labels/textfields etc. Then it will be like let button = app.buttons["accessId"], XCTAssertEqual(button.label, "EXPECTED LABEL"), and/or button.tap() – Hasaan Ali Apr 30 '18 at 07:39
  • @JoeMasilotti with your method, is it possible to call accessibilityCustomAction with XCUITest, please ? – XLE_22 May 02 '19 at 09:14
2

Comment of Matt Green gave me a good idea. We can define an unused label/button, ideally inside a base view controller and assign it an accessibility label to perform a query to find out which view controller is presented.

public class BaseViewController: UIViewController {

    let button = UIButton(frame: CGRect(x: 0, y: 0, width: 1, height: 1))

    public override func viewDidLoad() {
        super.viewDidLoad()
        if let identifier = self.theClassName.split(separator: ".").last {
            button.accessibilityIdentifier = String(identifier)
            view.addSubview(button)
        }
    }
}

public class DatePickerViewController: BaseViewController {
   ...
}

func testExample() {
    let app = XCUIApplication()
    app.launch()
    app.navigationBars.buttons["DateSelector"].tap()
    XCTAssertTrue(app.buttons["DatePickerViewController"].exists)
}

Note that inorder to make this approach work you have to add the view you use to identify view controller, in this case a button, should be added as a sub view and has to have a non zero frame.

Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112