Tag Archives: flexunit

flexunit basics

The following is adapted from a brief tutorial I wrote for our company‘s internal conventions wiki.

And yes, I know the syntax hiliting could be better. I’m looking into switching over to WordPress and a new stylesheet, so hopefully this sort of thing will all be resolved in the nearish future.


FlexUnit

FlexUnit is a powerful unit testing framework for as3/flex2 projects. There are two API’s out there called FlexUnit, one is a SourceForge project and one is official from Adobe. The SF project is ancient and unmaintained, I will be referring to the official API.

FlexUnit also has some competition from the AsUnit project, but seems to be slightly less well suiteded for our needs when testing AS3 projects. It does, however, seem to offer fairly good support for AS2 projects. The two frameworks are generally very similar and switching between them should be fairly painless if necessary as they both evolved from and are modeled after JUnit, the industry standard for unit testing in Java.

FlexUnit is available for download from Adobe Labs. You want the zip file.

The official API documentation for FlexUnit is at http://weblogs.macromedia.com/as_libraries/docs/flexunit.

Warning: If your default run target in Flex doesn’t use the right flash player version/options, it is entirely possible that your failures might not be documented. Run the swf again in the standalone debugging player and you should get the information you missed. I’m not entirely certain why this is happening but I don’t much care at the moment.

Using FlexUnit

To make use of FlexUnit in a Flex 2 project only two bits of prep work need to be done:

  • Add the flexunit.swc file (found in the zip on Adobe’s site) to your project’s library path.
  • Create a test runner application in the project. This is Flex, so remember that applications have to be in the root of the project tree.

An example simple test runner (adapted very slightly from Darron Schall’s howto post):

Test Cases

The way the framework functions is by running a series of test cases. Each test case is an instance of the TestCase class which may or may not do any number of things. Test cases may also be organized into TestSuites which are little more than a list of test cases which will be run in order if desired. TestSuite.add() may accept either individual TestCase instances or TestSuites.

The constructor for a test case accepts one argument, the name of the method to be called when the test is run. Thus it is possible and advisable to create a single class for each group of related tests and instantiate multiple copies, each instructed to run a different test.

When a test runs it counts the results of all assertion methods called. These methods are:

  • AssertEquals()
  • AssertFalse()
  • AssertNotNull()
  • AssertNotUndefined()
  • AssertNull()
  • AssertStrictlyEquals()
  • AssertTrue()
  • AssertUndefined()
  • fail()

Each of the assertX() methods takes two parameters, a string describing the error to describe if the test fails and a value to be tested.

Fail may be used to throw error messages if you want to perform more complex tests than the prefab asserts provide.

A sample test case class:
[as3]
package test {
import flexunit.framework.TestCase;
import flexunit.framework.TestSuite;

public class SomeTest extends TestCase {

// declaring the redundant constructor saves you from having to cast when you construct
public function SomeTest( methodName:String ) {
super( methodName );
}// end: constructor

// this method returns a suite containing all of the tests provided by this class in
// the order they should be run
public static function suite() : TestSuite {
var ts:TestSuite = new TestSuite();

ts.addTest( new SomeTest( "testA" ) );
ts.addTest( new SomeTest( "testB" ) );
return ts;
}// end: suite

public function testA() : void {
AssertTrue( "True does not equal true?!", true );

var tmp:Object;
AssertNull( "Tmp should be null here.", tmp );
tmp = {};
AssertNotNull( "Tmp shouldn’t be null any more.", tmp );
}// end: testA

public function testB() : void {
fail( "This test always fails" );
}// end: testB

}// end: class
}// end: package
[/as3]

Asynchronous Tests

FlexUnit offers good support for asynchronous event-driven test cases. AsUnit apparently has also recently (ie, as of early-mid October, 2006) added this support for AS3 projects.

There are a number of asynchronous methods that tests can call but the actual 1st-party documentation on how to use them is a bit thin. In stead, Adobe refers us to hunt down and comprehend the test cases written for the Mappr API. I’ve attempted a bit of this comprehension. The only one of their test suites to use asynchronous calls is the MapprServiceTest class.

Even in this case, they’re only using one aspect of the functionality. That is the TestCase.addAsync() call. This function looks like it might be enough for what we need, it adds a checkpoint to the queue (and pauses it?) and then returns a proxy function that looks like it should clear the checkpoint and then execute the next function in your list.

The framework looks like it has more robust options than this for writing asynchronous tests, but I believe this is all we need for now.

A sample class that tests loading data from an external source might look something like this:
[as3]
package test
{
import flexunit.framework.TestCase;
import flexunit.framework.TestSuite;
import flash.net.URLLoader;
import flash.net.URLRequest;

public class RemoteXMLTest extends TestCase {
private static var TIMEOUT:int= 10000; // 10 seconds
private var loader:URLLoader;

public function RemoteXMLTest( methodName:String ) {
super( methodName );
}

public static function suite() : TestSuite {
var ts:TestSuite = new TestSuite();
ts.addTest( new RemoteXMLTest("doIt") );
return ts;
}

public function doIt() : void {
var request:URLRequest = new URLRequest("http://somewhere.com");
assertNotNull( "request is null", request );

loader = new URLLoader();
assertNotNull( "loader is null", loader );

loader.addEventListener( "complete", addAsync( onLoaded, TIMEOUT ) );
loader.load( request );
}

public function onLoaded( result:Object ) : void {
// don’t actually have to do anything here, but eh, why not?
assertNotUndefined( "we got no result?", result );
}
}
}
[/as3]