FlexUnit4 Async Testing Events

When doing Unit testing in FlexUnit 4, you can test Async event firing. This is a tricky test if there are multiple events being fired. The reason it gets tricky is because of the single threaded nature of Flex.

When you simply dispatch one event ofter the other, they are added to the display stack and thus handled in REVERSE order when removed from the stack. However, if you are using remote calls, the stack is given a change to execute, while the remote call is sent off and thus Async events happen in NORMAL order. What makes this really tricky is if you have a combination of local events (reverse order) and remote events (normal order)

Here are some high level examples:

Example 1

dispatchEvent(Event1)

dispatchEvent(Event2)

dispatchEvent(Event3)

Correct Async testing is now done in reverse order, since each event is added to the display stack

Async.proceedOnEvent(this, someEventDispatcher, Event3, checkVars3, 500, null, noEventFired);

Async.proceedOnEvent(this, someEventDispatcher, Event2, checkVars2, 500, null, noEventFired);

Async.proceedOnEvent(this, someEventDispatcher, Event1, checkVars1, 500, null, noEventFired);

Notice the order of the Async.proceedOnEvent is reversed

Example 2

remote call(Event1)

dispatchEvent(Event2)

dispatchEvent(Event3)

Async.proceedOnEvent(this, someEventDispatcher, Event1, checkVars1, 500, null, noEventFired);

Async.proceedOnEvent(this, someEventDispatcher, Event3, checkVars3, 500, null, noEventFired);

Async.proceedOnEvent(this, someEventDispatcher, Event2, checkVars2, 500, null, noEventFired);

Notice the remote call clears properly, but the local calls are reverse order.

So the only way to do this is to understand that if a remote call is executed, you check for it and that if there are any local events, they are placed on a stack until no more events or a remote event takes place and then checked in reverse order.

Tricky….

Flex DateTimeValidator

I wrote the first version of a Date & Time validator for Flex. This version extends DateValidator and adds optional time. At the moment the time format is fixed to HH:MM:SS using “:” separator. I may update this later.

Enjoy



