Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot tap UIBarButtonItem in Xcode UI Test

My navigation bar as an "Add" button on it, and I need to have Xcode's UI test tap that button to perform tests in the view controller it opens. I add the button programmatically like so:

UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(showAddVC)];
self.navigationItem.rightBarButtonItem = addButton;

And in my test I have:

XCUIApplication *app = [[XCUIApplication alloc] init];
XCTAssert([app.buttons[@"Add"] exists]); // <-- This passes, so the test runner does see the button.

But when I try to tap it using either:

// Generated using the test recorder
[app.navigationBars[@"App Title"].buttons[@"Add"] tap];

or:

// Same expression used with the XCTAsset earlier
[app.buttons[@"Add"] tap];    

Nothing happens. The action that should take place when the button is tapped does not happen. I tried adding some sleep(5)'s between lines to let the app load, but that didn't help much.

This is the test log:

Test Case '-[xx]' started.
    t =     0.00s     Start Test
    t =     0.00s     Set Up
2015-12-22 16:25:02.898 XCTRunner[10978:384690] Continuing to run tests in the background with task ID 1
    t =     0.94s         Launch xx
    t =     1.01s             Waiting for accessibility to load
    t =     3.45s             Wait for app to idle
    t =     9.02s     Tap "Add" Button
    t =     9.02s         Wait for app to idle
    t =    39.07s             Assertion Failure: UI Testing Failure - App failed to quiesce within 30s
xx: error: -[xx] : UI Testing Failure - App failed to quiesce within 30s
like image 881
ecnepsnai Avatar asked Mar 14 '23 19:03

ecnepsnai


2 Answers

None of the above answers worked for me. What finally made it work, after hours of struggle, was repeating the tap. Try this:

[app.navigationBars[@"App Title"].buttons[@"Add"] tap];
[app.navigationBars[@"App Title"].buttons[@"Add"] tap];

While the above worked for me initially, I found that sometimes the first tap worked, which would result in two taps. My solution to this was to instead tap an arbitrary UI element that doesn't trigger any actions at the beginning of the UI test, and then proceed as normal. I think that the first tap works on certain devices, or maybe after the first UI test is run.

like image 196
Stefan S Avatar answered Mar 17 '23 11:03

Stefan S


Testing for exists does not seem to be sufficient in your case. Wait for the button to be hittable before trying to tap it.

    expectationForPredicate(predicate, evaluatedWithObject: element, handler: nil)
    waitForExpectationsWithTimeout(timeoutSeconds, handler: nil)

Where in your case it would be:

    expectationForPredicate(NSPredicate(format: "hittable == YES"), evaluatedWithObject: [app.buttons[@"Add"], handler: nil)
    waitForExpectationsWithTimeout(15, handler: nil)
    [app.buttons[@"Add"] tap];  

This will pause execution of code after the waitForExpectationWithTimeout until that predicate has been satisfied with the given element.

Otherwise, in extreme cases I have found that sometimes errors occur when trying to interact with certain components. How, why and when these occur is a bit of a mystery, but they seem to be somewhat consistent to certain components, and things involving UINavigationBars seem to have them happen more often.

To overcome these, I have found that using this extension will sometimes work.

extension XCUIElement {

    /* Sends a tap event to a hittable/unhittable element. Needed to get past bug */
    func forceTapElement() {
        if hittable {
            tap()
        }
        else {
            let coordinate: XCUICoordinate = coordinateWithNormalizedOffset(CGVectorMake(0.0, 0.0))
            coordinate.tap()
        }
    }
}
like image 38
Alex Avatar answered Mar 17 '23 12:03

Alex