Xcode 7 UI Testing: how to reject a series of system warnings in code

I am writing UI test cases using the new Xcode 7 interface testing feature. At some point in my application, I ask the user permission to access the camera and push notification. Thus, two iOS pop-ups will appear: "MyApp Would Like to Access the Camera" popup and "MyApp Would Like to Send You Notifications" pop-up. I would like my test to reject both pop-ups.

The user interface entry created the following code for me:

 [app.alerts[@"cameraAccessTitle"].collectionViews.buttons[@"OK"] tap]; 

However, [app.alerts[@"cameraAccessTitle"] exists] resolves false, and the code above generates an error: Assertion Failure: UI Testing Failure - Failure getting refresh snapshot Error Domain=XCTestManagerErrorDomain Code=13 "Error copying attributes -25202" .

So what is the best way to dismiss the system warning stack in a test? System pop-ups interrupt my application flow and immediately interrupt my regular test cases. In fact, any recommendations regarding how I can get around system warnings so that I can resume testing a regular thread are appreciated.

This question may be related to this SO post, which also has no answer: Xcode7 | Xcode Interface Tests | How to deal with location alerts?

Thanks in advance.

+50
ios objective-c xcode7 xcode-ui-testing xctest
Aug 21 '15 at 20:42
source share
10 answers

Xcode 7.1

Xcode 7.1 has finally fixed the issue with system warnings. There are, however, two small errors.

First, before submitting an alert, you need to configure a "UI interrupt handler." This is our way of telling the structure how to handle the alert when it appears.

Secondly, after the warning is presented, you must interact with the interface. Just listening to the application works fine, but it is necessary.

 addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in alert.buttons["Allow"].tap() return true } app.buttons["Request Location"].tap() app.tap() // need to interact with the app for the handler to fire 

The Location dialog box is just a line that helps the developer determine which handler was accessed and is not a warning type.

I believe that returning true from the handler means it is “completed”, which means that it will not be called again. For your situation, I would try to return false so that the second warning calls the handler again.

Xcode 7.0

The following will reject one “system warning” in Xcode 7 Beta 6:

 let app = XCUIApplication() app.launch() // trigger location permission dialog app.alerts.element.collectionViews.buttons["Allow"].tap() 

Beta 6 introduced many fixes for testing the user interface, and I believe that this was one of them.

Also note that I call -element directly on -alerts . Calling -element on an XCUIElementQuery forces the framework to select the same matching element on the screen. This is great for alerts where you can only see one visible at a time. However, if you try this for a label and have two labels, the framework will throw an exception.

+48
Aug 26 '15 at 13:29
source share

Gosh. He always clicks "Do not allow", although I intentionally say, click "Allow"

At least

 if app.alerts.element.collectionViews.buttons["Allow"].exists { app.tap() } 

allows me to move on and do other tests.

+3
Oct. 20 '16 at 7:53 on
source share

Goal - C

 -(void) registerHandlerforDescription: (NSString*) description { [self addUIInterruptionMonitorWithDescription:description handler:^BOOL(XCUIElement * _Nonnull interruptingElement) { XCUIElement *element = interruptingElement; XCUIElement *allow = element.buttons[@"Allow"]; XCUIElement *ok = element.buttons[@"OK"]; if ([ok exists]) { [ok tap]; return YES; } if ([allow exists]) { [allow tap]; return YES; } return NO; }]; } -(void)setUp { [super setUp]; self.continueAfterFailure = NO; self.app = [[XCUIApplication alloc] init]; [self.app launch]; [self registerHandlerforDescription:@""MyApp" would like to make data available to nearby Bluetooth devices even when you're not using app."]; [self registerHandlerforDescription:@""MyApp" Would Like to Access Your Photos"]; [self registerHandlerforDescription:@""MyApp" Would Like to Access the Camera"]; } 

Swift

 addUIInterruptionMonitorWithDescription("Description") { (alert) -> Bool in alert.buttons["Allow"].tap() alert.buttons["OK"].tap() return true } 
+3
Mar 15 '17 at 13:08
source share

