Channels ▼
RSS

Mobile

Project of the Month: Automating iOS Application GUI Testing With FoneMonkey


For each Cocoa Touch component, FoneMonkey provides a set of commands corresponding to the most common kinds of interactions including touches, keyboard input, swiping, shaking, and rotating. Most commands are recorded as higher-level operations rather than low-level screen interactions. For example, swiping on a UITableView results in the recording of a "Scroll" command with arguments specifying the row to scroll to; and swiping on a UISlider records a "Slide" command with the new value being selected. Commands are inherited by subclasses, and you can override existing commands or even add new ones. For each command, FoneMonkey records the class of the component that was the target of the operation — for example, the UIButton that was tapped, and component-specific arguments. Figure 3 shows some of the details of this process. As the script plays, FoneMonkey simulates user interface gestures and their effects.

Figure 2: Recorded commands are displayed in the console (left); individual commands can be edited (right).

Verification

A key requirement for any testing tool is the ability to check that the execution of the script actually creates the expected results.

FoneMonkey reports test failure if a command references a component that is not currently displayed. You can also add explicit verification steps with FoneMonkey's Verify command, which checks that a component's property value, or any valid property path expression, has some expected value. For example, you could verify that a UITextField's text value is "John Smith" or that a UIButton's labelText.font.fontName value is "Helvetica-Bold".

FoneMonkey waits a half a second before executing each successive command in a script. The FoneMonkey WaitFor command is a variant of the Verify command that retries until its condition becomes true or times out. You use WaitFor to force your script to wait for the completion of operations that can take longer than a half second. For example, if some operation takes anywhere from one to several seconds to complete and then displays a Done button, you could force your script to wait until the button appears before attempting to tap on it.

Although FoneMonkey provides a Pause command that suspends script execution for a specified number of milliseconds, the WaitFor command lets you synchronize your script playback with the actual responses of your application, rather than with estimates for the response time for each command.

Running FoneMonkey Tests

FoneMonkey tests can be saved and later reopened and rerun with the FoneMonkey Console. In addition, FoneMonkey saves OCUnit tests in Objective-C that can be run from Xcode or the command line. FoneMonkey also generates JavaScript-based UIAutomation tests, but these scripts suffer from the current limitations of the UIAutomation framework. If and when the UIAutomation framework improves, FoneMonkey code generation for UIAutomation will likewise improve.

Extending FoneMonkey

FoneMonkey recording and playback can be customized for any UIView subclass by implementing methods from the UIView (FMReady) category. These category methods provide component-specific recording and playback logic for each corresponding class. Together these category extensions provide support for recording and playing most common component interactions. The UIView (FMReady) category implementation provides default recording and playback handling for touches and other simple interactions.

Many components override this default handling by providing their own FMReady category extensions.

The simplest example of overriding a UIView (FMReady) method is providing an identifier for a component by overriding its monkeyID.

Component Identification

As previously mentioned, FoneMonkey identifies the target for each command (for example, the button to tap on) with the string value returned by the monkeyID method. By default, monkeyID returns the component's UIAccessibilityLabel if it has one, and otherwise uses some component-specific default. For example, if a UIButton doesn't have an accessibility label, monkeyID returns the button's label value. If the monkeyID is nil, FoneMonkey generates a monkeyID by concatenating a unique ordinal to the component's class name, for example, "UIButton #1".

To override the default identifier, implement the monkeyID method in a category extension for the component's class. You could instead implement monkeyID in the class itself, but by defining it in a category, you keep your test code segregated from your application code, and can easily exclude all test code from your release build.

For example, to use the value of the variable _someIdentifier as the monkeyID for a custom component called MyView you could implement something like this.

@implementation MyView (FMReady)
NSString* _someIdentifier;  
- (NSString*) monkeyID {
     return _someIdentifier ? _someIdentifier :
         [super monkeyID]; 
}
@end

Conclusion

FoneMonkey can be used for recording and replaying functional tests for native iOS applications. You can find complete FoneMonkey documentation, source code, and binaries at www.gorillalogic.com/fonemonkey.

In the next part of this series, we'll see how you can customize FoneMonkey recording and playback, and we'll take a look under the hood at the various mechanisms FoneMonkey uses for intercepting and simulating user interface interactions on iOS.


Stu Stern is Stu Stern is a Founder and CEO of Gorilla Logic Inc., the developer of FoneMonkey and other tools.


Related Reading

Making Two-Way Tables in iOS

Improving Data Entry with UI Pickers in iOS

The iPhone Isn't Easy


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video