flex.scoutant.org


Keyword - as3


2009/10/28

FlexUnit4 and Flash Builder 4 in action - A FlexUnit4-power TDD Use case

FlexUnit4 Use case : a Reverse Polish Notation Calculator

You need JavaScript enabled!

A nice introduction is the Wikipedia page on Reverse Polish Notation [1].

In Reverse Polish notation the operators follow their operands; for instance, to add three and four, one would write "3 4 +".

Let's play with the calculator above. It is a live Flash application.

Type some digits and press ENTER so as to push a number to the stack. When you have two numbers in the stack you can hit a binary operator key like '+' or '*'. The 2 operands will be consumed and the result will be push to the stack. As an option you can use keyboard shortcuts.

The requirements are easy to express. And present use case is prone to a test-driven development approach. And in our simple case, it is realy close to feature-driven development.

Test-driven development

Googling, you'll find many references and even books on TDD.

To make it short , the point is to write the test code before the target code.

So we have micro development cycle :

  • adding a test method and do the least for the test to compile
  • run the unit test and notice that it fails : RED state in the unit-test report panel
  • implement the target logic : you are not finised until the unit test succeed : GREEN state.

All the unit test are independent one from each other. So that they can be cumulated in unit-test campain that are to be run before any code is commited.

With that test automation infrastucture, you can now feel confident to refactor often your code : within some seconds you can have feedback in case of any regression.

But what about TDD for AS3 and Flex developpement?

ActionScript and Flex TDD out there?

It's true, there are not that many reference on test-driven development when talking about GUI.

Okay you will find the FlexUnit page @ Adobe Dev Net [2]. And some other references.

But FlexUnit is not integrated to Flex Builder, so that very few project have adopted a true TDD approach.

Next generation FlexUnit4 and Flash Builder 4 enabling true AS3/Flex TDD

Compared to Flex Builder 3, Flash Builder 4 comes with a major feature : a true unit testing support. With both :

  • TestCase class generation wizards and
  • the possibility to run a TestCase from the IDE without the bowler-plate of defining any test suite nor test app.

FB-wizard2

Moreover, after (too) many years of FlexUnit 0.90, Adobe has at least decided to put some effort in a next generation unit-test framework. We have a upcoming FlexUnit4 framework [3].

As you can see, it's an adaptation of JUnit4, the annotation-driven unit-test java support. Comprehensive illustration following at this post : http://www.insideria.com/2009/05/flashbuilder4-will-support-fle.html .

A true development workflow sample will surely best describe the benefits of the soon to be released tdd support with Flex4 ecosystem.

FB4-flexunit4

Today we will use the available releases : Flash Builder 4 beta 2 and FlexUnit4 beta 2 turnkey distribution. I suppose FlexUnit4 will be fully integrated to the IDE, but with beta release, a last step is necessary o set up the environnement.

FlexUnit4 Turnkey distribution is a Flex4 project you can import with Flash Builder 4 Import wizard.

A step by step true TDD sample : writing the AS3 test cases before the target code

Let's write AS3/Flex code meeting the RPN Calculator requirements. Let's create a Flex projet, called rpn. The TDD-style drives us to first write the TestCase. Let's do so using the 2 wizards mentionned above.

Create an AS3 TestCase class using wizard

Let's instanciate a Calculator from the FlexUnit4-powered CalculatorTest class below :

package org.scoutant.rpn {
	public class CalculatorTest {	
		protected static var calculator:Calculator = new Calculator();
		}
	}
}

Notice how our test case does not need to inherit any test case base class (as with FlexUnit 0.90). Obviously, it won't compile :

tdd-2

We need to define the Calculator class.

Create now the target class

JUnit4 integration within Eclipse is somewhat more powerfull : the quick-fix feature suggest the Java developer the generation of corresponding class. With Flash Builder 4 beta2, we'll have to do it manually. But this is not a big deal.

package org.scoutant.rpn {
	public class Calculator {
		public function Calculator() {
		}
	}
}

Okay, now it compiles. We can begin the the iterative tdd workflow at method level.

Create a FlexUnit4 test method

Writing unit-test method code

