Model-View-Controller (MVC) frameworks such as PureMVC or Cairngorm and TDD makes a good marriage. MVC framework works very well with TDD since the application logic is separated from the front view and the data. TDD allows us to create Test Cases on the logic only, as well as create a test case for the presentation layer.
With that said, Cairngorm by default doesn’t separate the view, behavior and model in each container. In order to utilize TDD it’s recommended to use the Presentation model pattern. Few blogs back I covered Cairngorm and the presentation model so in this blog post I am going to continue by showing you how to use TDD and FlexUnit to test your user stories.
The application I am using is simple application with only three business rules:
1. Connect to adobe feeds and retrieve the latest feeds related to Flex.
2. Display these results in a list.
3. Upon user clicking an item on the list display detail information.
Take a look and download the source code:

So far you can easily figure out how to create the User Story for item #1, To test Asynchronous tests in FlexUnit we can use the “addAsync” method. Here’s an example of calling the Flex News feeds.
package flexUnitTests
{
import flexunit.framework.TestCase;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
public class ServiceTestCase extends TestCase
{
private var service:HTTPService;
public function ServiceTestCase(methodName:String=null)
{
super(methodName);
}
override public function setUp():void
{
service = new HTTPService();
service.url = "http://rss.adobe.com/en/resources_flex.rss";
service.resultFormat = "e4x";
}
override public function tearDown():void
{
service = null;
}
public function testServiceCall():void
{
service.addEventListener(ResultEvent.RESULT, addAsync(serviceResultEventHandler, 2000, {expectedResults: "Flex News"}, failFunction));
service.send();
}
private function serviceResultEventHandler(event:ResultEvent, data:Object):void
{
var val:String = event.result[0].channel.title;
assertTrue("Unable to retrieve Flex News feeds", val, data.expectedResults);
}
private function failFunction(data:Object):void
{
fail("Unable to connect to Flex News feeds");
}
}
}
Essentially, we should create a test case for each Cairngorm user gesture. So the process is as follow:
1. Understand what the purpose of the user gesture and what model get effected
2. Dispatch the event and watch changes in the model.
Additionally, it’s recommended to create FlexMonkey view tests to ensure the application view is changing according to our requirement and will test the container.
I am not going to explain the entire code, let me cover one test case and you can figure out the rest;
The ReadAdobeFeedsTestCase this test case is based on testing ReadAdobeFeedsEvent. Each Cairngorm Event-Command is based on a user gesture and you need to understand what that user gesture means.
In our case we dispatch the event and on result we place the collection in the model, so we can dispatch the event and make sure the results reaches the model. Here’s the watcher:
watcherInstance = ChangeWatcher.watch(modelLocator.feedsPanelViewerPM,["feedsCollection"],
addAsync(itemsChanged, 2000, {compareResults: 0}, failFunc));
new ReadAdobeFeedsEvent().dispatch();
And once a change is made to the model the binding method will dispatch an event automatically and we can listen to that change and compare the result in the model:
private function itemsChanged(event:Event, data:Object):void
{
var len:int = modelLocator.feedsPanelViewerPM.feedsCollection.collection.length;
Assert.assertTrue("Collection is empty", len>data.compareResults);
}
Take a look at the complete code:
package flexUnitTests.events
{
import com.elad.application.events.ReadAdobeFeedsEvent;
import com.elad.application.model.ModelLocator;
import flash.events.Event;
import flexunit.framework.Assert;
import flexunit.framework.TestCase;
import mx.binding.utils.ChangeWatcher;
public class ReadAdobeFeedsTestCase extends TestCase
{
[Bindable]
private var modelLocator:ModelLocator = ModelLocator.getInstance();
private var watcherInstance:ChangeWatcher;
public function ReadAdobeFeedsTestCase(methodName:String=null)
{
super(methodName);
}
public function testReadAdobeFeedsEvent():void
{
watcherInstance = ChangeWatcher.watch(modelLocator.feedsPanelViewerPM,["feedsCollection"],
addAsync(itemsChanged, 2000, {compareResults: 0}, failFunc));
new ReadAdobeFeedsEvent().dispatch();
}
private function itemsChanged(event:Event, data:Object):void
{
var len:int = modelLocator.feedsPanelViewerPM.feedsCollection.collection.length;
Assert.assertTrue("Collection is empty", len>data.compareResults);
}
private function failFunc(data:Object):void
{
fail("Couldn't connect to Adobe feeds and update application model");
}
}
}
We have to make some changes in the TestRunner.mxml container.
The reason is that we need to add the same references as we added in our main application to the singleton classes: Front Controller and Service Locator. Otherwise the application will not be able to map between the event and commands as well as lack the ability to make service calls.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:flexunit="flexunit.flexui.*"
xmlns:business="com.elad.application.business.*"
xmlns:control="com.elad.application.control.*"
creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
import flexUnitTests.GesturesTestSuite;
import flexUnitTests.events.ReadAdobeFeedsTestCase;
import flexUnitTests.events.UserSelectedFeedTestCase;
import flexunit.framework.TestSuite;
private function onCreationComplete():void
{
testRunner.test = currentRunTestSuite();
testRunner.startTest();
}
public function currentRunTestSuite():TestSuite
{
var testsToRun:TestSuite = new TestSuite();
testsToRun.addTest(GesturesTestSuite.suite());
testsToRun.addTest(new UserSelectedFeedTestCase("testUserSelectedFeedEvent"));
testsToRun.addTest(new ReadAdobeFeedsTestCase("testReadAdobeFeedsEvent"));
return testsToRun;
}
]]>
</mx:Script>
<control:testController />
<business:Services />
<flexunit:TestRunnerBase id="testRunner"/>
</mx:Application>
download the source code
I will published a post soon on creating Test Suites and Test Cases for PureMVC, which is much easier than Cairngorm applications since the presentation containers are already split from the data and logic.