Requirements      
Prerequisite knowledge Required products
Sample files User level
Experience building Flex applications is recommended.
Flash Builder 4 (Download trial)
 
Intermediate
       

 

In this article I present some of the basics for getting started with Test Driven Development (TDD) using Flash Builder 4 and FlexUnit.
 
As Flash applications become more dynamic and complex, they become more difficult to maintain and scale, particularly when business requirements change throughout the course of development. These challenges are significant and they are common in all types of development, including mobile, web, and desktop applications.
 
Consider a scenario in which you need to make changes to a large application to meet new business requirements. How do you know if the small changes you made broke other parts of the application? How can you ensure that the code is bullet-proof, especially if you are not the person who wrote it?
 
For software engineers, this problem is neither new nor confined to a specific platform. Java and ASP developers have been challenged with the same issues and have found Test Driven Development (TDD) a useful technique for creating applications that can be easily maintained.
 
Flash has grown a long way from a small animation tool. The Adobe Flash Platform now comprises an ECMAScript-compliant programming language as well as methodologies common to other programming languages that are necessary for building large dynamic applications. In fact, Adobe and many other companies have found that TDD solves many of the challenges that developers face in their development cycles every day.
 
I have noticed that while many developers have heard of TDD, they are reluctant to use TDD because they are unfamiliar with it and afraid that using TDD will increase development time.
 
In my personal experience, I have found that using TDD correctly doesn't increase development time when used on projects for which it is appropriate. In fact, TDD can reduce development time and simplify application maintenance. I've also found that I can use FlexUnit on existing applications and apply TDD methods to any framework out there in some way or another.
 
It is important to note that TDD is applicable even when there is a separate Quality Assurance (QA) department that performs formal testing. TDD helps developers deliver more solid code, enabling QA to focus on other tasks, including testing the user interface and creating the use cases that they need to test.
 

 
Test Driven Development overview

So what is TDD anyway? Test Driven Development is a software development technique in which programmers writing failed test that will define the functionality before writing the actual code.
 
Note that using FlexUnit you often write the test after writing the code since many times you will be adding tests to existing code.
 
In Extreme Programming teams work on the development of dynamic projects with changing requirements and a development cycle that includes TDD for writing the test before the code itself. Note that TDD is not the complete development cycle; it is only part of the Extreme Programming (XP) development paradigm. Preparing the tests before writing the code helps a development team to demonstrate their work in small steps, rather than making the customer or other stakeholders wait for the complete result.
 
Moving in small increments also makes it easier to accommodate changing requirements and helps ensure that your code does what it needs to do, and nothing more. It is important to mention that the focus of the TDD technique is to produce code and not to create a testing platform. The ability to test is an added benefit.
 
TDD is based on the idea that anything you build should be tested and if you are unable to test it, you should think twice about whether you really want to build it.
 
Test Driven Development cycle

Figure 1. Test Driven Development cycle

 
The TDD process consists of six simple steps (see Figure 1):
 
  1. Add test - The first step is to understand the business requirements, think of all possible scenarios, and add tests based on those scenarios. If the requirements are not clear enough, you can raise questions right away instead of when the software is completed and will require much more effort to change.
  2. Write failed unit test - This phase ensures that the test unit itself is working correctly. It will not pass, since you haven't written any code.
  3. Write code - During this phase you write the code in the simplest, most effective way to ensure that the test passes. There is no need to include any design patterns, think about the rest of the application, or clean up the code. Your goal is simply to pass the test.
  4. Test Passed - Once you write all the code and the test passes you know that your test meets all the business requirements and you can share the work with the customer or other members of the team.
  5. Refactor - Now that the test is completed and you confirmed that it meets the business requirements, you can ensure that the code is ready for production by replacing any temporary parameters, adding design patterns, removing duplicate code, and creating classes to do the job efficiently. Ideally once the refactor phase is completed, the code undergoes code review, which is essential to ensure that the code is in good shape and complies with the company's coding standards. Following the refactoring and code review, the test should be run again to ensure that nothing was broken in the process.
  6. Repeat - When the unit test is completed, you can move to the next unit test and share the code with the customer or other members of the team.
 