Let's consider the single following feature : pressing the digits composing a number and when done : validating with an enter action. And notice how the number is now on top of the stack. An AS3 represnetation of that could be :

[Test]
public function enteringAnOperand(): void {
  calculator.enter( 3);
  Assert.assertEquals( 3, calculator.peak());
}

The test method name does not matter. With FlexUnit4 it does not need to be prefixed by test. What matter is the Test metadata. Just like JUnit 4 is based on Java annotations.

Now we have the unit test. It does not compile :

If needed adding the API code for the test to compile

Let's just define the mentionned enter(.) and peak() method.

public function enter(operand : Number) : void {
}
public function peak(index:int = 0) : Number {
  return NaN;
}

Now it compile. We can run the test. To do so with beta2 release, we need to select the test case in the explorer view : the contextual menu, with right click, shows Executes FlexUnit Tests. Of course the test fails. But we have bow the test infrastucture up and we can focus on implementing the target logic.

Implement the logic meeting that small piece of requirment.

A little bit of design : the Calculator requirement are somewhat similar to a Last-in first-out stack:

  • pushing items,
  • popping the two top-most items so as to operate them

Hence, our Calculator class may be based on an AS Array. Yes, the Array API comes with push(.) and pop() methods.

private var stack : Array = [];
public function enter(operand : Number) : void {
	stack.push( operand);
}
public function peak(index:int = 0) : Number {
	return stack[ index ];
}

Now we run the test and it turns to GREEN.

We are done with the feature. BUT a unique test for a feature may not be enough a validation.

a second unit-test method for same feature

[Test]
public function enteringTwoOperandsAndReadin(): void {
	calculator.enter( 3);
	calculator.enter( 6);
	Assert.assertEquals( 6, calculator.peak());
}

The test above test that the number that is pushed last, is actually retrieved by peak() method. The test compiles, let's run it to verify that the test passes.

tdd-4

Fixing the bug

It is not expected, the test fails! we have the RED bar. We must fix that before considering any other feature.

Notice the advantage of our tdd approach and it's micro cycles. The bug introduced by the implementation above is discovered with a minute of time!

We know that the cost of a bug is exponential to the time elapsed since it was intoduced. Here our tdd approach will minimize that cost.

public function peak(index:int = 0) : Number {
	return stack[ stack.length -1 - index ];
}

Yes the AS3 Array has a stack API : push(.) and pop(). But no peaking feature. Our first implementation was not LIFO-consistent.

After refactoring the index as above. We can run the test-case. We can see how the last test turns GREEN.

We can now consider next feature, for instance adding two operands.

TDD of next feature : adding two operands

implementing a test method

Let's enter 2 operands and hit the "+" operator. We want the 2 last operands to be consumed and the sum to be push into the stack. A test may be :

[Test]
public function addingTwoOperands():void {
	calculator.enter( 3);
	calculator.enter( 6);
	calculator.add():
	Assert.assertEquals( 9, calculate.peak() );			
}

For the test to compile we need to define the methode add() .We can begin with an empty method body. The test compiles.

We run it and we have the RED state.

! implementing the logic 
public function add():void {
	stack.push( stack.pop() + stack.pop() );
}

With the above implementation our test turns to GREEN. tdd-5

Expressing an espected Error with FlexUnit4

As in previous cycle we'll add more test to validate the target add() method.

The "+" operator is assuming to find two operands in the stack. If none or only one is available we want the calculator to raise an error.

The GUI shall give an error feedback the the user.

Compared to FlexUnit 0.90, FlexUnit4 provides an interesting feature. The possibility to express that a test is supposed to raise an error.

[Test (expects="Error")]
public function addingButNoOperand():void {
	calculator.add();			
}

We are mentionning that we expect an error: the test case will be green if we actually have the error. Let's run the test. tdd-6

Fixing the bug

We expect our add() method to raise an error. But corresponding assertion (expects="Error") is not validated.

We need to refactor our add() implementation. Looking att the AS3 Array reference documentation, we discover that popping an empty array does raise an Error but silently returns null.

We are not happy with that so let's add an applicative precondition and raise an ArgumentError if the stack has less that 2 operands.

