UI Automation on the iPhone

As a summer intern I could have worried about just being given tasks such as making the tea, but here at Airsource, among other challenges, I was given the chance to work on improving some the QA infrastructure via automated testing. I study Engineering and have learnt some theory regarding testing practices in software development, so it was great to put that to a practical use!

Apple announced with iOS4 the ability to do automated UI testing on the iPhone as a part of the Instruments development tool. Such testing is done with the help of a number of JavaScript libraries which allow you to access an application on a device and tap buttons, scroll views and varify text as if it were a human interacting with the hardware, and not a script.

Why is this – other than the fact it’s something from Apple – cool? It essentially means that it is possible for us as app developers and testers to automate some of the tests we always do to ensure we have not broken anything in our latest development cycle. There is nothing worse than working hard on adding a new feature to an app, testing it and releasing it only to discover that your testing missed a regression in your previous functionality. This forces you to make a brown paper bag release to fix it, for which you have to test everything again in addition to the things not fully tested the first time. This takes up time and effort which could be used to add more features, and more importantly can lead to your users having a broken app.

Automating the tests does not mean that they are better tests than if they were done manually. What it does mean is that tester time is saved on doing the mundane day to day tests so that they can spend more of their time on testing edge cases and new features, where the harder to find bugs are more likely to be and less likely to be found by the developers as they write code.

What Airsource is doing with it

At Airsource we’ve been busy automating tests for our app, Optiscan. Doing this we have found that, despite what Apple have tried to do with using timeouts, it is sometimes necessary to have delays in the tests. The main shortcoming in this area is the fact UI Automation swallows taps on elements which are not visible; meaning that your tests script may expect a changed state and fail because the tap to change the state was never registered. The reason that timeouts do not work on this is that the element you are attempting to tap exists and valid, so it can be tapped, but it may not be visible (due to an animation sequence for example) thus the tap is never registered by the app. This is a big shortcoming in the system of timeouts, it would be great if Apple fixed this by attempting taps until the tap occurs on a visible element or the timeout is reached.

When you are writing a test you do not want to have to worry if the button is ready to be tapped or not, you just want to tap the button! So as a part of the testing effort we have written a bunch of useful little functions which encapsulate all the delay logic and mean than we don’t worry about delays when we’re writing the tests.

The code below allows us to scroll to an element with a particular name in a scroll view, before waiting for it to become visible and then tapping it. This simple encapsulation of a common task means that there is no need for the test writer to worry about the button becoming visible to tap before tapping it, just worry about how to actually test the app.

// Allows you to scroll to an element with a particular name and tap it.
function scrollToElementWithNameAndTap(scrollView,name)
{
	if (! (elementArray instanceof UIAScrollView))
	{
		throw ("Expected a UIAScrollView");
	}

	var e = elementArray.scrollToElementWithName(name);
	waitForVisible(e,5,0.25);
	e.tap();
}

// Poll till the item becomes visible, up to a specified timeout
function waitForVisible(element, timeout, step)
{
        if (step == null)
        {
                step = 0.5;
        }

        var stop = timeout/step;

        for (var i = 0; i < stop; i++)
        {
                target.delay(step); // for the animation
                if (element.isVisible())
                {
                        return;
                }
        }
        element.logElement();
        throw("Not visible");
}

Using JavaScript objects' prototype property it is even possible to make scrollToElementWithNameAndTap(name) a method of each instance of UIAScrollView. Hence if you have a constructor for your tests you can use

UIAScrollView.prototype.scrollToElementWithNameAndTap = function(name){
                        scrollToElementWithNameAndTap(this,name)
                        };

and now you can call the method on all your UIAScrollViews just as if it were a native UI Automation method.

Doing more than it says on the tin

An unfortunate aspect of the UI Automation kit is that there is no reset button or method. The result of this is that if a test fails, then your app is in an unknown state, thus it is very likely that all subsiquent tests will also fail. Ideally you do not want it to appear as if 20 tests have failed when in fact only one has failed. Having the ability to target.reset() to reset the app to a known state would be very useful, and it is a pain that Apple have not implemented it in some form.

To remedy this we've had to add a small amount of code to our apps which makes it possible for us to interact with them on the level of the program, not just the UI level. UI Automation allows us to change the volume on the device using methods in UIATarget, so we've added a listener for the device volume to our app, which means we can run code when we detect a volume change. Code for a volume listener is shown below.

