This site is now just an archive over the rise and fall of Flash. The domain flashmagazine.com is available for sale
Login | Register
Unit Testing ActionScript 2.0 Using AS2Unit

Unit Testing ActionScript 2.0 Using AS2Unit

In the inaugural article of the Engineering RIAs series, Steven explained that Macromedia has positioned its Flash Technology as the key enabler in the development of Rich Internet Applications (RIAs). With the recent release of Flash MX 2004, Flash has been further catapulted to the forefront of this exciting new area of Enterprise Software Development. While there is little doubt that Rich Internet Applications gives us many benefits over traditional web based applications, it does increase the complexity of the client side development. This increased complexity necessitates the introduction of development practices regarded as the norm in the development of server side Enterprise applications using technologies such as J2EE and .NET. One of these key practices is Unit Testing.

Unit Testing

Unit Testing is the act of testing your code, at the code level. Unit Tests are programs written to test the software you develop; these tests are run frequently. Software developers are encouraged to write Unit Tests for every class they produce, and every part of a class that could possibly break should be tested. Each test will typically make a request to a class and verify that the result is as predicted. This act is known as an assertion.

This article will explain how AS2Unit can be used to test your ActionScript 2 classes.

Unit Testing goes hand-in-hand with Test First Design, a coding principal from the Agile Development movement that improves the quality, scalability and robustness of software applications. However, discussions of Test First Design go beyond the scope of this article so we will not discuss its merits. Test First design will be covered in more detail in a future article in the Engineering RIAs series.

Another key component of good software engineering is refactoring, the art of mercilessly changing code to reduce duplication and increase simplicity. Unit Tests give developers the courage to refactor, safe in the knowledge that their tests will protect them from any mistakes. Again, refactoring will be covered in a future article.

We have now explained why Unit Testing is a good thing, and why building Enterprise Rich Internet Applications requires the introduction of this development process. We will now show how Unit Testing can now be performed with Flash MX 2004.

About AS2Unit

The iteration::two development team are long-time advocates of JUnit, the Unit Testing Framework for Java. Recognising that Enterprise RIA development requires something similar, the iteration::two team developed AS2Unit, a free Unit Testing Framework for ActionScript 2.

AS2Unit is delivered as a Compiled SWF Component (SWC file) that can be dragged onto your stage in Flash MX 2004. Using AS2Unit is simple. After providing the component with the name of your test class, you need only run your movie and the component will kick into action, displaying the results of your tests back to you.

The AS2Unit component, and the source files referred to in this article, can be downloaded from the AS2Unit website at http://www.as2unit.org

Installing The AS2Unit Component

To install ASUnit, copy the AS2Unit component file, as2unit.swc, to your Components directory for your user. On Windows, this is Documents and Settings<USER>Local SettingsApplication DataMacromediaFlash MX 2004<LANGUAGE>ConfigurationComponents. Macintosh users will have an equivalent Components folder.

According to the Flash MX 2004 documentation, Flash MX 2004 needs to be restarted in order to have the component added to the Component Panel, but we have found that using the "Reload" option in the Components Panel menu will make the component available without a restart.

Using The AS2Unit Component

To use AS2Unit, open your Flash document and drag the AS2Unit component from the Components Panel onto your stage, as shown here:


image

You need to tell the AS2Unit component which test class you wish to run. To do so, select the AS2Unit component on the stage and enter the details into the Component Inspector (or the Properties panel).

image

The component has three parameters:

Test Class

This parameter is where you tell the AS2Unit component the test class to be run. The class name entered here must be fully qualified. ie. It must include the full package name as defined in the class statement of the class. In the Component Inspector above, the fully qualified class name is test.com.iterationtwo.TestMoney.

Run Tests

This parameter is set to true or false and tells the AS2Unit component whether the tests should be run when you run your movie. The initial default is true; setting it to false ensures means that your tests will not be run when you run your movie.

Display Results

This tells the component where the test results should be displayed. At present, the only option is for the results to be displayed on the Output Panel.

Unit Testing with AS2Unit

Now that we know how to set up AS2Unit, we will write our first unit test:

Making Money

Software Developers love to make money, so that’s what we’re going to do – create a Money class, and a set of unit tests to go along with it. We're going to make the Money class work in UK pounds and pence only, but this class could easily be converted to use Dollars and Cents.

There is no standard naming convention for test classes, nor for the packages they reside in. At iteration::two, we have settled on the convention of naming the test class with the same name as the class being tested, but prefixed by the word “Test”, and we mirror the package structure of the class being tested, inside a top level test package.

In our example, the Money class will have the fully qualified name com.iterationtwo.Money, and the TestMoney class will be test.com.iterationtwo.TestMoney.

