Wednesday, December 14, 2011

Automated Testing with MonoTouch on an iPad

Lately, my work has been focused on a brand new iPad application.  My team consists of around 10 members, and the project, while not containing a single line of code before December 1, will eventually grow to be quite large.  I took it upon myself to ensure that we had a solution for automated testing and continuous integration for the project.  Normally, Apple's tools for automated testing would fit the bill perfectly.  However, we happen to be using MonoDevelop and MonoTouch.  So far, I've enjoyed using these tools greatly.  But there was one issue that made automated testing a huge problem: it is impossible to automatically launch an application on an iPad using any of Xamarin's tools!

Update, 2/17/2012

As of version 5.2.3, Xamarin has added a --launchdev switch to mtouch, allowing you to programmatically launch an application on a device, and potentially making the following steps unnecessary. I am still using them myself, as they give me a way to pass information to my test application on the device, such as the IP address of the build server the device should report back to. But if you don't need that kind of functionality, or have a different method for communicating with your application, --launchdev will certainly make your life easier!

Problem

Xcode can launch applications on an iPad without any user interaction.  MonoDevelop can install applications unattended, but it cannot launch them.  There had to be a way to combine them to automatically launch and execute my unit test application.

What Didn't Work

I tried a number of things along the way that didn't work:

  • Writing a launcher application in MonoTouch based on an alarm clock application, set to go off every minute, that would detect and launch the test application.  The launcher would always be running, except when the the test application was running.  This required bi-direction communication via custom URL schemes, and in the end it proved very finicky and unreliable.
  • Writing a launcher application in MonoTouch based on a music player application.  It would play silent audio in a background thread, as well as detecting the test application and launching it.  PasteBot uses this for there purposes, but I was never able to get it to work.
  • Using mtouch --xcode to compile a built mono .exe version of the test application into an Xcode project, and automatically compiling and launching it with Xcode, manipulated through AppleScript.  In the end, I was never able to get the Xcode project generated by mtouch to compile.

What Did Work

The key to the solution was custom URL schemes.  Custom URL schemes are a way for iOS applications to communicate with each other.  I bound my unit test application to "unit://".  I then created a launcher application with Xcode.  I wrote my applicationDidBecomeActive method like this:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [application openURL:[[NSURL alloc] initWithString:@"unit://run"]];
}

This code tells the operating system to find an application bound to the URL scheme "unit://".  If it finds such an application, it launches it, and, if defined, passes the URL to the handleOpenURL method.  If you need to, you can use this mechanism to do much more than simply open an application.

To launch the launcher application, I had to resort to some AppleScript:

#!/usr/bin/osascript

tell application "System Events"
    tell application "Xcode"
        activate
        open "<path to test app launcher project>"
    end tell
    keystroke "r" using {command down}
end tell

Now, when I run this script, Xcode opens, builds, installs, and runs the launcher application.  When it opens, it immediately sends the openURL method, which triggers the unit test application.  It's a little indirect, but it gets the job done much more simply than anything else I tried.

What's Next

In addition to running unit tests written in MonoTouch, I'm also working on setting up continuous integration using TFS.  Not the easiest thing to accomplish on a Mac, but I'm pretty close.  Stay tuned for updates on how you can get your MonoTouch test results into a format TFS will understand.

No comments:

Post a Comment