public function add():void {
	if (stack.length < 2) {
		throw new ArgumentError();
	} 
	stack.push( stack.pop() + stack.pop() );
}

Now , running the test, yes it is GREEN. Let's add a second test, just to be sure that if we none operand, we do have an error too:

[Test (expects="Error")]
public function addingButNoOperand():void {
	calculator.add();			
}

The test case is still GREEN.

More tests for same feature

Lets add a test case with negative number and mixing integrer and Numbers...

[Test]
public function addingNegativeOperand():void {
	calculator.enter( -3);
	calculator.enter( -6);
	calculator.add();			
	Assert.assertEquals( -9, calculator.peak() );			
}
[Test]
public function addingIntegerAndNumberOperand():void {
	calculator.enter( 3);
	calculator.enter( 6.1);
	calculator.add();			
	Assert.assertEquals( 9.1, calculator.peak() );			
}

Still GREEN. we can can confidently consider implementing next feature.

TDD of similar feature (multiply operand) : copy and paste both the test fixture and the business logic

We can feel that the "*" operator is very much similar to the "+". Both are binary and symetrical. The code can be very similar. We can copy and paste both the test methods and the target logic.

But to prevent any mismatch, best is to abide to the RED before GREEN principle. So we can copy and adapt the test fixture. And verify the no compilation or the RED state.

TDD of next feature (substract operand)

Operating as above, we first provide the test fixture mentionning a substract(). Compile error. And by similarity we provide following implementation :

public function substract():void {
	if (stack.length < 2) {
		throw new ArgumentError();
	} 
	stack.push( stack.pop() - stack.pop() );
}

We just replaced "+" by "-" in the implementation. We can run the test.

tdd-7

Fixing the bug

It's NOT green. We must correct the implementation! The Substract operator is not symetrical ; and obviously the add template has to be adapted : Let's replace with :

public function substract():void {
	if (stack.length < 2) {
		throw new ArgumentError();
	} 
	stack.push( - stack.pop() + stack.pop() );
}

Now it turn to green. Thanks to our TDD approach we corrected the bug no later that 30 seconds after the bug was introduced.

Let's go on with division operator.

Next feature ( division operator)

The operator is not symetrical and we'll take care.

[Test]
public function divideTwoOperands():void {
	calculator.enter( 8);
	calculator.enter( 2);
	calculator.divide();
	Assert.assertEquals( 4, calculator.peak() );			
}

Let's implement the missing divide() method. It is a binary and orientated operator like the substract one. Let's take it as template :

public function divide():void {
	if (stack.length < 2) {
		throw new ArgumentError();
	} 
	var dividend:Number = stack.pop(); 
	stack.push( stack.pop() / dividend );
}

Here no surprise, it's GREEN.

Next feature ( unary square root operator)

unit-test fixture and target implementation
[Test]
public function squarreAnOperand():void {
	calculator.enter( 1024);
	calculator.sqrt();
	Assert.assertEquals( 32, calculator.peak() );			
}

Not compiling. We add en empty sqrt() implementation. RED state.

We may consolidate with following implementation.

public function sqrt() : void {
	if (stack.length < 1) {
		throw new ArgumentError();
	} 
	stack.push( Math.sqrt( stack.pop() ));
}

Now it turns GREEN.

Additional fixture for square root of negative number

Let's add an additional test fixture. For a negative number we want an error (we are not onsidering complex number).

[Test (expects="Error")]
public function squareRootNegativeNumber():void {
	calculator.enter( -1);
	calculator.sqrt();
}

Result. tdd-8

Fixing the expectation

It is NOT green. We expected an Error.

But diving into the AS3 doc, we can see that Math.sqrt() calculation actually does not raise an error for negative nymbers : it just returns NaN...

Let's manually raise the expected Error.

public function sqrt() : void {
	if (stack.length < 1) throw new ArgumentError();
	if ( this.peak() < 0 ) throw new ArgumentError();
	stack.push( Math.sqrt( stack.pop() ));
}

Now it's GREEN.

Now our Calculation logic seems to be finished.

Next step is to wrap it with the GUI components.

GUI wrapping

