Saturnboy
 3.22

Testing async services with Fluint

, , , ,

We’re pretty big on testing at Gorilla Logic, and in the world of Flex that usually means using FlexMonkey to test the UI and using FlexUnit to test the code. Alas, it is a huge pain in the ass to correctly test the many async objects and services inherent in any Flex app with FlexUnit. Enter Fluint, an superior Flex unit tesing framework by the cool guys at digital primates (no relation). Fluint is the heir apparent to take over the unit testing crown from the venerable FlexUnit. So let’s take Fluint and its enhanced async testing support for a spin.

Service Layer

First, assume we have a nice service layer in Flex that talks asynchronously to our backend. Just something simple to start:

public class MyService {
    public function getSomething(result:Function, fault:Function):AsyncToken {
        //call the backend
        var token:AsyncToken = backend.getSomething();
 
        //wire the callbacks to the result
        token.addResponder(new AsyncResponder(result, fault, token));
 
        return token;
    }
}

In this example, our service only has one method, getSomething() that takes two callback functions. It simply calls the backend method, wires up the callbacks (which get called when the backend method returns a result), and returns the token. It is absolutely critical that our callback-powered service method return the AsyncToken. The reason for this will become apparent.

We might use our service like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
        xmlns:mx="http://www.adobe.com/2006/mxml"
        creationComplete="complete()">
 
    <mx:Script>
    <![CDATA[
        import com.saturnboy.services.MyService;
        private var service:MyService;
 
        private function complete():void {
            service = new MyService();
            service.getSomething(resultHandler, faultHandler);
        }
 
        private function resultHandler(result:Object, token:Object=null):void {
            lbl.text = result.result.name;
        }
 
        public function faultHandler(error:Object, token:Object=null):void {
            lbl.text = 'fault';
        }
    ]]>
    </mx:Script>
 
    <mx:Label id="lbl" text="initial" />
</mx:Application>

We make a call our service, and then use the callbacks to alter the UI however we want depending on the result. In common usage, the fact that our service returns an AsyncToken is worthless, it might as well return void. So, why did I say this is critical? Throw Fluint testing into the mix and it’s “Show em what’s behind door number 2, Johnny!”

Fluint Testing

Fluint provides two different async wrapper methods: asyncHandler and asyncResponder. The first allows a test to be wired to an async method by events, the second allows a test to be wired to an async method by a responder. Since the service method we’re trying to test doesn’t throw any events, we’ll need to use the latter. So inside a Fluint test case, we have our test method:

public function testGetSomething():void {
    //call service with dummy callback
    var token:AsyncToken = service.getSomething(dummyResult, dummyFault);
 
    //create async test responder
    var responder:IResponder = asyncResponder(
            new TestResponder(testHandler, faultHandler), 1000, token);
 
    //wire test responder as 2nd callback
    token.addResponder(responder);
}
private function testHandler(result:Object, passThroughData:Object):void {
    assertEquals('something', result.result.name);
}

The trick is to wire a second callback via Fluint’s asyncResponder helper that actually does the testing, and just give the original service call some dummy callbacks. Note that if the service method didn’t return its AsyncToken there would be no way to wire a second callback. The Fluint async helper do two import operations: they handle the event or call the callback AND they mark the test method as an async method so the result is correctly reported by the test harness. You can read more about Async Testing in Fluint’s wiki. The rest of Fluint is your standard chain of crap borrowed from JUnit: test runner, test suites, and test cases.

Digging Deeper: It is equally critical to use dummy callbacks in the original service method call because in a failure situation they will cause Flash Player to error out instead of being caught by Fluint and reported as a test failure.

Files

The complete code is up on GitHub here: test_fluint_async. The code is MIT licensed and includes a working fluint.swc (see below) plus a mock async backend (so timeouts and faults are easily testable).

Alas, Fluint v1.1.0 was built incorrectly and is missing the TestResponder class (see issue 35). So if you want to try out Fluint in your project, I recommend you grab it from svn and build the swc yourself. Hopefully, this will all be fixed in the next release.

UPDATE: Fluint v1.1.1 was release on May 1, 2009 and fixes this issues and a few others. Download it here.


Comments

3.30.2009

1

I’m loving Fluint, but it can still be really tedious to write full UI integration tests with it. Is there a rule of thumb that you and/or Gorilla Logic use for deciding between FlexMonkey and Fluint?

On an unrelated note, I discovered your blog through Juan Sanchez’s link to your Degrafa button explorer, and I have to say that your content is spectacular. Please up the wonderful work!

3.30.2009

2

Well, if you look at your Flex code through MVC glasses, I try to test everything in the Model with Fluint, including any async backend stuff.

In the “pure” View, if a button doesn’t show up or the Model is incorrectly rendered, it’s really obvious and somewhat regression proof, so my View tests are typically sparse.

To me, FlexMonkey is all about testing the Controller. I often have complicated Controller logic that makes button X bring up page Y or page Z depending on some data. This kind of stuff often shows regressions, so it’s critical to cover it with FlexMonkey tests.

4.12.2009

3

thanks for pointing out wrong fluint swc, spent an hour trying to understand why flex can’t find stupid TestResponder

© 2017 saturnboy.com