package com.helper
{
import mx.validators.DateValidator;
import mx.validators.ValidationResult;
import mx.validators.Validator;
public class DateTimeValidator extends DateValidator
{
// Define Array for the return value of doValidation().
private var results:Array;
private var timeArray:Array = new Array();
public static const RANGE_ERROR:String = “RangeError”;
public static const FORMAT_ERROR:String = “FormatError”;
public static const MAX_EXCEEDED_ERROR:String = “MaxValueExceeded”;
public static const MIN_EXCEEDED_ERROR:String = “MinValuesExceeded”;
public static const INVALID_HOUR_ERROR:String = “INVALID_HOUR_ERROR”;
public static const INVALID_MIN_ERROR:String = “INVALID_MIN_ERROR”;
public static const INVALID_SEC_ERROR:String = “INVALID_SEC_ERROR”;
public static const INVALID_TIME_ERROR:String = “INVALID_TIME_ERROR”;
public static const INVALID_SEPARATOR_ERROR:String = “INVALID_SEPARATOR_ERROR”;
private var _wrongHour:String;
private var _wrongMinute:String;
private var _wrongSecond:String;
public function DateTimeValidator()
{
super();
}
// Define the doValidation() method.
override protected function doValidation(value:Object):Array
{
var dateTimeArray:Array = new Array();
// Clear results Array.
results = [];
var dateString:String = String(value);
// Assume Date is now DATE[ TIME]
dateTimeArray = String(dateString).split(” “);
if(dateTimeArray.length > 0 && String(dateTimeArray[0]).length != 0)
{
// Call the parent Date Validator to validate the date part
results = super.doValidation(dateTimeArray[0]);
}
// Return if there are errors.
if (results.length > 0)
{
return results;
}
// Now validate the time part if there is one
if(dateTimeArray.length > 1)
{
// Expecting HH:MM:SS String. Split the String
timeArray = String(dateTimeArray[1]).split(“:”);
if(timeArray.length == 3)
{
var hour:int = Number(timeArray[0]);
var minute:int = Number(timeArray[1]);
var second:int = Number(timeArray[2]);
/////
// Now check the hour values
if(hour >= 0 && hour <= 23)
{
} else {
results.push(new ValidationResult(true,”hour”,RANGE_ERROR,”Hours need to be between 00 and 23″));
}
// Now check the minute values
if(minute >= 0 && minute <= 59)
{
} else {
results.push(new ValidationResult(true,”minute”,RANGE_ERROR,”Minutes need to be between 00 and 59″));
}
// Now check the minute values
if(second >= 0 && second <= 59)
{
} else {
results.push(new ValidationResult(true,”second”,RANGE_ERROR,”Seconds need to be between 00 and 59″));
}
} else {
results.push(new ValidationResult(true,”format”, FORMAT_ERROR, “Please enter the time as HH:MM:SS”));
}
}
// Return if there are errors.
if (results.length > 0)
{
return results;
}
return results;
}
}
}
And just for kicks, here is the FlexUnit testing of the code:
package com.helper
{
import com.helper.DateTimeValidator;
import flexunit.framework.Assert;
import mx.events.ValidationResultEvent;
public class DateTimeValidatorTests
{
private var fixture:DateTimeValidator
[Before]
public function runBeforeEveryTest():void
{
fixture = new DateTimeValidator();
fixture.inputFormat =  “YYYY/MM/DD”;
fixture.allowedFormatChars = “/-.”;
}
[After]
public function runAfterEveryTest():void
{
fixture = null;
}
[Test]
public function formatDateTimeTests():void
{
fixture.inputFormat = “YYYY/MM/DD”;
Assert.assertEquals(“2010/12/31 00:00:00″, ValidationResultEvent.VALID, fixture.validate(“2010/12/31 00:00:00″).type);
Assert.assertEquals(“31/12/2010 00:00:00″, ValidationResultEvent.INVALID, fixture.validate(“31/12/2010 00:00:00″).type);
Assert.assertEquals(“12/31/2010 00:00:00″, ValidationResultEvent.INVALID, fixture.validate(“12/31/2010 00:00:00″).type);
fixture.inputFormat = “DD/MM/YYYY”;
Assert.assertEquals(“2010/12/31 00:00:00″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:00:00″).type);
Assert.assertEquals(“31/12/2010 00:00:00″, ValidationResultEvent.VALID, fixture.validate(“31/12/2010 00:00:00″).type);
Assert.assertEquals(“12/31/2010 00:00:00″, ValidationResultEvent.INVALID, fixture.validate(“12/31/2010 00:00:00″).type);
fixture.inputFormat = “MM/DD/YYYY”;
Assert.assertEquals(“2010/12/31 00:00:00″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:00:00″).type);
Assert.assertEquals(“31/12/2010 00:00:00″, ValidationResultEvent.INVALID, fixture.validate(“31/12/2010 00:00:00″).type);
Assert.assertEquals(“12/31/2010 00:00:00″, ValidationResultEvent.VALID, fixture.validate(“12/31/2010 00:00:00″).type);
}
[Test]
public function validDateTimeTests():void
{
Assert.assertEquals(“2010/12/31″, ValidationResultEvent.VALID, fixture.validate(“2010/12/31″).type);
Assert.assertEquals(“2010-12-31″, ValidationResultEvent.VALID, fixture.validate(“2010-12-31″).type);
Assert.assertEquals(“2010.12.31″, ValidationResultEvent.VALID, fixture.validate(“2010.12.31″).type);
Assert.assertEquals(“2010/12/31 00:00:00″, ValidationResultEvent.VALID, fixture.validate(“2010/12/31 00:00:00″).type);
Assert.assertEquals(“2010/12/31 23:59:59″, ValidationResultEvent.VALID, fixture.validate(“2010/12/31 23:59:59″).type);
Assert.assertEquals(“2010/12/31 01:09:09″, ValidationResultEvent.VALID, fixture.validate(“2010/12/31 01:09:09″).type);
Assert.assertEquals(“2010/12/31 2:3:4″, ValidationResultEvent.VALID, fixture.validate(“2010/12/31 2:3:4″).type);
}
[Test]
public function invalidDateTimeTests():void
{
Assert.assertEquals(“99/12/31″, ValidationResultEvent.INVALID, fixture.validate(“99/12/31″).type);
Assert.assertEquals(“12/31″, ValidationResultEvent.INVALID, fixture.validate(“12/31″).type);
Assert.assertEquals(“31″, ValidationResultEvent.INVALID, fixture.validate(“31″).type);
Assert.assertEquals(“2010/22/31″, ValidationResultEvent.INVALID, fixture.validate(“2010/22/31″).type);
Assert.assertEquals(“2010/12/55″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/55″).type);
Assert.assertEquals(“-2010/12/31″, ValidationResultEvent.INVALID, fixture.validate(“-2010/12/31″).type);
Assert.assertEquals(“2010/-12/31″, ValidationResultEvent.INVALID, fixture.validate(“2010/-12/31″).type);
Assert.assertEquals(“2010/12/-31″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/-31″).type);
Assert.assertEquals(“2010/12/31 -1:00:00″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 -1:00:00″).type);
Assert.assertEquals(“2010/12/31 00:-1:00″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:-1:00″).type);
Assert.assertEquals(“2010/12/31 00:00:-1″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:-1:00″).type);
Assert.assertEquals(“2010/12/31 00:00″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:00″).type);
Assert.assertEquals(“2010/12/31 00″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00″).type);
Assert.assertEquals(“2010/12/31 25:00:00″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 25:00:00″).type);
Assert.assertEquals(“2010/12/31 00:60:00″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:60:00″).type);
Assert.assertEquals(“2010/12/31 00:00:60″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:00:60″).type);
Assert.assertEquals(“2010/12/31 25:60:60″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 25:60:60″).type);
Assert.assertEquals(“2010/12/31 00:00 AM”, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:00 AM”).type);
Assert.assertEquals(“2010/12/31 00:00 PM”, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 00:00 PM”).type);
Assert.assertEquals(“2010/12/31 3654567″, ValidationResultEvent.INVALID, fixture.validate(“2010/12/31 3654567″).type);
Assert.assertEquals(3654567, ValidationResultEvent.INVALID, fixture.validate(3654567).type);
}
}
}