We can now develop the GUI against our unit-test Calculator class. Not only the Calculator class is tested but moreover the feature are clear. We have a somewhat a feature-driven approach.

Now we can take advantage of one of Flex4 main feature : the FXG skinning architecture. We can first develop a squetch GUI based on simple buttons.

Micro architecture

We have the Calculator.as logic.

We'll implement a rpn.mxml application. Our RPN Calculator mostly has a sreen representing the stack and buttons : digits and operator.

Bas practice is to consider all the GUI stuff in a single .mxml file. Even more when targetting Flex4 FXG skinning feature.

We'll at least consider Digit.mxml an Operator.mxml files.

Component layout

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
  xmlns:s="library://ns.adobe.com/flex/spark" 
  xmlns:rpn="org.scoutant.rpn.*"
  >
<s:layout>
  <s:VerticalLayout/>
</s:layout>
<fx:Script>
  <![CDATA[
  [Bindable] public var calculator:org.scoutant.rpn.Calculator = new org.scoutant.rpn.Calculator();
  [Bindable] public var line:String = "";
  ]]>
</fx:Script>
. . .
<s:SkinnableContainer width="100%">
  <s:layout>
    <s:HorizontalLayout/>
  </s:layout>
  <rpn:Digit id="_7" label="7"/>
  <rpn:Digit id="_8" label="8" />
  <rpn:Digit id="_9" label="9" />
  <rpn:Operator id="DIVIDE" label="/" operator="{calculator.divide}" />
  <s:Button label="1/x" />
</s:SkinnableContainer>
. . .

Connecting the logic to the GUI : with method inection

<rpn:Operator id="DIVIDE" label="/" operator="{calculator.divide}" />

The line above is instanciating a component (named Operator.mxml here). And we are injecting an AS3 method : {calculator.divide}.

The operator.mxml component is a normal Flex Button : the click handler will execute the operator.

The operator may be defined as a AS 3 variable of type Function. So that the function maybe injected as seen above.

<s:Button xmlns:fx="http://ns.adobe.com/mxml/2009"  xmlns:s="library://ns.adobe.com/flex/spark" 
  click="execute()"
  >
  <fx:Script>
    <![CDATA[
      [Bindable] public var operator:Function;
      public function execute():void{
        operator();
      }
    ]]>
  </fx:Script>
</s:Button>

Above we could he logic beeing invoqued with a user gesture : hiting the "DIVIDE" button invoques the Calculator divide() method.

As a feedback we expects now the "sreen" to be updated with the new stack content. Flex data-binding is the best friend for that!

Connecting the GUI to the logic : with data-bindig

We cannot use the peak() method for the data-binding. We need to bind to a specfic property or accessor. It could be :

<s:Label textAlign="right"  text="{''+calculator.stack[calculator.stack.length-1]}" />

Here the compiler warns us. tdd-9

I would say an other way : the Array class is not an EventDispatcher.

Hence we may refactor the Calculator class. We can wrap the Array with an ArrayCollection that is an EventDispatcher.

public class Calculator {		
  [Bindable] public var lifo:ArrayCollection = new ArrayCollection();
  private var stack : Array = lifo.source;

Now we can express the data-binding with getItemAt(.) expression. Just like the compilatior Quick Fix suggested.

<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-1)}" />

Now the user has the desired feedback. HP 28 S Calculator [4] has a 4-line screen, in a bottom up fashion. Could be :

<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-4)}" />
<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-3)}" />
<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-2)}" />
<s:Label width="100%" textAlign="right" text="{''+calculator.lifo.getItemAt( calculator.lifo.length-1)}" />

Now we have a functionnal RPN Calculator.

You need JavaScript enabled!

It was not the point in present post, but all the components used here inherits from Flex 4 Spark SkinnableComponent. In next post, we'll see how to write Skin mxml components. That can be referenced to as to have a nice RPN Calculator. With very few changes to our application, only specifying the skinClass property for our Skinnable Compoents...


2008/12/30

SharedObject to persist your Flash app state - A Design canvas sample

You need JavaScript enabled!

Move some figures arround, press Save, Close app, Re-open and notice how figure location is persisted... View source code [1]

