The Blog

Apr 22, 2008

dpuint - Unit Testing for Flex 2 and 3 

by Maxim Porges @ 9:27 PM | Link | Feedback (0)

Since we were in need of a unit testing suite for Flex apps at Highwinds, I started looking in to FlexUnit and some of the other options.

While I was doing this, Tommy suggested I check out dpuint. No, that's not a typo - it's called dpuint to stand for "digital primates Unit and Integration".

Turns out it's pretty cool. I never got to mess with FlexUnit much, but based upon the documentation there seems to be numerous improvements in dpuint over FlexUnit. I got some tests up and running today with testing asynchronous processing and they all worked like a treat. The test runner for dpuint also showed me the execution time for each test, which was nice to have available (I don't think this is a distinguishing feature for the library - I just wanted to bring it up).

One of the things that was really refreshing to see was a great tutorial for the framework on the Google Code site. If you have used any of the xUnit testing frameworks before, you will feel right at home; the only thing you will need to learn is how to wire up asynchronous tests, which doesn't take long. FYI, I would recommend looking at the Cairngorm example (look for "Testing Delegates") since the basic timer example they offer earlier in the tutorial only really shows you how to test an asynchronous call that fires an event. This is certainly a useful feature and worth knowing about, but not appropriate for testing service-level classes like gateways to services (which is what I was doing).

As a quick sample, here's a few methods from the beginnings of a test suite I wrote to wrap my head around the framework. This is intended to be a quick reference to the framework rather than an example of great unit testing practices, so please keep that in mind.

public class RawDataPointManagerTest extends TestCase
{
private var fixture : RawDataPointManager;

override protected function setUp() : void
{
fixture = new RawDataPointManager();
}

public function testFetch() : void
{
var passThroughData : Object = null;
var responder:IResponder =
asyncResponder(new TestResponder(handleFetchComplete, handleTimeout),
3000,
passThroughData);

fixture.fetch(new DataSetCriteria(), responder);
}

private function handleFetchComplete(event : ResultEvent, passThroughData : Object) : void
{
var resultArray : Array = event.result as Array;
assertEquals(45000, resultArray.length);
}

private function handleTimeout(info : Object, passThroughData : Object) : void
{
fail(FaultEvent(info).message.toString());
}
}

To explain, RawDataPointManager.fetch() accepts a DataSetCriteria object, which calls a web service behind the scenes, returning asynchronously as all service calls do in Flex. fetch() also accepts an IResponder so that it can be used by multiple clients that might process the result events using different responder functions for success and failure.

The asyncResponder call invokes a method in the TestCase class from which my suite extends. This produces an IResponder reference that will wait 3000 milliseconds (a wait value that is specified in the method call) before assuming that the call failed. If the call fails, the handleTimeout method will be invoked (it's being passed in by reference through the TestResponder); on success, handleFetchComplete is called instead.

In either instance, you can pass along "pass through data" with each asynchronous call. This is usually data you will use to assert equality, a boolean value, or whatever else your test is looking for. This info gets passed along to the success and fault handlers so that they can maintain state easily. In my example I am passing null - I just wanted to leave a reminder in there for me to look at tomorrow when I start writing less contrived tests. This pass through data is especially useful when you want to (for example) set a text field to a value and make sure that the value stuck. There's a good example of a use case for this in the tutorial for testing UIComponent. Obviously, you'd probably be testing more useful functionality than validating a text field's input was set, but you get the point.

In my assertion, I know that I have a sandboxed test data set that always returns 45,000 members in an array. Using this sandboxed data I can write very specific tests to make sure that the data is being retrieved and processed as expected. In handleFetchComplete(), I am currently just validating that the expected number of data rows is being processed and returned to the caller.

Hopefully this will give you enough info to see how easy it is to use the dpuint framework for testing asynchronous method calls. Please check out their web site for more info and let me know your experiences.