Deploying Swiz based Flex app

When you decide to deploy a Swiz based Flex app, you need to tell the compiler to keep the meta data tags. Under compiler settings, add the following:

-keep-as3-metadata+=Inject,Autowire,Mediate,Dispatcher,PostConstruct,PreDestroy

Then from the menu. Project -> Export Release Build.

Select the Tomcat WebApps directory and export. This will deploy the export to Tomcat as binary and with Swiz strapped in



		
	

Chrome Flash debugger cannot connect

From this website:

What to do if you upgraded and the Flash Debugger doesn’t work.

In Flex the progress bar read “Waiting for Adobe Flash Player to connect to debugger…”, and after a minute or so it timed out with the error: Failed to connect; session timed out.

The fix is actually pretty simple. In Chrome, just type the following URL into your address bar, chrome://plugins/ and click Disable under the Shockwave Flash plugin with the location that ends with “Flash Player Plugin for Chrome.plugin”. You will want to keep the other Flash plugin enabled because that is your debugger version.

Installing log4FX

This may seem odd, but there are no good install howto’s for log4fx. Log4FX is a Flex equivalent of lof4J, but allows you to do remote logging.

Install:

1) Download the latest from here

2) Download the license file from the same location as above (Look at the bottom, there are license files

3) Start Eclipse

4) Go to Software updates

5) Add an Archive and select the ZIP file

6) Select all the files and install

7) Restart Eclipse when prompted to do so

======

Set up Log4FX on a project

1) Right click on your project -> Log4FX -> Add Dependencies

2) You get a warning about the license. Select Yes to manage the license

3) Install the license and restart

Logging in Flex like log4J

Here is a quick way to put logs in your Flex code. Inside each class or MXML, create a variable to hold the logging.

private static const logger:ILogger = Log.getLogger(“MyLoggingName”);

Then inside the class, put the following line to trigger an output:

logger.info(“Inside creationCompleteHandler”);

or

logger.debug(“Inside creationCompleteHandler”);

HTH

Swiz Log output

Found this in my searching to see what Swiz is doing. Put this in your main application

<fx:Declarations>
<!– Configure Swiz Framework–>
<swiz:SwizConfig
strict=”true”
eventPackages=”com.wl.sfx.events”
viewPackages=”com.wl.sfx.views”
mediateBubbledEvents=”true”
beanLoaders=”{[ Beans ]}”
logEventLevel=”{LogEventLevel.DEBUG}”/>

<mx:TraceTarget id=”logTarget”
includeDate=”true”
includeTime=”true”
includeCategory=”true”
includeLevel=”true”
/>

</fx:Declarations>

When you run debugging on Flex now, you will see what Flex is up to

First Swiz

The documentation at the Swiz site is a bit light on, but since the config is actually very simple, there is no real need for extensive documentation. I will go through an example I built up here and explain what I understand the parts to be.

The steps involved are similar to what is outlined in the docs, but I would do it in this order:

1) Create an Event Class

2) Create a Model Class

3) Create a View:

  • That references the Model
  • Dispatches the Events

