Unit testing Flex

Posted: October 22, 2007

This is part 3 of my exploration into Flex. In part 1 I wrote a simple weather forecast application. In part 2 I refactored the non-UI code into separate ActionScript classes. In this part I discuss unit testing those classes using FlexUnit. In short, FlexUnit allows you to run unit tests from the command line.

Overview

Here’s an overview of what’s required to write tests for your Flex project:

  • Download flexunit and related libraries
  • Copy libraries into your project
  • Write your tests
  • Update build.xml to compile and run tests
  • Run: ant test

Add test libraries to your project

Download the following three libraries:

FlexAntTasks and FlexUnitOptional were written by Peter Martin. These two files provide a way to invoke FlexUnit from the command line. The stock FlexUnit distribution requires that you manually load up the test SWF in your browser and run it. Peter’s modules allow you to invoke the tests from ant. Read more about it here

Unzip these 3 zip files into a directory on your machine. I’ll refer to this dir as $SW Copy 3 files into your testlib directory in your project:

cp $SW/flexunit/bin/flexunit.swc testlib
cp $SW/FlexUnitOptional/dist/FlexUnitOptional.swc testlib
cp $SW/FlexAntTasks/dist/FlexAntTasks.jar testlib

Write the tests

You need two files:

  • FlexUnitRunner.mxml file that bootstraps the test. This becomes the test SWF
  • Unit test class(es)

My directory structure looks like this:

proj/
     src/
         mxml/    [contains main mxml file for project]
         classes/ [contains ActionScript classes I want to test]
         test/
              FlexUnitRunner.mxml  [defines list of test classes]
              bitmechanic/
                      util/
                           DateUtilTest.as  [unit test]

FlexUnitRunner.mxml was cribbed from Peter Martin’s example project:

<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
                xmlns:flexunit="flexunit.flexui.*"
                creationComplete="onCreationComplete()">

    <mx:Script>

        <![CDATA[
             import bitmechanic.util.DateUtilTest;

             import flexunit.junit.JUnitTestRunner;
             import flexunit.framework.TestSuite;

             [Bindable]
             private var runner : JUnitTestRunner;

             private function onCreationComplete() : void
             {
                status.text = "Please wait running test suite...";

                runner = new JUnitTestRunner();
                runner.run( createSuite(), onTestComplete );
             }

             private function onTestComplete() : void
             {
                status.text = "Finished running test suite.";
                fscommand( "quit" );
             }

             private function createSuite() : TestSuite
             {
                var ts : TestSuite = new TestSuite();

                // Add all tests classes here
                ts.addTest( new TestSuite( DateUtilTest ) );

                return ts;
             }
        ]]>
    </mx:Script>

    <mx:Label id="status" />

</mx:Application>

And here’s my unit test, DateUtilTest.as:

package bitmechanic.util
{
    import flexunit.framework.TestCase;
    import bitmechanic.util.DateUtil;

    public class DateUtilTest extends TestCase
    {

        /**
         * Constructor.
         * @param methodName the name of the individual test to run.
         */
        public function DateUtilTest( methodName : String = null )
        {
            super( methodName );
        }

        public function testConversion() : void
        {
            var str:String = "2001-10-03T13:03:30-07:00";
            var date:Date = DateUtil.convertSqlServerDate(str);

            // months are 0 based.  9 == october
            assertEquals(9, date.getMonth());
            assertEquals(3, date.getDate());
            assertEquals(2001, date.getFullYear());
            assertEquals(13, date.getHours());
            assertEquals(3, date.getMinutes());
            assertEquals(30, date.getSeconds());
        }

        public function testFailedConversion() : void
        {
            var date:Date = DateUtil.convertSqlServerDate("2001-10-03 13:0");

            // what happens?
            // this test will fail when we run it below..
            assertNull(date);
        }
    }
}

Modify build.xml

First we need to tell ant about our new flexunit task. Add this to the top of your build.xml:

<taskdef resource="com/adobe/ac/ant/tasks/tasks.properties"
         classpath="${basedir}/testlib/FlexAntTasks.jar"/>

Second we need to compile a test SWF based on your unit tests. You could add this to your compile task or test task:

<exec dir="." failonerror="true" executable="${mxmlc.bin}">
    <arg line="--strict=true -library-path+=lib -library-path+=testlib
               --file-specs src/test/FlexUnitRunner.mxml
               -output build/WeatherTest.swf" />

</exec>

Next we need to tell ant to run the test:

<target name="test" depends="compile">
  <flexunit timeout="0"

            swf="build/WeatherTest.swf"
            toDir="testout"
            haltonfailure="true" />
</target>

Run the tests

$ ant test
Buildfile: build.xml

compile:
     [exec] Loading configuration file /Users/jamescooper/tools/flex_sdk_2/frameworks/flex-config.xml
     [exec] /Users/jamescooper/dev/flex_weather/lib/Weather.swc (261724 bytes)
     [exec] Loading configuration file /Users/jamescooper/tools/flex_sdk_2/frameworks/flex-config.xml
     [exec] build/Weather.swf (276436 bytes)
     [exec] Loading configuration file /Users/jamescooper/tools/flex_sdk_2/frameworks/flex-config.xml
     [exec] build/WeatherTest.swf (136050 bytes)

test:
 [flexunit] opened server socket
 [flexunit] accepting data from client
 [flexunit] sent policy file
 [flexunit] accepting data from client
 [flexunit] Running bitmechanic.util.DateUtilTest
 [flexunit] Tests run: 2, Failures: 0, Errors: 1, Time Elapsed: 0 sec
 [flexunit] end of test
 [flexunit] end of test run

BUILD SUCCESSFUL
Total time: 14 seconds

Woops, an error!

Notice that one of the tests failed. The details are written to a XML file in “testout”. Better go fix that..