Our basic test class, without any individual tests, is defined as follows:

import as2unit.framework.TestCase
 import com.iterationtwo.Money;
 class test.com.iterationtwo.TestMoney extends TestCase {
    public function TestMoney( methodName:String )
    {
       super( methodName );
    }
 }

All Test Classes must extend as2unit.framework.TestCase and must have a constructor that accepts a String argument which is then passed onto the TestCase base class via the super() function. You will notice that we import as2unit.framework.TestCase and the class we are testing, com.iterationtwo.Money.

A single test class contains many individual tests. Individual tests are identifiable as being public functions where the function name starts with the word "test". AS2Unit is case insensitive in this regard, but we recommend the naming convention for functions of starting the function with a lower case letter, then capitalising each subsequent word within the function.

We will now add our first test to the test class, naming the function testCreateMoney() so that it will be indentified as a test by the AS2Unit component:

public function testCreateMoney()
  {
   var pounds:Number = 10;
   var pence:Number = 0;
   var money:Money = new Money( pounds, pence );
   assertNotNull( money );
   assertNotUndefined( money );
 
   assertEquals( 10, money.pounds );
   assertEquals( 0, money.pence );
  }

The testCreateMoney test instantiates an instance of the Money class using local variables defined within the test function. Though these local variables are not necessary, and their values could just as easily have been hard-coded within the Money class constructor call, extracting their definitions to local variables clarifies their use to readers of the test class. This pre-empts the refactoring known as Replace Magic Number with Symbolic Constant.

Once the class has been instantiated, we want to test that the object is created correctly. This is where we first introduce assertions.

Assertions

Assertions are the fundamental manner in which you tell AS2Unit how to determine whether the class is acting as you expect it to. In our test methods, we assert that objects and attributes should be in a specified state. If that is not the case, the test fails and the AS2Unit framework tracks the failure for later display.

The basic assertNotNull and assertNotUndefined statements take a single parameter, that being the object being asserted. If the object has the value being asserted against, in this case, either null or undefined respectively, then the test fails.

The assertEquals statement take two parameters, the first being the expected result, the second the actual result. Again, AS2Unit will mark the test as having failed if the pounds and pence attributes of the money instance are not as we expect.

We will see later how you can supply an additional user message to assert statements, to provide additional information to you if the test fails.

Assuming a valid com.iterationtwo.Money class is in place, running the above test class via AS2Unit will results in the following

 

image

For each test run via AS2Unit, the component displays a single period. If the test is successful, AS2Unit progresses to the next test. If the test fails on one of your assert statements, AS2Unit will display an F after the period. If the test produces an error, as will happen if an exception is thrown, AS2Unit will display an E. AS2Unit keeps tracks of all tests and at the end of the test run, displays the total number of tests along with the number of failures, the number of errors and the time take for the tests to run.

To show how AS2Unit presents failing tests to you, we can change the testCreateMoney function. If we change the following line

   assertEquals( 10, money.pounds );
to
   assertEquals( 100, money.pounds );

and rerun the tests via AS2Unit, the following error is produced:

image

We told AS2Unit that we expected the value of money.pounds to be 100, but it was in fact 10. AS2Unit recognised this and informed us via the failed test.

With all failing tests, we are provided with the name of the test function and the name of the test class, so we can easily identify which test failed.

An individual test function will stop processing on reaching the first assertion failure within that function. So, in the example above, the assertion on the pence attribute of the money instance was never run because the test failed when asserting the value of the pounds attribute.

We will now add another test to the TestMoney class, to test the addition of monetary values via the Money class:

public function testAddMoney()
{
	var pounds1:Number = 3;
	var pence1:Number = 50;
	var money1:Money = new Money( pounds1, pence1 );
 
	var pounds2:Number = 3;
	var pence2:Number = 20;
	var money2:Money = new Money( pounds2, pence2 );
 
	var money3:Money = money1.addMoney( money2 );
 
	assertNotNull( &money was null&, money3 );
	assertNotUndefined( &money was undefined&, money3 );
 
	assertEquals( “Pounds should be 6”, 6, money3.pounds );
	assertEquals( “Pence should be 70”, 70, money3.pence );
}

This test instantiates two individual Money instances money1 and money2, and calls the addMoney() function to add them together, with the result going into money3. We then assert that the money3 instance is not null or undefined and that the pounds and pence attributes of money3 are as expected.

You will notice that the assert statements are slightly different in this test function. All assert statements within AS2Unit can accept an additional first parameter, which is a user error message. If a test fails, AS2Unit will show that message in addition to the message shown by default. If we change the assert within the above test to

assertEquals( “Pounds should be 666”, 666, money3.pounds );

