Mocking Loader and Services
I've been using mock-as3 in my Flex unit tests a lot recently discovered a couple useful strategies for mocking things that are not obviously mockable at first glance: Loader and Services (AsyncToken).
Here is the source for a Flex project that I'll be referencing that contains a sample controller with its tests: MockingLoaderAndServices
Most of the relevant code is in com.foomonger.blog.mockingloaderandservices.controller.ControllerTest.
ControllerTest contains tests for Controller which has 2 methods: loadAssetsSwf() and loadDataXml(). Those methods call corresponding methods in the Delegate. loadAssetsSwf() demonstrates loading a SWF using a Loader. loadDataXML() demonstrates loading XML through an HTTPService. In ControllerTest I create MockDelegates which is then injected into the Controllers.
flash.display.Loader
When loading a SWF into another SWf, you use the Loader class and listen for events from the contentLoaderInfo property. To mock the loading of a SWF, you have to simulate events being dispatched from contentLoaderInfo. The problem is that you can't call dispatchEvent() nor subclass LoaderInfo and instantiate it.
The solution: LoaderInfo2. In the source you'll see that the class flash.display.Loader2 subclasses Loader and adds a contentLoaderInfo2 property of type IEventDispatcher. By default it's set to the normal contentLoaderInfo, but can be set to a generic EventDispatcher for testing. When you set contentInfoLoader2 to an EventDispatcher instance, you can then call dispatchEvent() on it. Note that Loader2 is used instead of Loader in the Delegate and Controller. See ControllerTest.test_loadAssetsSwf_complete() .
This is not an ideal solution but works well enough for my uses.
mx.rpc.AsyncToken
When loading data using Flex service objects, you get back an AsyncToken which you can add event listeners and responders. I typically add responders. To mock the service calls, you have to simulate the calling of the responders. The problem is how do you trigger the response?
After a little digging around, I found that ResultEvent and FaultEvent have the mx_internal method callTokenResponders() which triggers the response. So the solution is pretty straight-forward: create the appropriate Result(Fault)Event object, passing an AsyncToken, and call callTokenResponders() . See ControllerTest.test_loadDataXML_result().
I noticed that the event listeners that are added with token.addResponder() are not removed in the handlers methods. Does the framwork remove them for me, or should I manually remove them myself?
Jason Hanson
28 Sep 10 at 4:31 PM
Hey Jason, I don’t believe the framework removes them for you and I don’t think you need to remove them yourself because AsyncTokens are pretty much transient non-display objects so A) they should be garbage collected appropriately and B) nothing should be using them outside the service call round trip. I’ve been using addResponder() exclusively for a while now and haven’t had any issues (knock-on-wood).
Sam Ahn
28 Sep 10 at 8:37 PM