Testing Proxies in PureMVC
This post is prompted by Larry Marburger’s article, since I came across this problem a few weeks ago, and found a different solution.
The scenario
There’s this great new framework that you’re starting to use, and it’s persuaded you to turn a new leaf and unit-test your work as you go. First off, be warned this is all AS2.0, using asunit2.5, because I’m still stuck in the dark ages
The problem
PureMVC uses Proxy objects to access data from the model, which fire off Notifications (PureMVC-specific events) when the data is ready. However, in the case of a Proxy that loads XML before making its data available, how do we know when it’s ready to be tested?
Solution
Mediator objects sign up to receive notification of Proxy events in PureMVC, so we can subclass the Mediator to create a class that can run tests on the Proxy.
Example please
ASUnit calls run() for every TestCase in the TestSuite, the default implementation of which calls runNow() to run the tests within a class. So I’ve overridden run() to create the Proxy we want to test. When the Proxy is ready, it will notify the TestCase, and runNow() will be called.
In order to achieve this, I’ve created a TestHelper, which subclasses Mediator, to capture the Proxy’s completion Notification. The TestHelper tells the TestCase that it’s good to go, and runNow() gets called.
A thousand words
Implementation Specifics
First, the class we’re interested in, and as you can see, it’s very simple. It registers with the PureMVC ApplicationFacade for Events.XML_LOADED Notifications, and calls the responseHandler it was given by when created by NavigationProxyTest (runNow()).
-
import org.puremvc.as2.interfaces.IMediator;
-
import org.puremvc.as2.interfaces.INotification;
-
import org.puremvc.as2.patterns.mediator.Mediator;
-
import Events;
-
-
class NavigationProxyTestHelper extends Mediator implements IMediator {
-
-
public static var NAME:String = "NavigationProxyTestHelper";
-
private var _responseHandler:Function;
-
-
public function NavigationProxyTestHelper(view:Object, name:String) {
-
super(name || NAME, view);
-
}
-
-
public function listNotificationInterests():Array {
-
return [Events.XML_LOADED];
-
}
-
-
public function handleNotification(notification:INotification):Void {
-
switch(notification.getName()) {
-
case Events.XML_LOADED:
-
_responseHandler();
-
break;
-
}
-
}
-
-
public function setResponseHandler(response:Function):Void {
-
_responseHandler = response;
-
}
-
-
}
The NavigationProxyTest TestCase sets up the TestHelper in run(), and registers it with the ApplicationFacade. Then it creates the NavigationProxy instance that we want to test. When the Proxy is fully-loaded and raring to go, TestCase#runNow() will be called, which calls all methods starting “test” – so in this example testValuesFromXML().
-
class NavigationProxyTest extends TestCase {
-
-
private instance:NavigationProxy;
-
-
public function run():Void {
-
var mediator:NavigationProxyTestHelper = new NavigationProxyTestHelper(
-
createEmptyMovieClip("empty_mc", 1),
-
NavigationProxyTestHelper.NAME
-
);
-
ApplicationFacade.getInstance().registerMediator(mediator);
-
-
NavigationProxyTestHelper(mediator).setResponseHandler(
-
Delegate.create(this, runNow)
-
);
-
-
instance = new NavigationProxy("navigation.xml");
-
}
-
-
public function testValuesFromXML():Void {
-
-
// initialDestination & showIntroVideo are found in the XML that the NavigationProxy loads
-
-
assertEquals("destinationID = 72", 72, instance.initialDestination);
-
assertFalse("showIntroVideo = true", instance.showIntroVideo);
-
}
-
}

Larry Marburger — June 11, 2008 @ 1:38 pm
Great to meet another fellow PureMVC unit tester!
Now I know I’m not completely insane as there’s at least one other person out there who couldn’t figure out a “correct” way to unit test PureMVC.
I do like your implementation. In some aspects it makes more sense because with mine you need to pass some view component in order to use registerObserver which isn’t very clean. I didn’t do enough research to see if there is a better method for registering Observers without needing to use registerObserver() on an IView.
Luke Bayes — June 11, 2008 @ 4:51 pm
This article is great – thanks for putting it together!
A quick gotcha – about the run() method.
Keep in mind that the run() method is only called once per test case and could lead to interacting tests unless you clone that configured proxy in your setUp() method so that tests can interact with their own unique instances.
One way that we’ve solved the same problem (using ActionScript 3), is to move our asynchronous functionality into commands, and keep our proxies all synchronous all the time. I know that Cliff disagrees with that design decision, but it worked well for us and allowed us to more easily separate and share services across different features.
jamesk — June 11, 2008 @ 9:16 pm
@Luke: Yes, as it happens that’s exactly what I’ve done in the real version. I just stripped it out of this example code for brevity’s sake