There are very little resources online in term of how to use FlexUnit with PureMVC and TDD, furthermore in many cases it makes more sense to create the tests after user stories are implemented.
Let’s take the application we created for Cairngorm and convert it to PureMVC framework. This is a good exercise since it can help define the different between the two frameworks.
Creating tests with existing framework may be challenging at times. In fact, there are cases where it’s makes more sense to create the test after the code is completed rather than following common TDD practices.
TDD recommend to create the test before writing the code, however working in real life application and with exsiting frameworks you may find that sometimes it’s easier to write the code before the test, especially when you use plug-ins or code generator scripts to create your user gesture automatically. I recommend to use your own judgment and see what’s works best for you.
Take a look at the post I published yesterday:
http://elromdesign.com/blog/2008/12/29/tdd-and-asynchronous-tests-with-cairngorm-applications/
View application and download the source code:

In this example I am using the same FeedsPanelViewer.mxml container I created for the Cairngorm application. However, I made few small change to the container. I decided to remove the binding properties and let the mediator handle all the changes in the container.
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
width="600" height="450"
styleName="panelView">
<!-- Feeds List -->
<mx:List
id="feedList"
change="dispatchEvent(new UserSelectedFeedEvent(FeedVO( feedList.selectedItem )));"
labelField="title"/>
<!-- Detail information -->
<mx:VBox width="540" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Spacer height="15" />
<mx:HBox>
<mx:Label text="FEED DETAIL:" fontWeight="bold"/>
</mx:HBox>
<mx:HBox>
<mx:Label text="author:" fontWeight="bold"/>
<mx:Label id="author" width="100%"/>
</mx:HBox>
</mx:VBox>
</mx:Panel>
PureMVC Mediator:
package com.elad.TDDPureMVC.view
{
import com.elad.TDDPureMVC.events.UserSelectedFeedEvent;
import com.elad.TDDPureMVC.model.FeedsPanelViewerProxy;
import com.elad.TDDPureMVC.model.vo.FeedVO;
import com.elad.TDDPureMVC.view.components.FeedsPanelViewer;
import org.puremvc.as3.interfaces.IMediator;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;
public class FeedsPanelViewerMediator extends Mediator implements IMediator
{
public static const NAME:String = 'FeedsPanelViewerMediator';
private var feedsPanelViewerProxy:FeedsPanelViewerProxy;
public function FeedsPanelViewerMediator(viewComponent:Object=null)
{
super(NAME, viewComponent);
feedsPanelViewer.addEventListener(UserSelectedFeedEvent.USERSELECTEDFEED_EVENT, changeSelectedFeed);
}
private function changeSelectedFeed(event:UserSelectedFeedEvent):void
{
setDetail(event.selectedFeed);
}
public function get feedsPanelViewer():FeedsPanelViewer
{
return viewComponent as FeedsPanelViewer;
}
override public function listNotificationInterests():Array
{
return [
FeedsPanelViewerProxy.READ_ADOBE_FEEDS_SUCCESS,
];
}
private function setDetail(feed:FeedVO):void
{
feedsPanelViewer.author.text = feed.author;
feedsPanelViewer.category.text = feed.category;
feedsPanelViewer.description.text = feed.description;
feedsPanelViewer.link.text = feed.link;
feedsPanelViewer.pubdate.text = feed.pubdate;
}
override public function handleNotification(notification:INotification):void
{
feedsPanelViewerProxy = facade.retrieveProxy(FeedsPanelViewerProxy.NAME) as FeedsPanelViewerProxy;
switch ( notification.getName() )
{
case FeedsPanelViewerProxy.READ_ADOBE_FEEDS_SUCCESS:
feedsPanelViewer.feedList.dataProvider = feedsPanelViewerProxy.feedsCollectionVO.collection;
setDetail(feedsPanelViewerProxy.selectedFeed);
feedsPanelViewer.title = feedsPanelViewerProxy.panelTitle;
break;
}
}
}
}
Proxy:
package com.elad.TDDPureMVC.model
{
import com.elad.TDDPureMVC.model.vo.FeedVO;
import com.elad.TDDPureMVC.model.vo.FeedsCollectionVO;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
import org.puremvc.as3.interfaces.IProxy;
import org.puremvc.as3.patterns.proxy.Proxy;
public class FeedsPanelViewerProxy extends Proxy implements IProxy
{
public static const NAME:String = "FeedsPanelViewerProxy";
public static const READ_ADOBE_FEEDS_SUCCESS:String = 'readAdobeFeedsSuccess';
public static const READ_ADOBE_FEEDS_FAILED:String = 'readAdobeFeedsFailed';
public var service:HTTPService;
public function FeedsPanelViewerProxy()
{
super(NAME, new FeedsCollectionVO() );
service = new HTTPService();
service.url = "http://rss.adobe.com/en/resources_flex.rss";
service.resultFormat = "e4x";
service.addEventListener( FaultEvent.FAULT, onFault );
service.addEventListener( ResultEvent.RESULT, onResult );
}
public function getAdobeFeeds():void
{
service.send();
}
// Cast data object with implicit getter
public function get feedsCollectionVO():FeedsCollectionVO
{
return data.feedsCollectionVO as FeedsCollectionVO;
}
public function get selectedFeed():FeedVO
{
return (data.feedsCollectionVO as FeedsCollectionVO).collection.getItemAt(0) as FeedVO;
}
public function get panelTitle():String
{
return data.panelTitle as String;
}
private function onResult( result:ResultEvent ) : void
{
var feed:FeedVO;
var item:Object;
var len:int = result.result[0].channel.item.length();
var collection:FeedsCollectionVO = new FeedsCollectionVO;
var dataObject:Object = new Object();
for (var i:int=0; i<len; i++)
{
feed = new FeedVO();
item = result.result[0].channel.item[i];
feed.author = item.author;
feed.category = item.category;
feed.description = item.description;
feed.link = item.link;
feed.pubdate = item.pubdate;
feed.title = item.title;
collection.addItem(feed);
}
dataObject.feedsCollectionVO = collection;
dataObject.panelTitle = String(result.result.*[0].*[0]);
setData(dataObject);
sendNotification(READ_ADOBE_FEEDS_SUCCESS);
}
private function onFault( event:FaultEvent) : void
{
sendNotification( READ_ADOBE_FEEDS_FAILED, event.fault.faultString );
}
}
}
To test the PureMVC framework I will be using puremvc-flexunit-testing. Our test calls the registerObserver method used for listening for a PureMVC notification on a proxy. We pass the PureMVC view, proxy and information for the AddSync such as the method to send a success response and timeout.
package flexUnitTests.proxies
{
import com.andculture.puremvcflexunittesting.PureMVCNotificationEvent;
import com.andculture.puremvcflexunittesting.PureMVCTestCase;
import com.elad.TDDPureMVC.model.FeedsPanelViewerProxy;
import flexunit.framework.Assert;
import org.puremvc.as3.core.View;
import org.puremvc.as3.interfaces.IView;
public class ReadAdobeFeedsTestCase extends PureMVCTestCase
{
override public function setUp():void
{
var facade:ApplicationFacade = ApplicationFacade.getInstance();
facade.registerProxy( new FeedsPanelViewerProxy() );
}
private function get proxy():FeedsPanelViewerProxy
{
var retVal:FeedsPanelViewerProxy = ApplicationFacade.getInstance().retrieveProxy(FeedsPanelViewerProxy.NAME) as FeedsPanelViewerProxy;
return retVal;
}
private function get view():IView
{
return View.getInstance();
}
public function ReadAdobeFeedsTestCase(methodName:String=null)
{
super(methodName);
}
public function testReadAdobeFeedsEvent():void
{
registerObserver(this.view, this.proxy, FeedsPanelViewerProxy.READ_ADOBE_FEEDS_SUCCESS, handleResponse, 300);
this.proxy.getAdobeFeeds();
}
private function handleResponse(e:PureMVCNotificationEvent):void
{
Assert.assertEquals("Feed title is incorrect", proxy.panelTitle, "Flex News");
}
}
}



























