Anda di halaman 1dari 14

Adobe Developer Connection / Flex Developer Center /

Test Driven Development using Flash


Builder 4 and FlexUnit
by Elad Elrom

Requirements
Prerequisite knowledge
Experience building Flex applications is
recommended.

Required products
Flash Builder (Download trial)

Sample files
flashbuilder_tdd_source.zip (6317 KB)

User level
All

elromdesign.com/blog
Content
Test Driven Development overview
Creating a test suite and test case in
Flash Builder 4
Implementing TDD techniques using
Flash Builder 4
FlexUnit 4 metadata
Created
29 November 2009
Page tools
Share on Facebook
Share on Twitter
Share on LinkedIn
Print
Was this helpful?
Yes
No
By clicking Submit, you accept the
Adobe Terms of Use.
Thanks for your feedback.

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 ECMAScriptcompliant 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.
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.
Applying TDD techniques using FlexUnit 4

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).

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.

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.
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.

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.

Write a failed unit test


You are ready to start writing test code. 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
{
}

}
}

We can now write our first method. We are creating a calculator so we need to creating methods that the calculator helper
utility will use to perform all the calculations, well start with the add method. Create a test method by starting with the
[Test] metadata, than put code. The code I am creating wont pass the test until I will implement the method, see below:

[Test]
public function testAdditionMethod():void
{
Assert.fail("Test method not yet implemented");
}

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.

Figure 6. Running the FlexUnit tests

2. In the Run FlexUnit Tests dialog box, select the test cases (see Figure 7).
3. Click OK.

Figure 7. Select all available TestCases and TestSuites

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

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");

Figure 9. FlexUnit Results view

Update the testAdditionMethod stub to produce a fail result:

var result:Number = CalculatorLogicHelper.additionMethod(5,5);


Assert.assertEquals(result,10);

We write the test before the actual code. In our case we figure out that we have a static method that will calculate adding
two numbers and than we should be able to assert to ensure the addition calculation was performed. Once you save the
document you get compile time errors. See figure 11. This is a good thing since the compiler gives you instructions of what
you need to do next, which is create the helper class and 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).

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" minHeig
<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(), "C
}
]]>
</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).

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.

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.
Also download the article I published on Flash&Flex Magazine about Flexunit 4 with Test Driven Development. The
example is much more advanced and will compliment this article: http://ffdmag.com/magazine/991-flash-and-mobileapps-iphone-and-ipod-touch

Tutorials & Samples


Tutorials
Flex mobile performance checklist
Flex and Maven with Flexmojos Part 3:
Journeyman
Migrating Flex 3 applications to Flex 4.5
Part 4

Samples
Twitter Trends
Flex 4.5 reference applications
Mobile Trader Flex app on Android Market

Anda mungkin juga menyukai