Home >
In part one of this article, we demonstrated how to create some basic and simple unit tests using Fluint. Those work well for very basic situations, but even an average Flex application will use events and have asynchronous aspects. This article will build on the last and demonstrate how to test systems with these concepts in mind. You will learn to write unit tests that wait for events to occur before continuing and whose success or failure may depend on something that happens sometime in the future.
In the next article, you will move past unit tests and begin to explore the concept of integration testing, where, unlike unit testing, you no longer test the smallest pieces but rather combinations of units that need to work together. With an understanding of asynchronous and event based tests plus a new syntax to develop test sequences, you will be able to quickly and effectively write tests for UIComponents and larger aspects of a Flex or AIR application.
Prerequisites
- Adobe Flex Builder 3
- Basic Knowledge of Flex and ActionScript
- Reading “Automated Testing and You, Self-Help for the Flex Developer: Part 1”
Understanding Event-Based Synchronous and Asynchronous Tests
The test you wrote in the previous article was a simple synchronous test. You called the absoluteValue() method and it returns the result immediately. You then verified that result.
Unfortunately, the world isn’t such an easy place. I’m a fan of sandwiches, so let’s take my local deli as an example. The process is pretty simple: You walk up to the cashier. They ask your name and what you want. When your sandwich is ready, your name is called. It’s a real world example of an event-based system. You listen for your name. They call it when the time is appropriate.
However, there is a slight divergence in the internals of this process based on other conditions. If the deli is very busy, the cashier hands off your order to another individual who begins making the sandwich. The cashier then charges you for your purchase. At some point in the future when the sandwich maker is done, the cashier calls your name, you retrieve the sandwich, and you eat it.
If the deli is not busy, you still provide your name and order, but the cashier immediately begins making the sandwich. When it is complete, they call your name (which might be silly as they have done nothing else but make your sandwich since you last spoke) and charge you for your purchase. You then eat.
The first example is an asynchronous event-based model. You place the order, play and then wait an unknown period of time for the event (calling your name) to occur. The second is still an event based model, but it happened synchronously. You order, but the sandwich ready event occurs before the process continues. The net result is the difference in when you paid in relation to when you received your sandwich.
The world of Flex and AIR isn’t much different. The first example below shows a truly asynchronous event-based situation:
function handleEvent( event:Event ):void {
trace("Handling Event");
}
var blah:HTTPService();
blah.addEventListener( ResultEvent.RESULT, handleEvent );
blah.send();
trace("End of code block");
When this code runs, the trace statement “End of code block” will appear in the console. Then, at some point in the future, the “Handling Event” method will occur.
The next example below shows a synchronous event-based situation:
function handleEvent( event:Event ):void {
trace("Handling Event");
}
var blah:TextInput = new TextInput();
addChild( blah );
blah.addEventListener( FlexEvent.VALUE_COMMIT, handleEvent );
blah.text = "Mike";
trace("End of code block");
In this example, the trace statement “Handling Event” will occur before the statement “End of code block”. That is because the text input dispatches a valueCommit event immediately after the text changes, not at some point in the future. This example is event-based but synchronous.
When testing with Fluint, you will treat all event-based scenarios as asynchronous. Fluint will internally handle the differences and ensure that your tests still resolve accurately.
Writing your first Asynchronous Test
Most testing frameworks demonstrate their ability to handle asynchronocity using timers. We will not depart from this standard as it is an easily understandable way to demonstrate asynchronous events. To create an asynchronous test for a timer, you will create a timer in the setUp() method, start it in your test method and destroy it in your tearDown(). Unlike the examples in the previous article, your assertion will happen in a result handler.
Creating a test method, case and suite
- 1) Open the project you started in the previous article.
- 2) Create a new directory in your src folder named asyncSuite
- 3) Inside of the asyncSuite folder, create a folder named tests
- 4) Inside of the tests folder, create a new ActionScript class named TestTimer using net.digitalprimates.fluint.tests.TestCase as the superclass.
- 5) Import two timer classes for use in this test case:
import flash.events.TimerEvent; import flash.utils.Timer;
- 6) Define a new private var named timer of type Timer.
protected var timer:Timer; - 7) Create a method inside of the class that overrides the protected setUp() method. After calling super.setUp(), instantiate a new Timer() instance, that will wait one thousand milliseconds and only execute once. The timer needs initial values when created, however, these will be modified later.
override protected function setUp():void { super.setUp(); timer = new Timer( 1000, 1 ); }
- 8) Create another method that override the protected tearDown() method. After calling the super.tearDown(), stop the timer and set the timer variable to null.
override protected function tearDown():void { super.tearDown(); if ( timer ) { timer.stop(); } timer = null; }
- 9) Create another public method named testTimerSingle() which returns null.
Normally a test method is responsible for executing code and making an assertion. If that assertion is not true, the test method fails. Stated in the negative, if a test method does not make an untrue assertion, it passes. This poses a serious problem for asynchronous tests as the test cannot pass or fail immediately, but rather must wait for a result to occur.
This problem is solved through the use of a class named an AsyncHandler. An AsyncHandler instance waits for something to occur (or for a timeout to elapse) before declaring the test complete and deciding if it passes or fails.
- 10) Inside of the testTimerSingle method, create a new variable named handler of type Function and set it equal to the result of a call to the asyncHandler() method of the TestCase class.
var handler:Function = asyncHandler( handleSingleResult, 2000, null, handleShouldNotFail );
The asyncHandler() method accepts four parameters. The first is the method to call if the intended event occurs in a timely manner. The second is how long to wait for that event before declaring this test over. The third is arbitrary data which can be passed along with the test, and the fourth is the method to call if you do not receive the intended result before the timeout.
- 11) Next, set the delay property of the timer instance to 1000, indicating you want it to broadcast an event in 1 second.
- 12) Add an event listener to the timer, listening to the TIMER_COMPLETE event. When this event occurs, you want it to call the handler you created in step 10. Pass false, 0 and true for the remaining three parameters of the addEventListener() method to indicate that you want to use a weak reference.
timer.addEventListener(TimerEvent.TIMER_COMPLETE, handler, false, 0, true );
- 13) Call the start() method the timer. Your final test method should look like the following.
public function testTimerSingle() : void { var handler:Function = asyncHandler( handleSingleResult, 2000, null, handleShouldNotFail ); timer.delay = 1000; timer.addEventListener(TimerEvent.TIMER_COMPLETE, handler, false, 0, true ); timer.start(); }
- 14) Create a new method named handleSingleResult() that takes two arguments. The first is named event of type Event. Second, is named passThroughData of type Object. This method will be called if the timer event occurs before the timeout elapses.
protected function handleSingleResult( event:Event, passThroughData:Object ):void { }
The event object in this case is the timer event. The passThroughData argument will contain whatever data is passed into the third argument of the asyncHandler() method. In this case it is a null.
- 15) Create a new method named handleShouldNotFail() that takes one argument named passThroughData of type Object. Once again, this is argument would contain any data passed into the asyncHandler() method. This method will be called if the timer event does not occur before the specified timeout.
protected function handleShouldNotFail( passThroughData:Object ):void { }
- 16) Inside of the handleShouldNotFail() method, call the fail() method. The fail method takes a message to be displayed in the test result.
protected function handleSingleResult( event:Event, passThroughData:Object ):void { fail('Timeout Reached'); }
- 17) In the asyncSuite directory you created earlier, create a new ActionScript class named AsyncSuite designating net.digitalprimates.fluint.tests.TestSuite as the super class.
- 18) Add an import statement to the top of this class for the test case you created above:
import asyncSuite.tests.TestTimer; -
19) In the constructor of the AsyncSuite class, use the addTestCase() method of the TestSuite super class to add a new instance of your TestTimer class.
public function AsyncSuite() { addTestCase( new TestTimer() ); }
The AsyncSuite now contains a single test case, which in turn has a single method. However, in a real test environment, your suites may have hundreds of cases, each with dozens or hundreds of methods.
- 20) The final step to before executing your test is to add your new test suite to the test runner’s queue. To do this open the FlexTestRunner.mxml file and find the startTestProcess() method. This method is responsible for creating an array and passing it to the startTests() method of the testRunner.
- 21) Add a new instance of the AsyncSuite class to the array
protected function startTestProcess( event:Event ) : void { var suiteArray:Array = new Array(); suiteArray.push( new MathSuite() ); suiteArray.push( new AsyncSuite() ); testRunner.startTests( suiteArray ); }
If you run your FlexTestRunner.mxml file at this point, you should see three tests execute and pass: Two from the MathSuite and one from the AsyncSuite.
Using Pass Through Data
PassThroughData is a way to make asynchronous tests more dynamic. Any type of object can be passed to the asyncHandler() method. Later that object will be available in the success or failure handlers. To demonstrate this concept, you will create a new test that allows the timer to repeat multiple times and ensures it has repeated the right number of times by the time the test completes.
Creating a test method, case and suite- 1) Inside of the TestTimer class, create a new public method named testTimerMutliple() that returns null.
- 2) As the first line of this new method, create a new variable named obj of type Object and instantiate it.
- 3) Create a repeatCount property on this object and set it equal to 4.
- 4) Next, create a new asyncHandler as you did above, but instruct it to call the handleMultipleResult() method on a successful event. Further, pass this new obj to the passThroughData property of the asyncHandler() call.
public function testTimerMultiple() : void { var obj:Object = new Object(); obj.repeatCount = 4; var handler:Function = asyncHandler( handleMultipleResult, 2000, obj, handleShouldNotFail ); }
- 5) Next, set the delay property of the timer instance to 50 milliseconds.
- 6) Then set the repeatCount property to the obj.repeatCount.
- 7) Now add an event listener to the timer, referencing the handler you created above.
- 8) Call the start method of the timer.
public function testTimerMultiple() : void { var obj:Object = new Object(); obj.repeatCount = 4; var handler:Function = asyncHandler( handleMultipleResult, 2000, obj, handleShouldNotFail ); timer.delay = 50; timer.repeatCount = obj.repeatCount;; timer.addEventListener(TimerEvent.TIMER_COMPLETE, handler, false, 0, true ); timer.start(); }
- 9) Next, create a new method named handleMultipleResult() that accepts an event object and the passThroughData.
protected function handleMultipleResult( event:Event, passThroughData:Object ):void { }
- 10) Finally, call the assertEquals() method, making the assertion that the currentCount property of the timer is equal to the repeatCount of the passThroughData. In other words, that the timer ran as many times as you specified it should.
protected function handleMultipleResult( event:Event, passThroughData:Object ):void { assertEquals( timer.currentCount, passThroughData.repeatCount ); }
If you execute the test runner at this point, it will show four tests executed and four successes. If you wish, vary the amount of times that each timer will take to execute and the amount of time each test will wait for a response. It will allow you to simulate failures and see how the test runner reacts.
Next Steps
This article introduced the concept of asynchronous unit tests. In future article, I will cover integration and functional testing. In the meantime, you can learn more about the advanced features of Fluint at http://fluint.googlecode.com.






