Shared Object

Une application Flash a accès au file-system de façon très restreinte, via la technique des SharedObjet. A l'image des Cookies pour les applications web.

Dans le cas de l'application Flash, on dispose d'un espace de stockage, in fine, orienté objet : tout objet (ou grappe d'objet) AS3 peut être sérialisé en AMF3 pour être stocké sous forme de SharedObject. Pour que la dé-sérialisation opère correctement il convient d'enregistrer les classes, typiquement avec le meta-tag RemoteClass.

package model {
	[RemoteClass( alias="model.ImageVO")]
	public class ImageVO {
		public var source:String;
		public var x:Number;
		public var y:Number;
        }

Write

Ensuite, comme le monte la doc [2], il convient d'obtenir un SharedObject pour une nom donné, de lui adjoindre des données, et de réaliser l'écriture avec la méthode flush().

var mySO:SharedObject = SharedObject.getLocal("chessboard");
mySO.data['figure'] = new ImageVO('King.gif');
mySO.flush();

Read

Pour la lecture, c'est tout a fait similaire, sauf que l'on opèrere un down-cast pour promouvoir le typage fort.

var mySO:SharedObject = SharedObject.getLocal("chessboard");
var figure:ImageVO = mySO.data['figure'] as ImageVO;

Serializing UIComponents?

Notre exemple d'échiquier, est un simple Canvas qui contient des Images. L'exemple illustre également le déplacement d'image via Drag & Drop, explicité dans le post [3].

L'approche naïve, consiste dans un premier temps à tenter de sérialiser le Canvas ou les images. Cela ne fonctionnera pas. Un UIComponent comporte des centaines de propriétés et en générale on veut juste stocker quelques propriétés : dans notre exemple le positionnement x et y et la source pour une Image...

D'où la classe ImageVO, vue ci-dessus. C'est cette classe qui sera persistée. Il s'agit alors de prévoir l'instantiation d'une mx.controls.Image à partir de notre ImageVO. Plusieurs approches sont possibles ; on peut prévoir une sous-classe de Image qui ajoute simplement les accesseurs à la ValueObject correspondante :

public class ImageUI extends Image {
		public function get vo() : ImageVO {
			return new ImageVO( source, x, y, width, height);
		}
		public function set vo(ref:ImageVO):void {
			source = (ref!=null ? ref.source : null) ;
			width = ( ref!=null ? ref.width : 0);
			height = ( ref!=null ? ref.height : 0);
			x = ( ref!= null ? ref.x : 0);
			y = ( ref!= null ? ref.y : 0);
		}
       }

Pour bien faire, on aimerait bien ajouter un constructeur d'Image sur la base de son VO correspondant. Mais l'AS3 ne permet pas d'avoir plusieurs constructeurs, on peut en alternative opter pour une méthode statique de fabrication. Par exemple :

public static function newImage( vo:ImageVO, mouseDownCallback:Function=null) : ImageUI {
			var ref:ImageUI = new ImageUI();
			ref.vo = vo;
			return ref;
		}

Muni de cette classe l'instantiation d'un UIComponent à partir du SharedObjet correspondant est très simple. Reste simplement à voir comment se présente la persistance pour une collection d'instance.

Array of data

Comme on a affaire à une vrai sérialisation AMF3, on peut aussi sérialiser directement un tableau d'objets. Par exemple :

private function doSave():void {
            	mySO.data['figures'] = board.getChildren().map( function( item:ImageUI, index:int, arr:Array):ImageVO { 
                     return item.so
                 } );
            	mySO.flush();
            }

2008/11/22

Closures AS3 a la Groovy with Array.forEach() and Array.map() support

Dans nombre de langage de programmation fonctionnel, il est courant de manipuler des collections et d'appliquer des closures aux membres de la collection.

Un exemple avec Groovy [1] , un langage ciblant la JVM Java :

[ 1, 2, 3, 4, 5 ].each { x -> println x*x }
[ 1, 2, 3, 4, 5 ].collect { x -> x*x }

La dernière ligne produit très naturellement la collection :

[1, 4, 9, 16, 25].

En AS3 on a un support équivalent. Quoi qu'un peut moins elliptique.

Array.forEach() [2]

[1, 2, 3, 4, 5].forEach( traceSquare);
private function traceSquare( x:*, index:int, arr:Array):void {
  trace( "" + x*x);
}

Ceci produit vient la même sortie que la ligne groovy avec each{}.

Array.map() [3]

[1, 2, 3, 4, 5].map( square);
private function square( x:*, index:int, arr:Array): * {
  return x*x;
}

Quand le traitement est court comme ci-dessus, on peut aussi opter pour une définition inline, comme suit :

[1, 2, 3, 4, 5].map( function( x:*, index:int, arr:Array):* { return x*x } );
}

Solution de un-marshalling JSON avec closure Array.map()

Nombre de web services offrent un format Json. Le service de feeds de delicious en est un exemple : http://feeds.delicious.com/v2/json/tags/coutant .

{ "flex": 120, "blazeds": 8, . . .}

Il peut être intéressant de convertir cette réponse Json directement en un array d'objets AS3 représentant un tag (avec son nombre d'occurrence comme propriété). Ceci peut être fait simplement :