The God! I hate how XCTest has the worst time associated with UIView alerts. I have an application in which I get 2 warnings, the first of which wants me to select “Allow” to enable location services for application permissions, and then on the splash page the user must click UIButton called “Enable location” and finally SMS notification in UIViewAlert, and user must select “OK”. The problem that we encountered was the inability to interact with system alerts, as well as in race conditions, when the behavior and appearance on the screen were untimely. It seems that if you use alert.element.buttons["whateverText"].tap Tap, the XCTest logic should continue to click until the test time runs out. So basically, keep tapping anything on the screen until all system warnings are visible.

This is a hack, but this is what worked for me.

  func testGetPastTheStupidAlerts(){ let app = XCUIApplication() app.launch() if app.alerts.element.collectionViews.buttons["Allow"].exists { app.tap() } app.buttons["TURN ON MY LOCATION"].tap() } 

The “Allow” line is completely ignored, and the app.tap() logic is called evreytime, and the button I wanted to reach [“Enable Location”] appears, and the test pass

~ Totally confused, thanks Apple.

+1
Aug 04 '16 at 6:55
source share

The only thing I found that this was reliably fixed was to install two separate tests for handling alerts. In the first test, I call app.tap() and do nothing. In the second test, I call app.tap() again and then do the real work.

+1
Oct 17 '16 at 23:30
source share

For those who are looking for specific descriptions for certain system dialogs (as I did), they are not :), the line is intended only for testing testers. Associated Apple Document Link: https://developer.apple.com/documentation/xctest/xctestcase/1496273-adduiinterruptionmonitor




Update: xcode 9.2

The method sometimes works, sometimes not. The best workaround for me is when I know that there will be a system warning, I add:

 sleep(2) app.tap() 

and the system alert is gone

+1
Feb 27 '18 at 14:42
source share

In xcode 9.1, warnings are only processed if the test device has iOS 11 . Does not work on older versions of iOS, e.g. 10.3, etc. Link: https://forums.developer.apple.com/thread/86989

To handle alerts, use this:

 //Use this before the alerts appear. I am doing it before app.launch() let allowButtonPredicate = NSPredicate(format: "label == 'Always Allow' || label == 'Allow'") //1st alert _ = addUIInterruptionMonitor(withDescription: "Allow to access your location?") { (alert) -> Bool in let alwaysAllowButton = alert.buttons.matching(allowButtonPredicate).element.firstMatch if alwaysAllowButton.exists { alwaysAllowButton.tap() return true } return false } //Copy paste if there are more than one alerts to handle in the app 
0
Dec 20 '17 at 13:42 on
source share

@Joe Masilotti the answer is correct and thanks for that, it helped me a lot :)

I just wanted to point out one thing: UIInterruptionMonitor intercepts all the system warnings presented in the TOGETHER series, so the action that you apply in the completion handler applies to each warning ("Do not allow" or "OK"). "). If you want to handle alert actions differently, you must check in the completion handler which alert is currently presented, for example, by checking its static text, and then the action will be applied only to this alert.

Here is a small piece of code to apply the Do Not Allow action to the second warning, in a series of three warnings and the OK action for the remaining two:

 addUIInterruptionMonitor(withDescription: "Access to sound recording") { (alert) -> Bool in if alert.staticTexts["MyApp would like to use your microphone for recording your sound."].exists { alert.buttons["Dont Allow"].tap() } else { alert.buttons["OK"].tap() } return true } app.tap() 
0
Apr 11
source share

@ Jo-masilotti

I am using Mac Mojave 10.14.3 and the source version is 0.59.1, fastlane. But I could not turn off the system alert. I attached my code and warning screen

Could you help me

enter image description here enter image description here

enter image description here

0
May 24 '19 at 6:34
source share

It seems that the approach to introducing access to cameras and notifications streams, as you say, but are not physically controlled and do not leave the chance of when and how they are displayed.

I suspect that one of them is triggered by the other, and when it is pressed programmatically, it destroys the other (which Apple will probably never allow)

Think about it, you ask permission from users, and then make a decision on their behalf? What for? Because you cannot make your code work.

How to fix it - trace where these two components trigger pop-up dialogs - where are they called ?, rewrite to run only one, send NSNotification when one dialog has been completed to call and display the remaining one.

I would seriously abandon the approach to programmatically pressing dialog buttons intended for the user.

-one
Aug 24 '15 at 13:53 on
source share



All Articles