Black Pepper Blog

The thoughts and musings of our team


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.


Comments (2)Add Comment
Richard Evans
September 29, 2008
193.138.107.180
Votes: +0
...

Are you using a modified version of Cairngorm to be able to inject a different implementation into the Services singleton?

Services.instance.loginWebService = new LoginWebServiceStub();

I can see how you can use ServiceLocator.getInstance(). but not to modify the configured services from services.xml?

Kieran Shaw
September 29, 2008
78.86.232.161
Votes: +0
...

Hi Richard,

Yes, I am using a bit of a custom Services version rather than a standard Cairngorm ServiceLocator. To be honest, I'm not a fan of the singleton type service locator implementation, it's not something I've spent much time thinking about, but I don't like it particularly.

Write comment
 
  smaller | bigger
 

security image
Write the displayed characters


busy