// Set up a listener to act on volume changes
AudioSessionInitialize(nil, nil, nil, nil);
AudioSessionSetActive(true);
AudioSessionAddPropertyListener(
                         kAudioSessionProperty_CurrentHardwareOutputVolume,
                         applicationVolumeDidChange,
                         self);

// Note that this callback will only be called if the mute button is off.
void applicationVolumeDidChange(void *inClientData,
                         AudioSessionPropertyID inID,
                         UInt32 inDataSize, const void *inData)
{
        NSLog(@"Volume changed");

        // Do something like reset the system
}

This way calling target.clickVolumeUp(); just after UIALogger.logFail(...) in your testing script will mean that you can reset the app before the next test is run. Note that it is important that the hardware mute button is off when you are testing otherwise no signals will be sent that the volume has changed!

Shortcomings

Other than the swallowed taps and lack of reset method mentioned above, there are a couple of other shortcomings with UI Automation. The most obvious being the lack of screenshots when running the tests on the simulator; this makes it hard to verify UI and record what the app's state was when a test failed. To be honest it feels rather bizarre that you can take remote screenshots on the actual hardware iPhone, but not in the simulator. I hope that Apple fix this in future releases.

The less obvious, but rather annoying shortcoming (especially if you are building a large library of testing scripts) is the lack of a decent preprocessor. JavaScript does not have native support for preprocessing or any kind of #import structure, so it is good that UI Automation scripts support #import, but the lack of define statements and logic such as #define and #ifdef is a pain when you want only to run a certain initialisation script once, but it is called by each test you run. Emulation of this behaviour using JavaScript variables is not helped by the fact that variables can remain defined between different runs in Instruments.

Conclusions

Automated UI testing does not mean bug free code. No testing on any non-trivial piece of software can guarantee bug free code, and the testing is only as good as the tests themselves. Using automated testing means that it is easier to stop regressions by having a test in place for each previous bug which has cropped up, it also means that the tester's time can be utilised more effectively. Apple's UI Automation ticks many boxes for good automated testing despite its shortcommings, and we hope will prove an invaluable tool in our development cycle.

