30
Dec
08

FlexUnit Asynchronous tests with PureMVC Framework

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:
CairngormPM and TDD

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");
          }
     }
}

8 Responses to “FlexUnit Asynchronous tests with PureMVC Framework”


  1. 1 Fredrik Sandberg Dec 31st, 2008 at 8:29 am

    Very interesting to see how you´re handling the asynchronous tests with FlexUnit and PureMVC. Nice posting!!

  2. 2 Chiwai Chan Apr 16th, 2009 at 6:25 pm

    This framework will only work for projects using the PureMVC single core.

  3. 3 Proxy Guy May 11th, 2009 at 12:31 am

    Can I quote you in my report for school?

  4. 4 elad.ny May 11th, 2009 at 10:20 am

    Sure, just list this blog in the resources list.

  5. 5 Mo Feb 5th, 2010 at 8:18 am

    Hi there,

    Thank you for this article! Just a quick question here, how do you add the test to your testsuite? I want to be able to use testrunner…

    Cheers

  6. 6 elad.ny Feb 5th, 2010 at 9:41 am
  7. 7 ropp Apr 10th, 2010 at 3:55 am

    Good article! It’s a nice way to test proxies of PureMVC. But what puzzles me is how to test Mediators. Some suggestions?

  1. 1 Subotnik Blog » Blog Archive » FlexUnit and PureMVC code samples Pingback on Dec 31st, 2008 at 8:32 am