4) Create a Controller:

  • That references the Model
  • That has functions for the Events (just as comments for now)

5) Configure Swiz in the Application

6) Create & Configure a Bean Loader

7) Autowire the View to the model

8) Mediate the Controller to the Events

Ok, so what does all of this mean:

1) Event Class

Create a class that has constant references to String events. This keeps it clean and reduces typo error bugs

public class NavigationEvent extends Event{

public static const CHANGE_TO_HOME_VIEW:String = “changeToHomeViewEvent”;
public static const CHANGE_TO_ECHO_VIEW:String = “changeToEchoViewEvent”;
public static const CHANGE_TO_LOGIN_VIEW:String = “changeToLoginViewEvent”;

public function NavigationEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false){
super(type, bubbles, cancelable);
}

}

Two things to make sure of:

  • Inherit from event
  • bubbles=true. This way Swiz catches events

2) Create a Model Class

The model class represents the data that the View is showing. Think of it as the non-visual version of the view. This is where you put all the Bindable data. Since the View and the Controller reference this data, it is all Bindable. The Controller updates the data in the model and the Felx Binding reflects this in the View. Initially, create just a skeleton class for the Model to get things started.

public class ApplicationViewModel{

/* The different views on the view stack.
*/
public static const LOGIN_VIEW:int         = 0;
public static const HOME_VIEW:int         = 1;
public static const ECHO_VIEW:int         = 2;

[Bindable]
public var viewStackIndex:int = LOGIN_VIEW;

}

Here I have a ViewStackIndex that is the data of the Model.

3) Create a View

Now create a View that visualises what the model holds as data. Remember to reference the Model and make it Bindable. Events are dispatched from the View using the Events created above.

<fx:Script>
<![CDATA[
import com.wl.testswiz.events.NavigationEvent;
import com.wl.testswiz.models.ApplicationViewModel;

// Link the Application Model to the View
[Bindable]
public var applicationViewModel:ApplicationViewModel;

protected function button1_clickHandler(event:MouseEvent):void
{
var returnEvent:NavigationEvent = new NavigationEvent(NavigationEvent.CHANGE_TO_HOME_VIEW);
dispatchEvent(returnEvent);
}

protected function button2_clickHandler(event:MouseEvent):void
{
var returnEvent:NavigationEvent = new NavigationEvent(NavigationEvent.CHANGE_TO_LOGIN_VIEW);
dispatchEvent(returnEvent);
}

]]>
</fx:Script>

<s:Label x=”347″ y=”143″ text=”ViewStack Index: “/>
<s:Label x=”347″ y=”167″ text=”{applicationViewModel.viewStackIndex} “/>

<mx:ViewStack id=”viewstack1″ selectedIndex=”{applicationViewModel.viewStackIndex}” width=”100%” height=”242″ y=”283″>
<s:NavigatorContent label=”Login” width=”100%” height=”100%”>
<views:LoginView currentState=”LoginState” horizontalCenter=”0″ verticalCenter=”0″/>
</s:NavigatorContent>
<s:NavigatorContent label=”Home” width=”358″ height=”313″>
<views:HomeView height=”222″ width=”321″/>
</s:NavigatorContent>

</mx:ViewStack>

Notice that there is nothing here of Swiz yet. I have not done anything Swiz related.

4) Create a Controller

Next we create a controller for the Events that are fired off from the View. For now we will simply reference the Events via comments as shown below.

public class ApplicationController{

[Bindable]
public var applicationViewModel:ApplicationViewModel;

// Dummy Local event for now
// NavigationEvent.CHANGE_TO_LOGIN_VIEW”
public function login():void {
applicationViewModel.viewStackIndex = ApplicationViewModel.LOGIN_VIEW;
}

// NavigationEvent.CHANGE_TO_HOME_VIEW
public function logout():void {
applicationViewModel.viewStackIndex = ApplicationViewModel.HOME_VIEW;
}
}

In this class the ViewStack index of the Model is changed when these Events happen. We will wire everything together below.

5) Configure Swiz in the Application

Right, now we start the magic. Up to this point we have done nothing special at all. We have simply created a Model for the data, a View to show some of the data and a Controller to change the data. We will now wire in Swiz to make all of this work. Swiz is configured in the main application file using attributes as shown below. I am not going to explain each one of them, as it is well covered in the documentation.

<fx:Declarations>
<!– Configure Swiz Framework–>
<swiz:SwizConfig
strict=”true”
eventPackages=”com.wl.testswiz.events”
viewPackages=”com.wl.testswiz.views”
mediateBubbledEvents=”true”
beanLoaders=”{[ Beans ]}”
logEventLevel=”{LogEventLevel.ALL}”/>
</fx:Declarations>

