|
QuickTutorialForZeroDotFive
FlexMonkey Tutorial.
Updated for FlexMonkey 0.5. Running the Sample ApplicationThis tutorial will walk through the features of flexmonkey by using it to test a simple contact manager application. You can start the application now by opening this link in a new window. (Download the source and binaries.) The sample consists of the following components:
MonkeyContacts.mxml contains no Flexmonkey-specific code. Any application can be tested with Flexmonkey without requiring any source code modifciations. However, we do need to link the FlexMonkeyUI swc into our application's swf. The swf file included with the sample download already has flexmonkey linked in. (Learn more about configuring testing). When you launch the MonkeyExample.mxml application, its application window opens, and the FlexMonkey window opens automatically (Learn more about how Flexmonkey starts itself). You can move and resize the Flexmonkey window as necessary to work with the contact manager app beneath it.
Recording a ScriptClick the Record toggle button at the top of the screen. Now play around with the contact manager, which although primitive, offers a variety of UI gestures you can record. In addition to typing in the form, you can edit rows after they've been added to the grid, and the Phone Type field has a ComboBox itemEditor. !Flexmonkey will faithfully record all UI events (many events such as mousemoves are filtered by default). Flexmonkey displays each recorded event as a command in the Command List. Clicking anywhere in the Flexmonkey window will stop recording.
Playing Back a ScriptClick the Play button on the Command List window. Each recorded action will be replayed. By default, Flexmonkey pauses for a half second between commands to give each UI action time to complete. You can play subsets of commands from the list by selecting them in the grid and then hitting Run. If no rows are selected, Flexmonkey will play all the commands. Generating a FlexUnit TestClick on the FlexUnit TestCase tab to view the generated source code. Two methods are generated, the first calls FlexMonkey.runCommands passing it an array of FlexCommands generated from the Flexmonkey Command List. The second method contains a stub method where you would write actual validation code specific to what's being tested by the command sequence. You can copy and paste this code into a FlexUnit TestCase and add it to any FlexUnit TestSuite.
Two methods are generated, the first calls FlexMonkey.runCommands passing it an array of FlexCommands generated from the Flexmonkey Command List. The second method contains is a stub where you would write actual validation code specific to verifying the expected outcome of the script (as explained below). You can copy and paste this code into a FlexUnit TestCase and add it to any FlexUnit TestSuite. Click the Show completeTestCase checkbox and a complete FlexUnit TestCase will be generated.
After you copy and paste the generated code into an ActionScript source file and add testing validations, you can rebuild the app (including the Flexmonkey swc) and restart. The sample swf download already has a FlexUnit test linked in, and its source can be found in the test.FlexUnitTests.as source file. You can run the test by clicking on the FlexUnit Runner Let's take a look at the source: package test
{
import com.gorillalogic.flexmonkey.commands.CallCommand;
import com.gorillalogic.flexmonkey.commands.CommandRunner;
import com.gorillalogic.flexmonkey.commands.FlexCommand;
import com.gorillalogic.flexmonkey.core.MonkeyEvent;
import com.gorillalogic.flexmonkey.core.MonkeyUtils;
import com.gorillalogic.flexmonkey.ui.FlexMonkey;
import flash.display.DisplayObject;
import flexunit.framework.Assert;
import flexunit.framework.TestCase;
import mx.collections.ArrayCollection;
import mx.controls.DataGrid;
import mx.controls.DateField;
import mx.events.FlexEvent;
[Mixin]
public class FlexUnitTests extends TestCase
{
public static function init(root:DisplayObject) : void {
root.addEventListener(FlexEvent.APPLICATION_COMPLETE, function():void {
FlexMonkey.addTestSuite(FlexUnitTests);
});
}
// FlexUnit test method
public function testSomething():void {
var cmdRunner:CommandRunner = new CommandRunner();
cmdRunner.addEventListener(MonkeyEvent.READY_FOR_VALIDATION, addAsync(verifySomething, 10000));
cmdRunner.runCommands([
new FlexCommand("inName", "SelectText", ["0", "0"], "automationName"),
new FlexCommand("inName", "Input", ["Fred"], "automationName"),
new FlexCommand("inName", "ChangeFocus", [false, "TAB"], "automationName"),
new FlexCommand("inType", "Open", ["null"], "automationName"),
new FlexCommand("inType", "Select", ["Work", "1", "0"], "automationName"),
new FlexCommand("inType", "Type", ["TAB", "0"], "automationName"),
new FlexCommand("inType", "ChangeFocus", [false, "TAB"], "automationName"),
new FlexCommand("inPhone", "Input", ["555 555 5555"], "automationName"),
new FlexCommand("Add", "Click", ["0"], "automationName"),
new FlexCommand("grid", "Select", ["Fred | *Work* | 555 555 5555", "1", "0"], "automationName"),
new FlexCommand("grid", "Click", ["0"], "automationName")]);
}
// Called after commands have been run
private function verifySomething(event:MonkeyEvent):void {
var comp:DataGrid = MonkeyUtils.findComponentWith("grid","id") as DataGrid;
Assert.assertEquals("Fred", ArrayCollection(comp.dataProvider).getItemAt(0).name);
Assert.assertEquals("Work", ArrayCollection(comp.dataProvider).getItemAt(0).type);
}
}
}
Let's examine the code. First, we need a CommandRunner to run our FlexMonkey commands. var cmdRunner:CommandRunner = new CommandRunner(); Next, we have to tell FlexUnit that the test is asynchronous. After Flexmonkey completes running a command list, he dispatches a READY_FOR_VALIDATION event. We use FlexUnit TestCase's addAsync method to tell FlexUnit that our READY_FOR_VALIDATION event handler's completion signals that the test has passed (unless of course one of our assertions fails). (Learn more about asynchrounous testing with FlexUnit). cmdRunner.addEventListener(MonkeyEvent.READY_FOR_VALIDATION, addAsync(verifySomething, 10000); The second argument to addAsync tells FlexUnit to fail the test if the event is not received within 10 seconds. The handler passed to addAsync is where you would typically write your validation code: private function verifySomething(event:MonkeyEvent):void {
var comp:DataGrid = MonkeyUtils.findComponentWith("grid","id") as DataGrid;
Assert.assertEquals("Fred", ArrayCollection(comp.dataProvider).getItemAt(0).name);
Assert.assertEquals("Work", ArrayCollection(comp.dataProvider).getItemAt(0).type);
}You can easily access UI components using the MonkeyUtils.findComponentWith() Method. /** * Find the first component with the specified property/value pair. If a container is specified, then * only its children and descendents are searched. The search order is (currently) indeterminate. If no container is specified, * then all components will be searched. If the prop value is "automationID", then the id is resolved directly without searching. */ public static function findComponentWith(value: String, prop:String="automationName", container:UIComponent=null):UIComponent You can then examine component properties to determine if the script has produced the expected state using standard FlexUnit assertions. Assert.assertEquals("Fred", ArrayCollection(comp.dataProvider).getItemAt(0).name);
Assert.assertEquals("Work", ArrayCollection(comp.dataProvider).getItemAt(0).type); The testSomething method adds a row to the table and we're checking that the columns in its first row have the expected name and phone type, "Fred" and "Work". You must register your test class with FlexUnit. A convenient way to do this is defining a static initializer using the Flex Mixin metadata tag. [Mixin]
public class FlexUnitTests extends TestCase
{
public static function init(root:DisplayObject) : void {
FlexMonkey.addTestSuite(FlexUnitTests);
}The init() method will be called when the class is loaded. You must explicitly reference your test class when you compile, using the "includes" compiler option: -includes test.FlexUnitTests and don't forget you still need the include-libraries option as well: -includes test.FlexUnitTests -include-libraries "/YOUR_FLEX_INSTALLATION/frameworks/libs/automation_agent.swc" "/YOUR_FLEX_INSTALLATION/frameworks/libs/automation.swc" "/YOUR_APP_LIBS/FlexMonkeyUI.swc" (Learn more about linking libraries) Running FlexUnit TestsAfter you've finished editing your TestCase, rebuild and restart your app. Click on the FlexUnit Runner tab and click the Run button. Any tests that have been registered will be run.
Understanding Recorded CommandsThe FlexAutomationAPI dispatches AutomationRecordEvents containg an event name, an array of argument objects, and a reference to the target component. Flexmonkey checks for the existence of a property value it can use to identify the component on playback. Properties are searched in the following sequence:
Many (most?) Flex components generate an automationName using some logically identifying value. A Button, for example, has a default automationName set to its label. The FlexAutomationAPI generates an AutomationID for every component, so every recorded command is guaranteed to provide an identifier value. automationID's are however derived by serializing the component tree from the component back to the root, and is therefore a very long and very unreadable string. If you want your tests to be readable and (human) modifiable, you will want to assign components id's or automationNames. Alternatively, you can modify a command to use a specific property-value pair, as described below. Modifying Recorded CommandsClicking the Enable editing checkbox will allow you to modify the recorded commands. You can change the Value and Property columns to any property-value pair you want to use to identify the component. Flexmonkey will return the first component found containing the pair, and the search order is indeterminate. You can limit the search to the children of a component identified by the Container Value and Container Property columns. If no Container values are specified, the search considers all components. (Specifically, all "raw" descendants of the SystemManager). Understanding FlexCommandsFlexmonkey generates FlexCommands from your recorded commands, and you can of course easily change or create them in source code, or generate command sequences programmatically. /** * Generate a flex event for the component identified by the specified * property-value pair. * * @param value the value to search for * @param name the name of the event to generate * @param args the event args * @param prop the property to search for the specified value. Defaults to automationName. * @param containerValue if specified, only children/descendants of the container having this property value will be searched. * @param containerProp The property to search for the specified containerValue. If no containerProperty-containerValue pair is specified, all components will be searched. */ public function FlexCommand(value:String, name:String, args:Array = null, prop:String = "automationName", containerValue:String = null, containerProp:String = null) For example: // Click the "Add" button 100 times.
for (i:int=0; i < 100; i++) {
var cmd:FlexCmmand = new FlexCommand("Click","Add",["0"],"label"]);
cmds[i] = cmd;
}
runCommands(cmds);Adding Pauses to your ScriptAll commands are executed asynchronously, and it can sometimes be necessary to force a command to wait additional time before executing during playback. You can do this by adding PauseCommands to the Array you pass to FlexMonkey.runCommands(). /** * Pause for the specified delay time * @param delay in milliseconds */ public function PauseCommand(delay:int) By default, Flexmonkey pauses 5 seconds between command invocations during playback. Here's an example of adding a pause to a script: cmdRunner.runCommands([
new FlexCommand("inName", "SelectText", ["0", "0"], "automationName"),
new PauseCommand(2500),
new FlexCommand("inName", "Input", ["Fred"], "automationName")]);
}Calling Functions from Your ScriptSometimes you might want to execute some actionscript code between commands, for example, to test some assertions. You can insert a function call between commands with a CallCommand: /** * Call a function during playback * @param func the function to call */ public function CallCommand(func:Function) Here's an example of calling a function within a script: cmdRunner.runCommands([
new FlexCommand("inName", "SelectText", ["0", "0"], "automationName"),
new CallCommand(checkSomething),
new FlexCommand("inName", "Input", ["Fred"], "automationName")]);
Getting helpCheck out the discussion board at http://groups.google.com/group/flexmonkey. Contributing to FlexmonkeyWhy don't you?
|
Sign in to add a comment