Mock Business Delegates with Swiz

It is a common scenario that you have to build the Flex client side when the server side is not ready yet. I want to show how you easily can fake/mock service calls with the Swiz framework.

Let’s take the login example. We have got the view with the login form, the controller which catches the LoginEvent and calls the business delegate to login the user.

The classes (download link at the end):

The SwizLoginExample.mxml loads the beans and defines the main view:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
	horizontalAlign="center" verticalAlign="middle"
	preinitialize="preInit()" xmlns:view="example.view.*">
 
	<mx:Script>
		<![CDATA[
			import example.Beans;
			import org.swizframework.Swiz;
 
			private function preInit():void
			{
				Swiz.loadBeans([Beans]);
			}
 
		]]>
	</mx:Script>
 
	<view:MainView />
 
</mx:WindowedApplication>

The Beans.mxml defines the application beans:

<?xml version="1.0" encoding="utf-8"?>
<BeanLoader xmlns="org.swizframework.util.*"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	xmlns:business="example.business.*"
	xmlns:ctrl="example.ctrl.*"
	xmlns:model="example.model.*">
 
	<model:AppModel id="appModel" />
	<ctrl:UserController id="userController" />
	<business:UserMockDelegate id="userDelegate" />
	<!--<business:UserDelegateImpl id="userDelegate" />-->
 
</BeanLoader>

The MainView.mxml only contains the two states for login and the view after the login which I called AppView. The MainView has a dependency to the AppModel because we bind against appModel.appState. Here you see the Autowire annotation which tells Swiz to autowire by type. This means that when the MainView is added to stage Swiz looks up the AppModel bean by type from the Beans.mxml BeanLoader and injects the reference into the view:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:view="example.view.*"
	currentState="{appModel.appState}">
 
	<mx:Script>
		<![CDATA[
			import example.model.AppModel;
 
			// autowire by type
			[Bindable]
			[Autowire]
			public var appModel:AppModel;
		]]>
	</mx:Script>
 
 
	<mx:states>
		<mx:State name="{AppModel.STATE_LOGIN}">
			<mx:AddChild>
				<view:LoginView />
			</mx:AddChild>
		</mx:State>
		<mx:State name="{AppModel.STATE_APP}">
			<mx:AddChild>
				<view:AppView />
			</mx:AddChild>
		</mx:State>
	</mx:states>
 
</mx:Canvas>

The LoginView.mxml only contains a form and when the user presses “Login” we create a new LoginEvent and dispatch it with Swiz:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
 
	<mx:Script>
		<![CDATA[
			import example.event.LoginEvent;
			import org.swizframework.Swiz;
 
			private function onLogin(e:Event):void
			{
				Swiz.dispatchEvent(new LoginEvent(username.text, password.text));
			}
 
		]]>
	</mx:Script>
 
	<mx:Form>
		<mx:FormItem label="Username">
			<mx:TextInput id="username" />
		</mx:FormItem>
		<mx:FormItem label="Password" direction="horizontal">
			<mx:TextInput id="password" displayAsPassword="true" />
			<mx:Button label="Login"
				click="onLogin(event)" enabled="{username.text != '' &amp;&amp; password.text != ''}" />
		</mx:FormItem>
	</mx:Form>
 
 
</mx:VBox>

The LoginEvent.as is a very simple event class which extends the basic flash event and holds the values for username and password. The important thing here is the type of the event which is the full qualified classname:

package example.event
{
	import flash.events.Event;
 
	public class LoginEvent extends Event
	{
 
		private static const TYPE:String = "example.event.LoginEvent";
 
		public var username:String;
		public var password:String;
 
		public function LoginEvent(username:String, password:String)
		{
			super(TYPE);
			this.username = username;
			this.password = password;
		}
 
	}
}

To catch this event we can define a dynamic mediator in the UserController.as which was already defined in our beans. To mediate an event you use the Mediate annoation with the type of the event (we used the full qualified classname) and the member variables of the LoginEvent to pass over to the loginUser method. Further the UserController has dependencies to the UserDelegate and the AppModel.

package example.ctrl
{
	import example.business.IUserDelegate;
	import example.model.AppModel;
 
	import mx.controls.Alert;
	import mx.rpc.AsyncToken;
	import mx.rpc.events.ResultEvent;
 
	import org.swizframework.controller.AbstractController;
 
	public class UserController extends AbstractController
	{
 
		[Autowire(bean="userDelegate")]
		public var userDelegate:IUserDelegate;
 
		[Autowire]
		public var appModel:AppModel;
 
		public function UserController()
		{
			super();
		}
 
		[Mediate(event="example.event.LoginEvent", properties="username,password")]
		public function loginUser(username:String, password:String):void
		{
			var call:AsyncToken = userDelegate.loginUser(username, password);
			executeServiceCall(call, onLoginUser);
		}
 
		private function onLoginUser(re:ResultEvent):void
		{
			var result:XML = re.result as XML;
			var success:Boolean = result.@success == "true";
			if(success)
			{
				var username:String = result.@username;
				appModel.username = username;
				// login was successfull so switch the appplication state
				appModel.setAppState(AppModel.STATE_APP);
			}
			else
			{
				Alert.show("Login failed", "Login");
			}
		}
 
	}
}

The UserDelegate is the thing we want to mock so we are using the IUserDelegate.as interface:

package example.business
{
	import mx.rpc.AsyncToken;
 
	public interface IUserDelegate
	{
		function loginUser(username:String, password:String):AsyncToken;
	}
}

The mocked implementation uses the Swiz TestUtil class to fake an AsyncToken with a faked result:

package example.business
{
	import mx.rpc.AsyncToken;
 
	import org.swizframework.util.TestUtil;
 
	public class UserMockDelegate implements IUserDelegate
	{
 
		public function loginUser(username:String, password:String):AsyncToken
		{
			var result:XML = <loginResult success="true" />
			result.@username = username;
 
			// fake the login result, wait 500ms and show busy cursor
			return TestUtil.mockResult(result, 500, true);
		}
 
	}
}

When the login was successfull the UserController sets the username and state in the AppModel.as:

package example.model
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
 
	public class AppModel extends EventDispatcher
	{
 
		public static const STATE_LOGIN:String = "stateLogin";
		public static const STATE_APP:String = "stateApp";
 
		[Bindable]
		public var username:String;
 
		private var _appState:String = STATE_LOGIN;
 
		[Bindable(event="appStateChange")]
		public function get appState():String
		{
			return _appState;
		}
 
		public function AppModel()
		{
		}
 
		public function setAppState(state:String):void
		{
			_appState = state;
			dispatchEvent(new Event("appStateChange"));
		}
 
	}
}

This causes the MainView to switch to the STATE_APP which shows the AppView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
 
	<mx:Script>
		<![CDATA[
			import example.model.AppModel;
 
			[Bindable]
			[Autowire]
			public var appModel:AppModel;
 
		]]>
	</mx:Script>
 
	<mx:Label text="Welcome {appModel.username}" />
</mx:VBox>

If we want to use the real backend service you simply comment the UserMockDelegate bean and uncomment the UserDelegateImpl bean:

package example.business
{
	import mx.rpc.AsyncToken;
	import mx.rpc.remoting.RemoteObject;
 
	public class UserDelegateImpl implements IUserDelegate
	{
 
		[Autowire(bean="userService")]
		public var userService:RemoteObject
 
		public function UserDelegateImpl()
		{
		}
 
		public function loginUser(username:String, password:String):AsyncToken
		{
			return userService.loginUser(username,password);
		}
 
	}
}

To have a deeper look download the Flex project archive.

7 comments to Mock Business Delegates with Swiz