<views:MainView id=”mainView”/>

Few things to note however:

  1. The view and event packages do NOT have a “.*” at the end. This is NOT a package reference.
  2. The beanLoaders array has to be in the default path else it won’t be found
  3. Since Swiz is only completely initialised once this application file is initialised, you cannot put any logic in the main file. I originally had the ViewStack code in here and it would not work. Only have references to the main view here as shown above.

6) Create & Configure a Bean Loader

Next part is to tell Swiz what you are using as Models and Controllers and add a Bean reference ID to them. These IDs will be used throughout the application.

<swiz:BeanLoader xmlns:swiz=”http://swiz.swizframework.org&#8221;
xmlns:fx=”http://ns.adobe.com/mxml/2009&#8243;
xmlns:controllers=”com.wl.testswiz.controllers.*”
xmlns:models=”com.wl.testswiz.models.*”>

<fx:Declarations>
<!– Models –>
<models:ApplicationViewModel id=”applicationViewModel”/>
<models:LoginViewModel id=”loginViewModel”/>
<models:HomeViewModel id=”homeViewModel”/>

<!– Controllers –>
<controllers:ApplicationController id=”applicationController”/>
<controllers:LoginController id=”loginController”/>
<controllers:HomeController id=”homeController”/>

<!– Delegates –>
<!– Remote Services –>

</fx:Declarations>

</swiz:BeanLoader>

Nothing fancy here at all. We reference the models and the controller classes and give them a bean name. I will discuss remote services and delegates in another post. That’s it, Swiz is now configured into your application. Now we need to strap it up in the various parts. The next two parts are what you are going to be doing for all classes to tie them to each other. The simple explanation is as follows:

  • Autowire – Tie this class to a Bean
  • Mediate – React to this Event

This will make sense below

7) Autowire the View to the model

We already have a reference to the Model in the View, but now we need to tie it to Swiz. This is done by adding the Autowire Metadata tag. Using the code from Step 3) above we add the following

……

<fx:Script>
<![CDATA[
import com.wl.testswiz.events.NavigationEvent;
import com.wl.testswiz.models.ApplicationViewModel;

// Link the Application Model to the View
[Bindable]
[Autowire (bean="applicationViewModel")]
public var applicationViewModel:ApplicationViewModel;

protected function button1_clickHandler(event:MouseEvent):void

………..

This ties this View to the Model. Nothing else is needed.

8) Mediate the Controller to the Events

The View fires off events and the Controller class catches them. We need to tell Swiz this. Using the controller class from step 4) above

public class ApplicationController{

[Bindable]
[Autowire(bean="applicationViewModel")]
public var applicationViewModel:ApplicationViewModel;

// Dummy Local event for now
[Mediate(event="NavigationEvent.CHANGE_TO_LOGIN_VIEW")]
public function login():void {
applicationViewModel.viewStackIndex = ApplicationViewModel.LOGIN_VIEW;
}

[Mediate(event="NavigationEvent.CHANGE_TO_HOME_VIEW")]
public function logout():void {
applicationViewModel.viewStackIndex = ApplicationViewModel.HOME_VIEW;
}
}

Here we wire the Controller to the Model and also catch (Mediate) the Events from the View.

That’s it. There is nothing else to it. It is simple and I think a nice light framework. The annotations make the notation easy to understand. More to follow in another post.

Install Flex Builder 3 on Spring STS

So, after much struggling, I finally have an option open.

I downloaded the new Spring STS, which is basically Eclipse with a whole lot of Spring-ness added to it. I tried to install the STS plugin in eclipse but no luck, so I thought I’d give this a go.

The FB3 install fails, but can be manually fixed by doing the following:

1) Install FB3 (I installed it in the STS main directory)

2) Do not overwrite the newer file when prompted so during the FB3 install. Finish the install.

3) Under the STS main directory go to dropins ( sts/dropins )and create a new directory there call FlexBuilder3 (sts/dropins/FlexBuilder3)

4) Now under the installed FB3 plugin (sts/Adobe Flex Builder 3 Plug-in) copy the entire “eclipse” (sts/Adobe Flex Builder 3 Plug-in/eclipse) directory to this new directory just created in step 3 above ((sts/dropins/FlexBuilder3)

5) Start STS and all is well.

Follow

Get every new post delivered to your Inbox.