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...