23 Responses to “UI Automation on the iPhone”

  1. Gary Says:

    Thanks for the great post. I’ve been messing around with this as well and have seen the same shortcomings. Do you use a continuous integration environment? Have you been able to hook your tests in to automatically run after builds?

    Do you ever need to shutdown your iphone application completely and restart it? I haven’t figured out a way to do this as part of a test. For example, my application is localized. I would like to be able to change the language setting on the iPhone and restart the application. Is there a way to do something like this that you have found?

    Once again, thank you for sharing what you have found!

  2. swetha Says:

    I have to select more than 50 cells in the table,8 cells per screen greter than 8 cells screen has to scroll up and tick next to the item
    Here is the below code
    table= appl.tableViews()[0];
    table_cell=table.cells().length;
    for(i=0;i6)
    {
    table.scrollDown();
    UIALogger.logPass(“scroll”);
    system.delay(4);

    system.delay(1);
    // appl.logElementTree();
    }
    issue here is it still misses to tick few items in middle of the screen

  3. swetha Says:

    sorry for loop is placed wrong
    Here is the right code

    table= appl.tableViews()[0];
    table_cell=table.cells().length;
    for(i=0;i6)
    {
    table.scrollDown();
    UIALogger.logPass(“scroll”);
    system.delay(4);

    system.delay(1);
    // appl.logElementTree();
    }

  4. Maja Says:

    hey Gary,
    I was wondering if you have gotten a response anywhere for your question? I have the same issue. I localized my app and having to change the language and then start the app again is a pain in the butt.
    Do you know yet whether you can change the language and start the app within one test?

    @airsource: thanks for the great blog!!!

    Maja

  5. Jossif Says:

    Hi Gary, I wonder if you know how to launch Instruments with test scripts from console? Or maybe there is any other solution how to launch test framework done with Instruments automatically so that it can be included to continuous integration process.

  6. John Says:

    No, there does not appear to be any support for continuous integration using UIAutomation. If you want continuous integration, use other tools e.g. the google toolbox for mac.

  7. UI Test Automation | Mobile Solutions Blog Says:

    [...] regressions and make our life easier. However, there is room for improvement, which has been nicely summarized by Air Source. For us, a missing UIALogger.warn function in the JavaScript library was the biggest [...]

  8. swetha Says:

    Hi Maja,

    Use scrollelementtovisible() command it works it scroll to each and every element in the screen

    regards
    swetha

  9. Mark Warren Says:

    @Jossif
    A bit late to reply, but I’ve started on automating an iOS app, and one of the goals is to have a fully automated solution. This obviously required me to automate obtaining a build (from git in our case), building the project (xcodebuild command line) and installing it into the simulator, then running instruments.

    Instruments was the tricky part, but using Applescript I’ve managed to get there. Basically I use Applescript to open a pre-saved trace file, and I use the “system events” command to ensure the trace window is frontmost, then I click the record option from the menu. This initiates the test run. The script then sits and scans the automation results file (using grep), looking for “script complete” or “An exception occurred”, if either are found it selects the record trace command (menu shows Stop Trace, but it seems it’s actually still referred to as Record Trace) to stop the trace, then saves the trace and quits instruments,

    It’s a little clunky, but it does work. Oh, one caveat, make sure your screen does not screensave as otherwise you will need to dismiss the screensaver (use system events to quit the ScreenSaverEngine process if it’s running) as instruments won’t be able to gain focus.

  10. Rogier Festen Says:

    Great article,

    Thanks for the tips on how to solve the “Element not visible” issue.

    Rogier

  11. Matthew Johnson Says:

    @Mark, are you willing to share the scripts you’ve put together to fully automate this process?

  12. Mark Warren Says:

    @Matthew,
    I’ll need to check wether I’m able to share the scripts due to company policy (the usual “Anything you develop is owned by the company” contract clause).

  13. Matthew Johnson Says:

    @Mark, thanks for checking into it. I’m working on figuring this out but seeing your scripts would definitely help save me time! Feel free to email me at matthew.johnson@capella.edu. I promise not to share them if that helps! :)

  14. UI Test Automation | Synyx Weblog Says:

    [...] regressions and make our life easier. However, there is room for improvement, which has been nicely summarized by Air Source. For us, a missing UIALogger.warn function in the JavaScript library was the biggest [...]

  15. Alex Funduianu Says:

    Hi guys – this CI integration stuff is like so damn hard to get to.
    @Mark
    I subscribe to what Matthew asked for – It would sooo nice to have the scripts or screenshots with scripts:)

    @airsource – you should show more tweaks you guys did – how did you detect and handle the current state of the app with regard to any/all of your scripts

  16. Alex Funduianu Says:

    @Mark – I have a big question: your pre-saved trace file remembers the last .js script you have loaded so, did you like integrate all your tests in just one java script file? I really need some help with these apple scripts so please respond on alex_vabtan@yahoo.com if you’re willing to give some help on this.

    @airsource – sorry for off-topic.

  17. Balaji Says:

    Is UIAutomation support the data driven logic. Because I want to define all the data in some external file like .txt/js and then I will use the same script with multiple data combination testing.
    It will be great, if get some suggestion on this.

  18. Peter Says:

    Hey, great post. But has anyone looked into the command line tool for instruments and tried to run a UIAutomation trace? I have tried it but have found no way to execute the javascript file. Like telling instruments from the command line which js file to run.

    If anyone here has figured it out, do lemme know. Would be of great help.

  19. Richie Says:

    This post details how to run UIAutomation from the command line. Hope it helps. http://lemonjar.com/blog/?p=69

  20. Amanda Says:

    Great article!
    @Mark
    I’m learning how to automate Ipad app, and using notepad++ as my JavaScript editor, but there is no auto-complete function, so it is difficult to write script. which JavaScript editor do you prefer for Mac?
    Same as Alex Funduianu, I also want to know how to integrate all your tests in just one java script file. please respond on lijing914@msn.com. It will be great, if get some suggestion on this.

    Amanda

  21. @ashok Says:

    This is regarding dragFromToForDuration function, it is not working.
    Basically after scrolling on an iPad and when i see certain images or section visbile, during the record of instrument dragFromToForDuration function is displayed with coordinates. But when i playback, it is not working or recognising and hence i cannot scroll down and view the images? Please help me in this regard?

  22. Solde Says:

    Solde…

    [...]The Air Source » Blog Archive » UI Automation on the iPhone[...]…

  23. Manikanta Says:

    Hi All,

    I had a bunch of script files for my app and now I would like to integrate these UIAutomation test files (*.js) in Hudson and trigger the scripts from there while building the application. I don’t have any clue for doing this , please share solution if any had done already.

    Thanks in advance.

Leave a Reply