Using FlexUnit for testing Flex and ActionScript projects
FlexUnit is a unit testing framework for Flex and ActionScript 3.0 applications and libraries. It offers functionality similar to JUnit, a Java unit testing framework. FlexUnit is used in many internal Adobe projects and is open source.
 
Flash Builder 4 provides integrated FlexUnit support, and allows you to create the scaffolding of the test unit automatically, saving you time, eliminating the need to create the same classes over and over again, and ensuring the use of best practices.
 
There are two versions of FlexUnit: FlexUnit 0.9 (also referred to as FlexUnit 1) and FlexUnit 4 (also referred to as FlexUnit 4). This article covers FlexUnit 4.
 
To use FlexUnit in previous versions of Flex Builder you had to download the FlexUnit SWC file and include it in your project. Flash Builder 4 includes five SWCs automatically once you create tests. The following SWCs will be added under your project's Referenced Libraries:
 
  • flexunit_0.9.swc
  • hamcrest-1.0.2.swc
  • flexunit-core-flex-4.0.0.2-sdk3.5.0.12783.swc
  • flexunitextended.swc
  • FlexUnitTestRunner_rb.swc
These SWCs include all the APIs for FlexUnit 0.9, FlexUnit 4, the test runner, and other libraries. The SWCs are maintained as part of the Flex 4 SDK so there is no need to download FlexUnit or add them manually. They will be added automatically once you add the tests.
 

 
Creating a test suite and test case in Flash Builder 4

In this section you will use Flex Builder 4 with FlexUnit 4 to create a test suite and test case.
 
Create a test suite class
To illustrate how to use FlexUnit in Flash Builder, I will use a simple application that calculates numbers. Follow these steps to create the application and add a test suite:
 
  1. Choose File > New > Flex Project to create the project.
  2. For the Project Name, type CalculatorApplication.
  3. Click Finish.
  4. To create a test suite, choose File > New > Test Suite Class (see Figure 2).
Creating a new Test Suite Class in Flash Builder 4

Figure 2. Creating a new Test Suite Class in Flash Builder 4

 

5. In the New Test Suite Class dialog box, name the class CalculatorTestSuite.
6. Select New FlexUnit 4 Test (see Figure 3).
7. Click Finish.
Creating a New Test Suite Class named CalculatorTestSuite

Figure 3. Creating a New Test Suite Class named CalculatorTestSuite

A test suite is a composite of tests. It runs a collection of test cases. During development you can create a collection of tests packaged into test suite and once you are done, you can run the test suite to ensure your code is still working correctly after changes have been made.
 