to force a failure, the following is produced by AS2Unit:

image

As you can see, AS2Unit has shown the message defined within the test, prefixed to the message it would normally show.

AS2Unit also provides boolean assertions, as shown in the following test:

public function testEquals()
{
	var pounds1:Number = 3;
	var pence1:Number = 20;
	var money1:Money = new Money( pounds1, pence1 );
 
	var pounds2:Number = 3;
	var pence2:Number = 20;
	var money2:Money = new Money( pounds2, pence2 ); 
 
	assertTrue( &monies should be equal&, money2.equals( money1 ) );
}

The assertTrue() statement will cause the test to fail if the result of the money2.equals( money1 ) is not true. AS2Unit also provides an assertFalse() statement.

Testing For Exceptions

There is one slightly different statement also available to authors of test classes, that being the fail() statement. Authors of test classes can call fail() when they want to force a test to fail. fail() is often used when checking whether an expected exception is thrown within the class being tested, as in the following example:

public function testCreateMoneyBadConstruction()
{
	var e:Error;
	var exceptionThrown:Boolean = false;
	try
	{
		var money:Money = new Money();
	}
	catch ( e:Error )
	{
		exceptionThrown = true;
	}
	if ( !exceptionThrown )
		fail( &Exception should have been thrown& )
}

When this test fails, the following result is produced:

image

Below is the complete list of statements available to test class authors using AS2Unit:

assertEquals()
assertTrue()
assertFalse()
assertNotNull()
assertNull()
assertUndefined()
assertNotUndefined()
fail( userMessage:String )

Most of these have been covered above; the usage of the others is obvious from their names.

If you take a look at the complete source for the TestMoney class, you will see that we have a total of 13 tests. Running this complete test class via AS2Unit results in the following test output:

image

 

Our TestMoney class is now complete, until, that is, we want to give additional functionality to our Money class. When this occurs, we will write more tests and can be confident that any changes we make to the Money class will be protected by the existing tests.

Other Considerations

Runtime Access to Classes

In order that the AS2Unit component can find the specified test class at runtime, the test class must be included in the compiled SWF. The Flash MX 2004 compiler only includes classes identified as being required at runtime, and unless the test class is referenced in the timeline code or in an ActionScript 2 class, it will not be included in the compiled SWF and, therefore, AS2Unit is unable to run the tests.

If AS2Unit cannot find the class it has been told to run, an error similar to the following will be produced:

[AS2Unit] Class &test.com.iterationtwo.TestMoney& not found. If you are attempting to run your tests, please ensure you have provided the fully qualified class name (including any package) in the Component Inspector, and that the class has been included in your classpath and the compiled SWF. See the documentation for more details.

To ensure that our test class is included in the compiled SWF, we add a reference to it on Frame 1 of an actionscript layer on our timeline. In our example, we have the following single line within our actionscript layer:

image

Note that there is no need to instantiate an instance of the class; a reference is enough for the Flash MX 2004 compiler to include the class in the compiled SWF.

If you have included the above, but AS2Unit still produces the error, check that your classpath within Flash MX 2004 has been set to include the location of your classes. Flash MX 2004 has a global system classpath and a movie level classpath. Please refer to the Flash MX 2004 documentation for details on how to set them and how they differ.

Publish for Flash 7

In order to use AS2Unit, the Flash Document onto which the AS2Unit component is dragged must be compiled for Flash 7. This is due to the advanced ActionScript 2 features used by the AS2Unit component.

However, this does not mean that applications tested with AS2Unit must be compiled for Flash 7. If your application is to be compiled for Flash 6 or earlier, create a separate Flash document, to be used for testing only, and drag the AS2Unit component onto its stage. That document can then be compiled for Flash 7.

Known Issues

Output Panel

When you drag the component onto the stage, or open an existing Flash file with the AS2Unit component on the stage, warning messages can appear in the Output Console. These warnings can be ignored.

The Future

This initial release of AS2Unit provides a basic implementation of a framework to allow you to test your ActionScript 2 classes. Future versions of the AS2Unit component will introduce a richer feedback on tests, as they run. Keep an eye on the AS2Unit Website for future releases, or join up for the as2unit mailing list via the website.

Summary

In this article, we have introduced Unit Testing and shown how we can test ActionScript 2 classes by creating test classes that extend TestCase base class of the AS2Unit framework. We have shown how the AS2Unit component can be used to run these test classes, and provide the results back to the test author.

In the next article in the AS2Unit series, we will explain how the setUp()%2
Visit AS2Unit website

 

Get new stories first

Click to follow us on Twitter!

 

Comments

No comments for this page.

Submit a comment

Only registered members can comment. Click here to login or here to register