tagsAsJson.split(",").map( function( item:*, index:int, arr:Array):Tag { return new Tag(item) } );

2008/11/ 8

DeliSphere, a Flash 3D-animation tagcloud for your Delicious bookmarks

Visit the demo page to see the animated tagcloud for your delicious.com [1] account.

You can view the source and if you want to insert the DeliSphere tagcloud in your site, you can download the delisphere distribution.

The Delesphere tagcloud is an adaptation of the famous WordPress [2] Cumulus plugin.

The WordPress Cumulus plugin

WP-Cumulus [3] is a WordPress plugin providing a Flash 3D animation displaying the tags related to the WordPress-powered blog. You can visit Roy Tanck's site at http://www.roytanck.com.

If you are using DotClear-powered blog, you can use the TagFlash [4] adaptation.

WP-Cumulus is a WordPress plugin, hence a PHP script that will request the MySql database for tags hosted by the blog. A click on a tag will then open up the posts related to the tag.

Delisphere architecture - Delicious API

DeliSphere Flash animation uses Delicious web services to get the tag cloud. Consider for example the bookmarks for delicious account : http://delicious.com/coutant.

Delicious provides an API [5]. But to use those web services you need to be authenticated with targeted delicous account.

When you are just reeding public data like bookmarks or tags, the smartest approach is to use the feeds [6] services. Choosing RSS ot Json format. Json is just fine, for example the tag feed for above account is http://feeds.delicious.com/v2/json/tags/coutant .

{ "flex":135, "blazeds":7, "dnd": 8, "jee5":23 ... }
Delisphere architecture - cross-domain policy

FlashPlayer cross domain policy will not enable a Flash application downloaded from domain A to be requesting services from an another domain B. Unless domain B is actually exposing a cross-domain friendly policy file.

Delicious.com is not exposing such a friendly policy file. But since V2, the feeds service does! Here is the file : http://feeds.delicious.com/crossdomain.xml .

<cross-domain-policy>
  <allow-access-from domain="*" />
</cross-domain-policy>

See post crossdomain.xml now on feeds.delicious.com [7]

That way the delisphere app can be downloaded from anywhere onto a brower and from that location be connecting to Delicious feeds web service.

Last step is the randomizing of spherical coordinates [8]

Notes

[1] Social bookmarking delicious.com

[2] WordPress http://wordpress.org

[3] WP-Cumulus : a WordPress plugin to display your site's tags in a flash movie, http://wordpress.org/extend/plugins/wp-cumulus/

[4] tagflash, Dotclear plugin, an adaptation to Dotclear of WP-Cumulus http://plugins.dotaddict.org/dc2/details/tagflash

[5] Delicious API V2 : http://delicious.com/help/api

[6] Delicious data feeds http://delicious.com/help/feeds

[7] crossdomain.xml now onf feeds.delicious.com http://tech.groups.yahoo.com/group/ydn-delicious/message/1999 ,

[8] Distribution points on a sphere, in ActionScript3, randomizing sperical coordinates : http://blog.massivecube.com/?p=9