Hi, this looks great! Just what I was talking about over lunch, as it happens.
I notice it can run headless, can you confirm it would run in Linux Server environment? Or would we need to setup an X-Windows session or something?
We run Cruise Control on Centos5 (no UI).
Thanks,
Chris
As far as headless, the answer is yes and no. AIR itself isn't intended to run in truly headless environment, so, our headless switch just means we don't render anythign to the screen, not that there isn't a video card required.
That said, here is a great page about how someone a gentleman named Matthew Wilson managed to make this work in a truly headless environment. It is about dpUInt (which was the pre-release name of Fluint) but totally applicable:
http://groups.google.com/group/dpuint-discussion/web/using-dpuint-with-headless-servers
Mike
Thanks Mike!
Thanks for doing this series, unit and integration testing in Flex/AIR is something I've been looking into for our company.
Just wanted to point out a small typo in step (16) - you say to call fail in handleShouldNotFail but then you show it in the function for handleResult.
These tutorials have been excellent, thanks so much. I am likewise looking into testing for our company!
I was wondering though, what are the best ways to test user interfaces using Fluint, both UIComponents and MovieClips. I have gone through the loginform example on the googlecode site but an example dealing with dynamically adding and removing display objects would be very helpful. Any suggestions on how to do this?
Best,
Lance