Jump to content

How to use UIAutomation to create iPhone UI tests

+ 8
  blackbear's Photo
Posted Jun 21 2010 05:56 AM

One of the more useful (from a developer standpoint) features coming in iOS 4 (formerly iPhone OS 4) is the UIAutomation tool. This lets you run an automated set of tests against an application, and test to see if they had the expected results. Unfortunately, at the time of this writing, there is minimal documentation for the tool, so here's a quick walkthrough of how to use it.

To start, you need the iOS 4 SDK, which is available as of today from developer.apple.com. Next, you need to make sure that you have your application set up to be easily run by the UIAutomation suite. The main thing is to tag all your UI controls in Interface Builder with names, by setting the Accessability label to a unique value for the view.

Attached Image


Once these are all set and you've rebuilt your application for debug, you need to write a Javascript file that runs your application and tests to make sure that everything is running as expected. Here's a sample file, followed by some commentary.


[color=#1C2837][font=arial, verdana, tahoma, sans-serif][size=2][color=#000000]UIALogger.logStart("Starting Test");
var view = UIATarget.localTarget().frontMostApp().mainWindow().elements()[2];
var textfields = view.textFields();
if (textfields.length != 2) {
    UIALogger.logFail("Wrong number of text fields");
} else {
    UIALogger.logPass("Right number of text fields");
}
var passwordfields = view.secureTextFields();
if (passwordfields.length != 1) {
    UIALogger.logFail("Wrong number of password fields");
} else {
    UIALogger.logPass("Right number of password fields");
}
textfields["username"].setValue("tturner");
passwordfields[0].setValue("tod");
view.buttons()["logon"].tap();
var errorVal = view.staticTexts()["error"].value();
if (errorVal != "Invalid User Name or Password") {
    UIALogger.logFail("Did Not Get Invalid Username Error: " + errorVal);
} else {
    UIALogger.logPass("Username Error Detected");
}[/color][/size][/font][/color]


The first line of the test simply sends an informational message to the log. The next line uses UIATarget.localTarget().frontMostApp() to get the currently running application of the test target (which is whatever we've configured Instruments, the test runner, to run our tests on.) We then use mainWindow to get the main UIWindow, and then Elements to find all the children of the window. The third child of the main window in this application is a scroll view, so we use the [2] array reference to get it. We then get all the text fields (which don't include password fields), and make sure there are 2 of them. We also check that there is 1 password field. Next, we set the username and password field, and tap the submit button. Then we check to see that the error label has been set to the correct error message.

You should note that this last bit doesn't currently work. The text label string of a UILabel shows up as the StaticText field's name, not it's value, which is always nil. If you set the Accessibility label, it becomes the name property, and there is no longer any way to discover the current text in a UILabel. I have a bug report open with Apple about this.

FOLOWUP: This bug still exists, but I was able to work around it by using a UITextField or UITextView with the border and user interaction turned off in place of a UILabel that I wanted to change. The .value accessor works correctly on text fields and views.

You can find all the available Javascript objects by searching for "UI Automation Reference Collection" in the iPhone documentation online.

Once you've written your Javascript, it's time to run it and see if it works. Fire up Instruments (you can find it in Spotlight), and create an iPhone Simulator Automation trace:

Attached Image


Once you're in the Trace window, use the Choose Target pulldown to navigate to the debugging version of you App:

Attached Image


Use the Scripts pulldown to choose the Javascript test file you created, then click the record button. This will start up the iPhone simulator, wait about 5 seconds for your application to start, and then start going through the tests you wrote. Note that when the trace gets to the end of the Javascript file, it doesn't stop recording, you have to do it manually. You'll see the results in the Script Log:

Attached Image


You can also throw another instrument, such as the memory leak instrument, in with your UIAutomation run, and automatically find any memory leaks that occur while the application is being exercised.

One final tip. If you make a call to logElementTree() on any element, it will dump a full tree view of the current element and all its children to the log, which can be very useful when you're trying to figure out how to navigate the element tree.

17 Replies

 : Jun 30 2010 10:35 AM
Hi blackbear,

Thank you for this guide. I was wondering if you could help me with the picker classes? Specifically, I have a picker with two wheels. I want the test to scroll to a specific element of each wheel. Is there a method for doing this?

EDIT: I solved my own problem. I created a function that taps on the element just above/below the selector however many times the argument specifies.

After working with this for an afternoon, I can tell it has a lot of potential. Hopefully some better documentation surfaces soon.

function scrollPicker(wheel, numElements)
{
    var location = wheel.rect();
    var origin = location.origin;
    var size = location.size;
    
    var wheelWidth = size.width;
    var wheelHeight = size.height;    
    
    var tapWidth = wheelWidth/2;
    var tapHeight = 0;
    
    if(numElements < 0)
    {
        tapHeight = wheelHeight/2 - 47;
    }
    else
    {
        tapHeight = wheelHeight/2 + 47;
    }
    
    var tapLocation = new Point(origin.x+tapWidth, tapHeight+origin.y);
    
    var app = UIATarget.localTarget();
    var count = Math.abs(numElements);
    
    for(var i=0; i<count; i++)
    {
        app.tap(tapLocation);
        pausecomp(200);
    }
}

function pausecomp(millis)
{
var date = new Date();
var curDate = null;

do { curDate = new Date(); }
while(curDate-date < millis);
} 

The pausecomp(millis) method is necessary because if you don't delay the taps, they won't always work correctly.

This post has been edited by zpthacker: 01 July 2010 - 10:21 AM

 : Jul 02 2010 11:54 AM
Thanks for the tip!.

I noticed that "captureScreenWithName" only seems to work with a device and not with the simulator.
 : Jul 14 2010 04:32 AM
Hi,

Is there any way to run UIAutomation saved templates from terminal (command line)? I've found instruments command line tool but it only opens my app and does not run script.

Thanks.
 : Jul 21 2010 03:37 AM
Hi,

Thanks for this helpful post.

In this post we test application for which we owner code also. But I want to test the application whose code is not available and we have only .app file of it.

Is it possible to test applications using .app file using Automation tool.

Thanks,
 : Oct 12 2010 03:49 AM
Hi zpthacker,
I have picker wheel with one wheel, i need to scroll the picker and tap on the wheel item.
I tried the function scrollPicker(wheel, numElements),
below are the method calls
var picker = window.pickers(); 
var pickerWheels = picker[0].wheels();
var pickerWheelValues =pickerWheels[0].values();
// function call
scrollPicker(pickerWheels[0], pickerWheelValues.length);

Got the below error

Exception raised while running script: ReferenceError: Can't find variable: Point

var tapLocation = new Point(origin.x+tapWidth, tapHeight+origin.y); <-----

Am i missing something, can u pls help me out?
0
  sree.tester's Photo
Posted Oct 12 2010 11:27 PM

Hi,

Is it possible to run an automation test case using instruments & UI automation on a iPhone device rather than on the simulator.

Any pointers will be really helpful.

Thanks in advance
0
  wookay's Photo
Posted Oct 28 2010 01:40 PM

Hello,
There's a script tool for UI Automation & UI Inspect.
Here's example.
http://gist.github.com/620219

Full source code and the sample app for it.
http://github.com/wookay/libcat



Thanks. it's under development but could be useful to develop iPhone application.
 : Nov 09 2010 02:33 PM
Can someone let me know the reasons why we receive "Target failed to run" after clicking on the Record button (both the Build & appropriate Test Script are selected)? The above error seem to be b/c of the Script not being in the same folder as the App.

And anyone knows why the UIAutomation doesn't identify the elements from the UI? Is it b/c the xcode 3.2.4 doesn't support the UIAutomation on the iPad Simulator (I am trying to work on the iPad App here and not the iPhone)? Any thoughts would be greatly appreciated.

This post has been edited by shmikkil01: 16 November 2010 - 07:31 AM

+ 1
  shmikkil01's Photo
Posted Nov 18 2010 09:02 AM

Hey Blackbear,

I was trying to run the same code as you had here but was not able to identify the elements from the App...
------
UIALogger.logstart("ADSeM App");
var window = UIATarget.localTarget().frontMostApp().mainWindow();
var view = window.elements()[0];

var buttons = window.buttons();

// Check number of Static fields
if(buttons.length!=2)
{
UIALogger.logFail("FAILED: Incorrect Number of Buttons");
}
else
{
UIALogger.logPass("PASSED: Correct Number of Buttons");
}
I basically have an App with a couple of buttons where I need to use the tap feature.
-----
I know these are basics but couldn't find any info on how to get started over this. Your inputs would be very helpful...
-1
  KaLoMiDo's Photo
Posted Sep 21 2011 10:05 PM

Hey Blackbear,

Excuse me, Could you tell me how to use UIAutomation to create iPhone UI tests detail? I'm a begainner. :)

Thanks!
0
  Raju123's Photo
Posted Sep 27 2011 10:59 PM

hi calomido

try this one http://guerratopia.c...-testing-in-ios
0
  Raju123's Photo
Posted Sep 27 2011 11:01 PM

Hi zpthacker,

i am getting the same problem as senthilk01 in using pickerwheel function you provided.
kindly give the solution ASAP.
0
  iElena's Photo
Posted Sep 28 2011 07:09 AM

Thanks for your article!
0
  An1lkumar's Photo
Posted May 17 2012 10:57 PM

Hello BlackBear,
Thank you for your Post , i was hoping if you can elaborate on your WORK AROUND for accessing the 'ERROR LABEL' on alert messages . I need to access the error label to check for appropriate alert message.

can you please elaborate on your description below.

"FOLOWUP: This bug still exists, but I was able to work around it by using a UITextField or UITextView with the border and user interaction turned off in place of a UILabel that I wanted to change. The .value accessor works correctly on text fields and views"
 : Mar 26 2013 04:35 PM
Hi James, thanks for this tutorial on using UIAutomation to create iPhone UI tests. I found it very helpful!
I compiled a list of some top resources on beginning automated unit testing with XCode; and I included your post. Hope other developers find this useful too: http://www.verious.c...ting-with-xcode

Thank you

Best,
Giancarlo
0
  jamesmartine's Photo
Posted Feb 25 2014 01:46 AM

UIautomation provide great help to debug the error. Most of the time i am stucking up on some error i usually use it to find that where the actual problem is. I have Integrated This with an online site so in when the problems are also become the part of the data. It will help to solve the problems in future.
0
  BlueHost Review's Photo
Posted Apr 15 2014 04:20 PM

which ones do you use configured Instruments?

UIALogger.logStart("Starting Test");
var view = UIATarget.localTarget().frontMostApp().mainWindow().elements()[2];
var textfields = view.textFields();
if (textfields.length != 2) {
UIALogger.logFail("Wrong number of text fields");
} else {
UIALogger.logPass("Right number of text fields");
}
var passwordfields = view.secureTextFields();
if (passwordfields.length != 1) {
UIALogger.logFail("Wrong number of password fields");
} else {
UIALogger.logPass("Right number of password fields");
}
textfields["username"].setValue("tturner");
passwordfields[0].setValue("tod");
view.buttons()["logon"].tap();
var errorVal = view.staticTexts()["error"].value();
if (errorVal != "Invalid User Name or Password") {
UIALogger.logFail("Did Not Get Invalid Username Error: " + errorVal);
} else {
UIALogger.logPass("Username Error Detected");
}



click here