Note: Although in Extreme Programming you are encouraged to write tests before creating the code (and ideally that's how you should work), in real life there are many times where you will find yourself writing the tests after the code. Such decisions are made case by case, and it is OK to adjust the methodology to fit your workflow.
 
Flash Builder 4 added the following class under the flexUnitTests folder:
 
package flexUnitTests { [Suite] [RunWith("org.flexunit.runners.Suite")] public class CalculatorTestSuite { } }
The Suite metadata tag indicates that the class is a suite. The RunWith tag instructs the test runner to execute the tests that follow it using a specific class. FlexUnit 4 is a collection of runners that will run a complete set of tests. You can define each runner to implement a specific interface. You can, for example, specify a different class to run the tests instead of the default runner built into FlexUnit 4.
 

 
Add a test case class

Create the Test Case class:
 
  1. Choose File > New > Test Case Class.
  2. Select New FlexUnit 4 Test.
  3. Type flexUnitTests as the package.
  4. Type CalculatorLogicTester as the name.
  5. Click Next.
Creating a new Test Case class

Figure 4. Creating a new Test Case class

Note: In FlexUnit 1 you can choose to generate setUp() and tearDown() stubs. These stubs are called automatically when the test case starts (setUp) and ends (tearDown). They can be used to set up information and events before the test starts and clear information and events to ensure you don't have any memory leaks. In FlexUnit 4 you can define these methods using the metadata tags [Before] and [After], as you will see later in this article.
 
In the next dialog box (see Figure 5) you select the methods you would like to test.
 
  1. Select additionMethod.
  2. Click Finish.
Selecting the methods to test

Figure 5. Selecting the methods to test

 
Write a failed unit test

You are ready to start writing test code. Open CalculatorLogicTester.as and notice that the test method for additionMethod has been created for you. In FlexUnit 1, each method you create must start with "test", to enable the test runner to recognize the method. As a result, the method name was changed to testAdditionMethod. In FlexUnit 4, method names do not need to start with "test"; instead they are recognized by the [test] metadata, so feel free to refactor the method names. Here is the generated code:
 
package flexUnitTests { public class CalculatorLogicTester { [Before] public function setUp():void { } [After] public function tearDown():void { } [BeforeClass] public static function setUpBeforeClass():void { } [AfterClass] public static function tearDownAfterClass():void { } } }
Lastly, remember to add the test case you would like to test into the CalculatorTestSuite test suite. Add the line in bold below to CalculatorTestSuite.as:
 
package flexUnitTests { [Suite] [RunWith("org.flexunit.runners.Suite")] public class CalculatorTestSuite { public var calculatorLogic:CalculatorLogicTester; } }
Build the project. To run the application, follow these steps:
 
  1. Click the compile icon and choose FlexUnit Tests (see Figure 6) or choose Run > Run > FlexUnit Tests.
Running the FlexUnit tests

Figure 6. Running the FlexUnit tests

2. In the Run FlexUnit Tests dialog box, select the test cases (see Figure 7).
3. Click OK.
Select all available TestCases and TestSuites

Figure 7. Select all available TestCases and TestSuites

 
4. When the application finishes, review the test status results in your browser. (see Figure 8).
 
FlexUnit test results in the browser

Figure 8. FlexUnit test results in the browser

 
5. Close the browser window.
6. Examine the test results in the FlexUnit Results view (see Figure 9).
 
As you can see, the test failed because you had the following code in CalculatorLogicTester.as:
 
Assert.fail("Test method Not yet implemented");
FlexUnit Results view

Figure 9. FlexUnit Results view

Edit additionMethod() to return zero since we are not writing the code yet. Make the method static and the class final as shown below:
 
package com.elad.calculator.utils { public final class CalculatorLogicHelper { public static function additionMethod(value1:Number, value2:Number):Number { return 0; } } }
Also update the testAdditionMethod stub to produce a fail result:
 
var result:Number = CalculatorLogicHelper.additionMethod(5,5); Assert.assertEquals(result,10);
The assertEquals method will fail because the method additionMethod code hasn't been implemented yet. Run the application and see the fail result (see Figure 10). A message shows the problem (Error: expected: <10> but was <0>) and the failed method (testAdditionMethod).
 
FlexUnit Results view showing failed method

Figure 10. FlexUnit Results view showing failed method

 
What's happening under the hood?
Under the application folder structure you can find the files CalculatorLogicTester.as, CalculatorTestSuite.as, and FlexUnitCompilerApplication.mxml (see Figure 11).
 
The CalculatorApplication folder structure

Figure 11. The CalculatorApplication folder structure

Take a look at FlexUnitApplication.mxml:
 
<!-- This is an auto generated file and is not intended for modification. --> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:flexui="flexunit.flexui.*" creationComplete="onCreationComplete()"> <fx:Script> <![CDATA[ import flexUnitTests.CalculatorLogicTester; public function currentRunTestSuite():Array { var testsToRun:Array = new Array(); testsToRun.push(flexUnitTests.CalculatorLogicTester); return testsToRun; } private function onCreationComplete():void { testRunner.runWithFlexUnit4Runner(currentRunTestSuite(), "CalculatorApplication"); } ]]> </fx:Script> <fx:Declarations> <!-- Place non-visual elements (e.g., services, value objects) here --> </fx:Declarations> <flexui:FlexUnitTestRunnerUI id="testRunner"> </flexui:FlexUnitTestRunnerUI> </s:Application>
As you can see there is a GUI called FlexUnitTestRunnerUI, which is similar in functionality to the FlexUnit TestRunner class in FlexUnit 0.9. Essentially, the application adds the entire test and runs the test in the UI.
 
In FlexUnitApplication.mxml you can see that FlexUnit uses the FlexUnit 4 runner:
 
private function onCreationComplete():void { testRunner.runWithFlexUnit4Runner(currentRunTestSuite(), "CalculatorApplication"); }
However, the framework is flexible, so developers can create and use their own runners, but still use the same UI. In fact, currently there are runners for FlexUnit 1, FlexUnit 4, Fluint, and SLT.
 
The test runner in FlexUnit is a UI component that will create an instance of the test suite and allow you to add all the tests you would like to run. The test runner will also display the test information in your browser.
 
Note: There is currently a test runner for Flex applications but not for pure ActionScript applications. You can, however, create a Flex project to test your pure ActionScript code or use Ant tasks. See http://opensource.adobe.com/wiki/display/flexunit/CI+ReadMe for more information.
 
In addition to the files you see in the Project Navigator there are more under the surface, including:
 
  • _FlexUnitApplication_FlexInit-generated.as
  • _FlexUnitApplication_mx_managers_SystemManager-generated.as
  • _FlexUnitApplication-binding-generated.as
  • _FlexUnitCompilerApplication_FlexInit-generated.as
  • _FlexUnitCompilerApplication_mx_managers_SystemManager-generated.as
  • FlexUnitApplication-generated.as
  • FlexUnitApplication-interface.as
  • FlexUnitCompilerApplication-generated.astem
  • FlexUnitCompilerApplication-interface.as
  • FlexUnitTestRunner_properties.as
These classes handle tasks such as the binding, styles, and definitions for FlexUnit.
 
Note: The bin-debug/generated package holds all files that get created by the MXMLC compiler and are normally invisible to you. To see them, select the project, then right-click and select Properties. Under Flex Compiler in Additional Compiler Arguments add the following option: -keep-generated-actionscript or -keep-generated-actionscript=true.
 

 
Implementing TDD techniques using Flash Builder 4

You are now ready to proceed with step 3 of the TDD process.
 
 
Write code
Now that the test failed you can write code to pass the test. The code you write should be the minimum code to get the test to pass. Add additionMethod() to return the sum of two numbers:
 
Create the utility class by following these steps:
 
  1. Choose File > New > Package.
  2. Type com.elad.calculator.utils for the name.
  3. Click Finish.
  4. Select the new com.elad.calculator.utils package in Package Explorer.
  5. Choose File > New > ActionScript Class.
  6. Type CalculatorLogicHelperfor the name.
  7. Click Finish.
package com.elad.calculator.utils { public final class CalculatorLogicHelper { public static function additionMethod(value1:Number, value2:Number):Number { var retVal:Number = value1+value2; return retVal; } } }
 
Test passed
Now without any changes to the testAdditionMethod test method it passes and you will get a green light, meaning the test has succeeded (see Figure 12).
 
FlexUnit Results view showing a successful test

Figure 12. FlexUnit Results view showing a successful test

 
Refactor code
Now that your test passed you can refactor the code in order to get it ready for production. For instance, you may add a design pattern to replace a block of if..else statements.
 
In this case there is nothing to refactor since the code is so simple.
 
 
Rinse and repeat if desired
You can continue to create the unit tests for the subtraction, multiplication, and division methods. The complete code is below:
 
package com.elad.calculator.utils { public final class CalculatorLogicHelper { public static function additionMethod(value1:Number, value2:Number):Number { var retVal:Number = value1+value2; return retVal; } public static function subtractionMethod(value1:Number, value2:Number):Number { var retVal:Number = value1-value2; return retVal; } public static function multiplicationMethod(value1:Number, value2:Number):Number { var retVal:Number = value1*value2; return retVal; } public static function divisionMethod(value1:Number, value2:Number):Number { var retVal:Number = value1/value2; return retVal; } } }
The complete unit test class code is in the CalculatorLogicTester.as file included with the sample files for this article.
 
 
Assertion methods
So far you have used only the assertEquals assertion method in the test cases. There are, however, many other assertion methods (see Table 1).
 
Assertion method
 
Meanings
 
assertEquals
 
Asserts that two values are equal.
 
assertContained
 
Asserts that the first string is contained in the second one.
 
assertNotContained
 
Asserts that the first string is not contained in the second one.
 
assertFalse
 
Asserts that a condition is false.
 
assertTrue
 
Asserts that a condition is true.
 
assertMatch
 
Asserts that a string matches a regular expression (regexp).
 
assertNoMatch
 
Asserts that a string doesn't match a regexp.
 
assertNull
 
Asserts that an object is null.
 
assertNotNull
 
Asserts that an object is not null.
 
assertNotUndefined
 
Asserts that an object is defined.
 
assertUndefined
 
Asserts that an object is undefined.
 
assertStrictlyEquals
 
Asserts that two objects are strictly identical.
 
assertObjectEquals
 
Asserts that two objects are equal.
 
Table 1. Available assertion methods in FlexUnit 1 and FlexUnit 4.
 
To use an assertion method, pass a string message and two parameters to compare. The string is the message to be used if the test fails.
 
For example:
 
assertEquals("Error testing the application state", state, 1);
If you omit the message string, you'll get the default message.
 
In the editor, type Assert. to see code hints for the available assertion methods.
 
 
The Hamcrest assertion method
In addition to the standard assertions FlexUnit 4 supports new methods thanks to the Hamcrest library, which is based on the idea of matchers. Each matcher can be set to match conditions for your assertions.
 
Here is an example taken from Drew Bourne's project that tests whether one number is close to (within a specified threshold) of another number:
 
package org.hamcrest.number { import org.hamcrest.AbstractMatcherTestCase; public class CloseToTest extends AbstractMatcherTestCase { [Test] public function comparesValuesWithinThreshold():void { assertMatches("close enough", closeTo(1, 0.5), 1.5); assertDoesNotMatch("too far", closeTo(1, 0.5), 1.6); } [Test] public function hasAReadableDescription():void { assertDescription("a Number within <0.1> of <3>", closeTo(3, 0.1)); } } }
 
Putting it all together
Now that you have a class that can handle all the basic operations of a calculator and you have test cases for the methods, you can implement a simple calculator application (see Figure 13). Take a look at the sample code in CalculatorApplication.mxml, which uses the utility class.
 
The calculator application

Figure 13. The calculator application

 
FlexUnit 4 metadata

The FlexUnit 4 framework is based on metadata tags. So far you've seen [Suite], [Test], and [RunWith]. Here are some other common metadata tags:
 
[Ignore] - Causes the method to be ignored. You can use this tag instead of commenting out a method.
 
[Before] - Replaces the setup() method in FlexUnit 1 and supports multiple methods.
 
[After] - Replaces the teardown() method in FlexUnit 1 and supports multiple methods.
 
[BeforeClass] - Allows you to run methods before a test class.
 
[AfterClass] - Allows you to run methods after a test class.
 
 
Useful metadata you can use in your test cases
The calculator example was intentionally simple to help you get up to speed with TDD. This section covers a practical overview of FlexUnit metadata that you can use on more complex projects.
 
To begin, create a new FlexUnit 4 Test Case class and name it FlexUnitTester. Copy the code from the FlexUnitTester.as sample file into the new class.
 
The method following the Before metadata will be run before every test, and the method following After tag will be run after every test:
 
[Before] public function runBeforeEveryTest():void { // implement } [After] public function runAfterEveryTest():void { // implement }
The following example demonstrates the expected attribute of the Test metadata. The rangeCheck method creates a new Sprite object. The code will produce a successful test, because the child at index 1 doesn't exist and thus the code causes an exception during runtime.
 
[Test(expected="RangeError")] public function rangeCheck():void { var child:Sprite = new Sprite(); child.getChildAt(1); }
Here is another example that shows an expected assertion error. The testAssertNullNotEqualsNull Test expects an AssertionFailedError error. The test will fail, because the assertEquals method will succeed (since null equals null).
 
[Test(expected="flexunit.framework.AssertionFailedError")] public function testAssertNullNotEqualsNull():void { Assert.assertEquals( null, null ); }
In FlexUnit 1 you had to comment out code to ignore a method that you didn't want to test any more. The Ignore metadata tag makes it easier to skip a method.
 
[Ignore("Not Ready to Run")] [Test] public function methodNotReadyToTest():void { Assert.assertFalse( true ); }
If you want to set the order of Test, Before, or After methods, you can add the order attribute, like this:
 
[Test(order=1)] public function checkMethod():void { Assert.assertTrue( true ); }
 
Asynchronous tests
If you've worked with FlexUnit 1 in the past, you know that it isn't always easy to create asynchronous tests and test event driven code. I often found myself modifying existing classes just to accommodate FlexUnit or creating tests in a hackish way. One of Fluint's biggest advantages is the ability to accommodate multiple asynchronous events. FlexUnit 4 incorporated Fluint functionality to support enhanced asynchronous tests, including asynchronous setup and teardown. This feature can be used by every test stub. To see this feature in action, create a new Test Case Class, name it AsynchronousTester, and copy in the code from the AsynchronousTester.as sample file.
 
This example tests a service call. The testServiceRequest() function makes a service call to retrieve an XML file that is included with the project. The test sets a timeout to specify the amount of time to wait before it fails. The ResultEvent fires before the timeout is reached and the test succeeds. In the second test, testFailedServiceRequest(), the service request asks a file that doesn't exist. Since the check is for the fault event the test passes, as expected.
 
The last test is for a scenario in which multiple asynchronous calls are made.
 
 
Theories
FlexUnit 4 introduces the concept of theories. A theory, as the name suggests, allows you to create a test to check your assumptions about how a test should behave. This type of test is useful when you have to test a method that can return large or even unbounded values. The tests take parameters (data points) and these data points can be used in conjunction with each test. You can use this functionality to check if the results are within a specified range.
 
To see how it works, create a new Test Suite Class, name it FlexUnit4TheorySuite, and copy in the code from FlexUnit4TheorySuite.as, which includes the following:
 
[Theory] public function testNumber( number:Number ):void { assumeThat( number, greaterThan( 0 ) ); assertThat( number, instanceOf(Number) ); }
In this case, the theory checks that the number is greater than zero and verifies the variable type.
 
 
Testing user interfaces
Although it may be disputable if UI testing is part of TDD, in FlexUnit 4 you have the ability to create tests that can check user interfaces and MXML components.
 
FlexUnit 4 includes the concept of sequences, and you can create sequences that include all the operations you would like to perform on a UI.
 
The FlexUnit4CheckUITester.as sample file shows an example of this functionality.
 
The setUp method creates a component that holds a button. Next, instead of adding the component to the view, it calls UIImpersonator.addChild().
 
The testButtonClick method is a simple async test that sets a handler and dispatches a mouse event. The handler handleClickEvent simply checks the event string type.
 
The second test, testButtonClickSequence, includes a sequence that sets the button's label name. Next, SequenceWaiter instructs the sequence to wait until the button click event is dispatched. Before the event is dispatched, the code calls addAssertHandler( handleButtonClickSqEvent, passThroughData ).
 
The handleButtonClickSqEvent method handles the event and compares the button label of the test with the pass through data.
 

 
Where to go from here

This article covered unit testing and Test Driven Development using Flash Builder 4 and FlexUnit, including some of the new features in the FlexUnit 4 framework. There has been a great deal of interest in TDD (and unit testing in general) recently, because it leads to applications that are easier to scale and maintain and less prone to errors.
 
After learning how to create FlexUnit test suites and test cases, I hope you are inspired to use TDD on your mobile, web, and desktop Flash applications, and write better, more scalable, and more reusable code.
 
For more information on TDD and FlexUnit, see chapter 14 of AdvancED Flash on Devices: Mobile Development with Flash Lite and Flash 10 and chapter 2 of AdvancED Flex 4. For more information on integrating FlexUnit with Continuous Integration (CI) server see FlexUnit4AntTasks.
 
You may also want to check out my article on InsideRIA and the FlexUnit 4 feature overview.