UIAutomation in Xcode (Primer & Cheatsheet)

After playing around with UIAutomation, I decided to write a primer to get you up and running.

A link to a cheat sheet is provided at the bottom of the primer, and both will no doubt be tweaked regularly over time as I play and learn more about UIAutomation.

Drop me a line with any comments, suggestions or tips.

Instruments.
  1. Start by opening Xcode, then in the menu, click ‘Xcode' > ‘Open developer tools’ > ‘Instruments'
  2. Once ‘Instruments' is open it might be a good idea to pin this to your dock.
  3. You should now see several options, choose ‘Automation'
  4. Once open, you should see the below and this is where you will write your first script.


My first script.
  1. ‘Instruments has already made a start by adding the first line of JavaScript for us.
  2. The target is the device or simulator that you’ll attach to later on, in order to run your script. It’s from this variable where you can now target your app and the elements within to perform your tests.

What am I testing?
Essentially, your app - but there is a little bit more to take into consideration when creating UI tests for existing apps - much like Unit & Integration tests, the job is a whole lot easier when starting a project from scratch.

To keep things simple, we’ll pretend we have just started working a brand new app and we have the first stages of the prototype ready for demo.

Clone the below repo to see the prototype we are going to use for this tutorial and have a quick run through the app.

Git: https://bitbucket.org/calicoware/uiautomationprimer.git

How am I going to test?
Now that we know what we are testing, it’s important to know what tests we intend to run.
Start by making a note of all buttons and actions that could occur in your app. For our test app we want to the test the following:-
  1. Button clicks
  2. Paging (both left & right)
  3. Alerts

All the above actions make up our app, but don’t be afraid to add some more tests that don’t necessarily have anything to do with your apps immediate usability.
  1. Gestures - pinch and swipe up
  2. Random taps across the screen.

Basically, try to do anything that you’re not supposed to do in your app, and all being well your app should be fine.

My first script.
First we are going to identify the main screen and touch the first button and back again.

// attach to the device
var target = UIATarget.localTarget();

// target the app
var myApp = target.frontMostApp();

// touch the "accPush" button in the main window
myApp.mainWindow().buttons()["accPush"].tap();

// tap the back button in the navigation bar
target.frontMostApp().navigationBar().leftButton().tap();

Once you’ve added this in, hit record in the top right (not bottom middle - more on that later) and watch the magic unfold.
With any luck your test should perform successfully!

Hopefully the code comments are self explanatory - however, lets touch on the “accPush" identifier a little more.

We could have identified the button by it’s label “PUSH" - but some UIButton text labels or any other text labels could have the same value throughout your application, or even be localised.

This is where “Accessibility Labels" come in, either in Interface Builder or programatically - you can set custom labels to help your script identify certain controls.


UILabel *myLabel;
myLabel.isAccessibilityElement = YES;
myLabel.accessibilityLabel = @"accMyLabel";

Handling UIAlerts..
UIAlerts can pop up anytime during an apps lifetime, so make sure your UIAutomation tests are ready to handle them when they do. Here is a basic overview on how to handle UIAlerts:-

// attach to the device
var target = UIATarget.localTarget();

// target the app
var myApp = target.frontMostApp();

// touch the "accPush" button in the main window
myApp.mainWindow().buttons()["accPush"].tap();

// tap the back button in the navigation bar
target.frontMostApp().navigationBar().leftButton().tap();

// handler for UIAlert
UIATarget.onAlert = function onAlert(alert){
UIALogger.logMessage("alert Shown");
}
// tap the 'alert' button
myApp.mainWindow().buttons()["accAlert"].tap();
// add a delay so the handler can grab the UIAlert
target.delay(2);

I’ve highlighted the additions to our script in bold, as you can see - you simply need to add a handler and in some occasions a delay “after" the UIAlert is shown (this gives the handler time to grab hold of your alert).

Being random.
Next lets do some random tapping, when you ask people to “break your app" - one of the first things they do is randomly tap in places they should not - usually this is fine, but with debug code and some hidden ‘developer’ features this can prove a worth while test.

