With Xcode 7, Apple has given us a new tool for user interface testing. I’m not going to cover the basics here, but for an great introduction to user interface testing in Xcode, check out this awesome article: UI Testing in Xcode 7

As of beta 3, there is no way to handle native system alerts. This is a pretty big roadblock to actually UI testing apps that support location. Luckily, we can work around this by digging a little bit into the CoreLocation framework.

A prerequisite for this is the Hopper Disassembler, which I can’t recommend highly enough. It’s a great starting point for poking at and tearing apart system frameworks, libraries and executables.

First, lets open the CoreLocation framework with Hopper. As of writing, this framework existed on the file system at:

/Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreLocation.framework

Let’s open the binary within the framework.

CoreLocationHopper

Looking on the left panel under “labels”, there are lots of CLLocationManager methods. Scrolling through, we can see a couple of interesting authorization related ones.

    +[CLLocationManager _authorizationStatus]
    +[CLLocationManager authorizationStatus]
    +[CLLocationManager setAuthorizationStatus:forBundleIdentifier:]
    +[CLLocationManager setAuthorizationStatus:forBundle:]

The setAuthorizationStatus:forBundle: method is a private method, but since we are going to be only calling it from a test, we can use it. Note: Do NOT use this in a production app, you will absolutely be rejected from the app store. This is going to let us set the location permission to whatever we want, while supressing the location alert from showing.

While we can’t call the method directly, we can build a method signature and invoke a custom invocation like this:

    SEL selector = NSSelectorFromString(@"setAuthorizationStatus:forBundleIdentifier:");
    NSMethodSignature *methodSignature = [CLLocationManager methodSignatureForSelector:selector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
    invocation.selector = selector;

    CLAuthorizationStatus status = kCLAuthorizationStatusAuthorizedAlways;
    NSString *identifier = @"com.test.testapp";

    [invocation setArgument:&status atIndex:2];
    [invocation setArgument:&identifier atIndex:3];
    [invocation invokeWithTarget:[CLLocationManager class]];
  • Notice that the invocation argument indexes start at 2, this is because the first two arguments are implicit (self and _cmd)
  • We used a bundle identifier string rather than loading the bundle, this is because as UI tests run they run inside a different TestRunner bundle.

That’s it! Enjoy!

Hopefully  fixes all the pain points and remaining concerns of UI testing before the final Xcode 7 release :)

Edit: For supressing some other permission dialogs, check out this github issue: https://github.com/facebook/xctool/issues/276#issuecomment-30074325 (Thanks to Joe Masilotti for the tip!)