Features

Features of FlexUnit 4.x

Each feature is marked with a (4.0) or (4.1) to indicate the version of FlexUnit exposing this feature.

Flex or ActionScript 3  (4.0)

FlexUnit is offered with or without dependencies on the Flex libraries. If you are testing a Flex project, you may find some of the extensions available by using the Flex version useful, however, the ActionScript only build will work with any ActionScript 3.0 project.

Test Metadata (4.0)

Test cases are now marked with a piece of metadata named [Test]. Your tests no longer need any special name (prefixed with test, etc.) Also, the need for specific Test and Suite classes disappears. Your classes no longer need to inherit from any framework class. Here are a couple of sample tests.

[Test]
public function addition():void {
   assertEquals(12, simpleMath.add(7, 5));
}
 
[Test]
public function subtraction():void {
   assertEquals(9, simpleMath.subtract(12, 3));
}

Because your test classes no longer inherit from a class in the FlexUnit framework, you will notice that the assert functions you used in the past (assertEquals, assertTrue) are now referenced as static functions of the Assert class; more on the new ways of asserting later in this post.

Before and After (4.0)

Sometimes you need to setup your test environment (or fixture) for your tests. In the example above, you need to ensure your simpleMath reference exists before the tests are run. In previous versions of FlexUnit and Fluint you could override a setup() or teardown() method to accomplish this goal. FlexUnit 4 introduces Before and After metadata which accomplishes a similar goal. Any methods marked with Before will be run before each test method. Any methods marked with After will be run after each test method. This also means you can have multiple methods that run before or after the test.

[Before]
public function runBeforeEveryTest():void {
   simpleMath = new SimpleMath();
} 
 
[Before]
public function alsoRunBeforeEveryTest():void {
   simpleMath1 = new SimpleMath();
} 
 
[After]
public function runAfterEveryTest():void {
   simpleMath = null;
   simpleMath1 = null;
}

If you do choose to use multiple before or after, you can control the order that these methods execute using an order parameter. So, for example [Before(order=1)], [Before(order=2)].

BeforeClass and AfterClass (4.0)

Methods marked with Before and After will run before and after each test method respectively. BeforeClass and AfterClass allow you to define static methods that will run once before and after the entire test class. Like Before and After, you can define multiple methods for BeforeClass and AfterClass and can control the order.

[BeforeClass]
public static function runBeforeClass():void {
   //run for one time before all test cases
} 
 
[AfterClass]
public static function runAfterClass():void {
   // run for one time after all test cases
}

Exception Handling (4.0)

Test metadata can also have an expects parameter. The expects parameter allows you to indicate that a given test is expected to throw an exception. If the test throws the named exception it is considered a success, if it does not, it is considered a failure. This prevents us from having to write tests wrapped in a try block with an empty catch.