Amend your script to include the following and re-run your test :-)

var MAX_TAPS = 100;

for (var i = 0; i < MAX_TAPS; i++){
var y = (target.rect().size.height - 25);
var x = Math.floor((Math.random() * 300) + 1);

target.tap({x:x, y:y});
}

Ahh - it’s a ghost….. or developer debug control, easter egg or even an intentional part of the app - either way, a non visible button was clicked and tested - you can now make your mind up if this should be there or not…..

People like to swipe.
Now we are going to take a look at gestures - people love gestures and even if your app doesn’t make use of them, people love to try and swipe (just to see what happens) and for that reason you must test for it.
Update you script with the following and re-run.

target.dragFromToForDuration({x:60.00, y:500.00}, {x:60.00, y:170.00}, 0.5);

All going well that should have ran fine, nice to see our UIAlert handler doing its job again.
There are a few different gestures to look out for (swipes, flicks & scrolls), take a look at the code below for examples on how to test them - depending on how your app is written you might need to play around with their values a little.

// swipe, scroll
target.dragFromToForDuration({x:60.00, y:170.00}, {x:60.00, y:500.00}, 0.5);

// flick
target.flickFromTo({x:centerX - 80, y:centerY}, {x:centerX + 80, y:centerY});

Be aware, scrolling an flicking does not work to well in the iOS Simulator, so your best performing these tests on a physical device.

Auto-generting Scripts.
So we’ve had a go a creating our own scripts from scratch - but let say our app has a little more to it than the one given in this primer and we need a quick way to get a base on which to start writing our own tests with.
Luckely Instruments has an auto script feature which will generate the required Javascript as you move through testing your app.

Simply create a new script, hit the other record button (middle bottom) and start testing your app. You’ll notice that as you move through your app, the script will start to auto-generate.

This handy little feature can be a big help especially for more complex apps. It will allows you to identify elements and buttons that are not necessarily visible in Interface Builder but have been written programatically and buried deep within your codebase.

However you’ll still probably want to tweak a few things once your done, but iOS simulator does have another handy feature for identifying buttons, labels and elements for UIAutomation

Accessibility Inspector.
This neat little feature will no doubt play a big part in writing your UIAutomation scripts. In the iOS simulator (yep, simulator only i’m afraid) go to ‘Settings’ > ‘General’ > ‘Accessibility’ - ‘Accessibility Inspector’.
Once enabled you’ll see the following appear in the simulator



With the inspector expanded (above) you’ll be able to inspect any element/button/label and see it’s properties and more importantly it’s ‘Label’ (used as the identifier in your script).
Collapse the Inspector (press X) to continue through your app, the header will follow your round until your switch this off in settings:



Running your script again.
So lets say your happy with your script and you want to keep it for future use.
First make sure you save it, in Instruments go to ‘File’ > ‘Save As’. Your file will be saved as a .trace file. (I’ve included a .trace file for this tutorial in the test project).

Now anytime you need to re-run your script, simply build and run your app in the iOS Simulator or device, open Instruments and open your .trace file.
Once open make sure your app is select (top left) and click the record button in the top left to start your script.

Automatically running your script.
Okay, we can happily run our script by loading it independently through Instruments, but we can streamline this a bit more by creating a template and adding it to our profiling option in Xcode.

In Instruments, click ‘File’ > ‘Save As Template’ and save the file to your default location (giving it a suitable name).

Next, re-load Xcode (important) and select your Target App and click ‘Edit Schemes’



Once in ‘Edit Schemes’ select ‘Profile’ from the left hand and open the ‘Instrument’ drop down, in the list you should see your recently added Template - select the template and click close.



All you need to do now is select ‘Product’ > ‘Profile’ from the menu and your app will build and run your script automatically.

Conclusion.
There is so much more I haven’t covered yet (including the stuff I haven’t learned yet)- but hopefully this is enough to get you off the ground and if like me, now you have the basic foundation - your happy to go solo from here on.

Download cheatsheet

Thank you for taking the time to read this article and as always any comments and suggestions are always welcome.

Chris