Testing Flex Cairngorm Commands

As I look deeper into Flex testing, I keep coming across little problems that are so easy to deal with in Java, but just aren't as simple as you might think in Flex.

One of these problems is testing chunks of code that call off to live services. There are two bits of technology that make this easy in Java and that's mock objects and Inversion of Control (IoC) for dependency injection. Flex doesn't natively do either of these.

Dependency injection is available via a couple of IoC frameworks that are available for Flex. Mock object frameworks don't seem to exist yet for ActionScript, which is a shame. So, what do you do?

Unfortunately it's back to stubs and a bit of manual dependency injection. Let's look at a LoginCommand that will make a call off to a LoginService.

public class LoginCommand implements ICommand
    {

        public function execute(event:CairngormEvent):void
        {
            trace("LoginCommand.execute()");
            var loginEvent:LoginEvent = event as LoginEvent;
            ModelLocator.instance.credentials.username = loginEvent.username;
            ModelLocator.instance.credentials.password = loginEvent.password;
            ModelLocator.instance.rememberMe = loginEvent.rememberMe;

            Services.instance.loginWebService.getUserDetails(resultHandler, faultHandler);
        }
    }

Basically we just get a LoginEvent triggering a LoginCommand.execute(). This then sets a few properties on the ModelLocator and then fires off to a LoginService instance which is managed by the Services locator. The problem is...we are unit testing here, how are we going to test this when we know that the LoginService.getUserDetails() call will blow up, and even if we stop it blowing up, we still want to know that the call was made.

public class LoginCommandTest extends TestCase
    {
        public function testLoginCommand() : void {

            var login:LoginCommand = new LoginCommand();

            var username:String = "user";
            var password:String = "password";
            var rememberMe:Boolean = false;

            var event:LoginEvent = new LoginEvent(username, password, rememberMe);
            // inject fake LoginService into real Services locator
            Services.instance.loginWebService = new LoginWebServiceStub();
            // ensure login web service is called
            CairngormEventDispatcher.getInstance().addEventListener("LoginService.getUserDetails", addAsync(callListener, 1000));

            login.execute(event);

            assertEquals(username, ModelLocator.instance.credentials.username);
            assertEquals(password, ModelLocator.instance.credentials.password);
            assertEquals(rememberMe, ModelLocator.instance.rememberMe);

        }

        private function callListener(event:CairngormEvent) : void {
            assertTrue("Login service should have been called", true);
        }
    }

So, there is the test. Most is fairly standard setting up of an event, calling the LoginCommand and then checking the ModelLocator properties. The secret sauce is the LoginWebServiceStub setup and the added of the event listener.

public class LoginWebServiceStub implements LoginService
{
    public function getUserDetails(resultHandler:Function, faultHandler:Function):void
    {
        new CairngormEvent("LoginService.getUserDetails").dispatch();
    }
}

As you can see, the trick here is that you must use interfaces for your services, which is a good habit to get into anyway. You can then inject your own implementation of that service for your testing that just fires off an event that you can listen for to find out if the call was made.

Remember that you must wrap any event listener callbacks in the "addAsync" method so that the FlexUnit framework knows you want to wait for an event before finishing the test.

This site uses cookies. Continue to use the site as normal if you are happy with this, or read more about cookies and how to manage them.

X