[Test(expects="flash.errors.IOError")]
public function doIOError():void {
   //a test which causes an IOError }Or
[Test(expects="TypeError")]
public function divisionWithException():void {
   simpleMath.divide( 11, 0 );
}

Ignore (4.0)

Ignore metadata can be added before any test case you want to ignore. You can also add a string which indicates why you are ignoring the test. Unlike commenting out a test, these tests will still appear in the output reminding you to fix and/or complete these methods.

[Ignore("Not Ready to Run")]
[Test]
public function multiplication():void {
   assertEquals(15, simpleMath.multiply(3, 5));
}

Async (4.0)

In previous versions of FlexUnit it was difficult to have multiple asynchronous events and to test code that was event driven but not always asynchronous. Fluint provides enhanced asynchronous support including asynchronous setup and teardown, but every test carried the overhead of the asynchronous code to facilitate this feature. FlexUnit 4 allows the developer to specify which tests need asynchronous support using the async parameter. When provided, the async parameter enables the full asynchronous support provided by Fluint for that particular test. When the async parameter is specified you may also specify an optional timeout for the method.

[Before(async,timeout="250")]
public function setMeUp():void {
}
 
[After(async,timeout="250")]
public function allDone():void {
}
 
[Test(async,timeout="500")]
public function doSomethingAsynchronous():void {
   //Async.proceedOnEvent( testCase, target, eventName );
   //Async.failOnEvent( testCase, target, eventName );
   //Async.handleEvent( testCase, target, eventName, eventHandler );
   //Async.asyncHandler( testCase, eventHandler );
   //Async.asyncResponder( testCase, responder );
}

In addition to the async parameter, there are several new Async methods, each of which can also take individual timeouts, handlers and passThroughData.

Hamcrest (4.0)

Earlier I alluded to new assertions. Thanks to the hamcrest-as3 project we now have the power of Hamcrest assertions. Hamcrest is based on the idea of matchers which match conditions for your assertions. For example:

[Test]
public function testGreaterThan():void {
   assertThat( 11, greaterThan(3) );
}
 
[Test]
public function isItInHere():void {
   var someArray:Array = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
   assertThat( someArray, hasItems("b", "c") );
}

For more information on hamcrest:

Suites (4.0)

FlexUnit 4 has a concept of test suites just like FlexUnit and Fluint. Test suites are just a collection of classes that represent tests or even other suites. A suite is defined by the [Suite] metadata. However, in FlexUnit 4, you also need to provide one additional piece of metadata called [RunWith] which instructs the test runner to execute the tests defined below using a specific class. The [RunWith] metadata forms the basis of the extensibility layer which will be discussed shortly.

[Suite]
[RunWith("org.flexunit.runners.Suite")]
public class FlexUnitIn360 {
   public var t1:BasicMathTest;
   public var t2:MyTheory;
}

The test cases and any nested test suites, are simply defined as public variables. There is no need to instantiate them or mark them in any other way. FlexUnit’s test suite code understands how to recursively parse this class and find the tests.

User Defined Metadata Parameters (4.0)

It’s often extremely useful to include additional pieces of information which are relevant to your development process when defining tests. So, for example, you might want to provide a detailed description of what a test is supposed to prove. This description could then be displayed if the test fails. Or perhaps you would like to note that a test relates to a give issue number in your bug tracking system. These custom parameters are stored by the framework when encountered during the test and can be used in reporting the success or failure later.

[Test(description="This one makes sure something works", issueID="12345")]
public function checkSomething():void {
}

Parameterized Testing (4.1)

There are times when you need to repeat a test across a series of data points. Imagine a class that does a complex calculation based on input values. It is likely that you would want to test hundreds or thousands of different input values, checking each of their expected outputs to feel comfortable that this class was behaving properly. Or, perhaps you have a whole series of components which implement an interface and you wish to verify that setting the ‘x’ property on each of those objects dispatches and expected event or updates a given property. These are both cases where Parameterized testing can simplify your life.

Parameterized testing in FlexUnit works by allowing you to specify arguments to be passed to a constructor, or arguments which will be passed to a method. Here is a quick sample of the approach:

Constructor (JUnit Style):

[Parameters]
public static function data2():Array {
  return [ [ 3, 6 ], [ 4, 8 ], [ 5, 10 ], [ 6, 12 ] ];
}
 
private var _input:int;
private var _expected:int;
 
public function TestParameterized( param1:int, param2:int ) {
  _input = param1;
  _expected = param2;
}
 
[Test]
public function doubleTest():void {
  assertEquals(_expected, _input*2);
}

Method (TestNG Style):

public static function dataThree():Array {
  return [ [ 0, 1, 1 ], [ 1, 2, 3 ], [ 2, 4, 6 ] ];
}
 
[Test(dataProvider="dataThree")]
public function addTwoValuesTest( value1:int, value2:int, result:int ):void {
  assertEquals( value1 + value2, result );
}

In this case, there are parameters defined by static properties containing arrays or arrays. The framework iterates through the array, calling your methods with the parameters of each inner array. Parameterized testing can be combined with the asynchronous testing mentioned above.

Theories, Datapoints and Assumptions (4.0)

At first glance, theories are similar to parameterized testing; they also allow a developer to create methods and a constructor that accept parameters. A theory is different in how the data is defined and in the way a test fails. A theory is akin to a mathematical theory wherein any failure means the whole theory is bad. Further, theories are often reversible. For instance, a valid theory might entail testing a math class by first multiplying two number and then dividing the product by the first number. If your code is valid, you should get the second number back. Testing this across a potentially infinite number of values is the real of a theory. In the parameterized example, you saw that sets of data were passed to methods. In theories, all data of a given type (for instance integers) is aggregated and every possible combination is passed to the methods. Theories are a little harder to understand at first, but their benefits are far reaching. If you are up for reading a bit of theory, this document will introduce the ideas . Here is a quick sample of using these new techniques:

[DataPoints]
[ArrayElementType("String")]
public static var stringValues:Array = ["one","two","three","four","five"];
 
[DataPoint]
public static var values1:int = 2;
[DataPoint]
public static var values2:int = 4;
 
[DataPoints]
[ArrayElementType("int")]
public static function provideData():Array {
   return [-10, 0, 2, 4, 8, 16 ];
}
 
[Theory]
public function testDivideMultiply( value1:int, value2:int ):void {
   assumeThat( value2, greaterThan( 0 ) );
 
   var div:Number = simpleMath.divide( value1, value2 );
   var mul:Number = simpleMath.multiply( div, value2 );
 
   assertEquals( mul, value1 );
}      
 
[Theory]
public function testStringIntCombo( value:int, stringValue:String ):void {
   //call some method and do something
}

In this case, there are datapoints defined by static properties as well as method calls. The framework introspects the datapoints and uses this data combined along with any type specified in the ArrayElementType metadata. This information is used in combination with the theory method signatures to call each theory for each possible combination of parameters. As of 4.1, Theories can be combined with the asynchronous testing mentioned above.

RunWith (4.0)

FlexUnit 4 is nothing more than a set of runners combined to run a complete set of tests. A runner is a class that implements a specific interface and understands how to find, execute and report back information about any tests in a given class. Each time a new class is encountered, FlexUnit 4 works through a list of possible runners and attempts to identify the correct one to execute the tests contained in the class.

The RunWith metadata allows you to override the default choice made by the framework and specify a different class to act as the runner. This feature allows developers to write entirely new types of runners, with support for new features, which can work directly with the existing framework and report their results back through the same interface.
In the case of the suite, you are instructing the framework to run this class in a specialized runner that simply finds the correct runner for all of the classes it contains.

[RunWith("org.flexunit.runners.Suite")]

Adapters (4.0)

Using the flexibility of the multiple runners discussed above, the new FlexUnit 4 framework has legacy runners built in for both FlexUnit 1 and Fluint tests. This means that FlexUnit 4 is completely backwards compatible; all existing FlexUnit and Fluint tests can be run, and even mixed into suites with FlexUnit 4 tests without any code changes.

Further, supplemental runners are in development for FUnit and several other testing projects

Rules (4.1)

When creating large suites of unit tests, you could find yourself duplicating complex setup code many times. Rules offer the ability to factor this logic into a separate class that will be invoked before and after each test method call. Allowing you to alter how a method is called, perform additional setup or even examine and change the result of a Test runner.

The first target use case for this feature was the construction and verification of Mock objects and more information will be available shortly on the Wiki.

User Interface Facade (4.0)

FlexUnit 4 provides a UIComponent testing facade which allows you to add or remove components from the display list. This allows you to accurately test component methods in a real runtime state. This feature creates a foundation for other projects to extend into areas of integration and functional testing without the need for extensive rewrites or modifications.

[Before(async,ui)]
public function setUp():void {
   //Create a textInput, add it to the testEnvironment. Wait until it is created, then run tests on it
   textInput = new TextInput();
   Async.proceedOnEvent( this, textInput, FlexEvent.CREATION_COMPLETE, 200 );
   UIImpersonator.addChild( textInput );
}

As of 4.1, UIComponent impersonation can now be used with ActionScript only projects as well as Flex projects. Additional documentation on these and other features will become available as the project progresses.

Flash Builder Integration (4.0)

FlexUnit 4 is integrated into the premium edition of Adobe Flash Builder 4, allow execution and reporting of unit tests with the IDE.

Continuous Integration Support (4.0)

Lastly FlexUnit 4 provides an integration tier for Continuous Integration(CI) systems. FlexUnit 4 itself is built via Hudson and reports back the success and failure of all test methods, along with additional information about code metrics and analysis using associated tools. The CI support is configurable and extensible allowing mixed suites of FlexUnit 4, Fluint and FlexUnit 1 tests to be reported back through a single interface.