Testing and Debugging (Open Source Flash Development) Part 1

This topic covers the following:

■    Writing and using unit tests for ActionScript 2 and ActionScript 3 development

■    Using advanced logging techniques in Xray

■    Using Xray to debug applications

■    Using Xray to find performance bottlenecks

In this topic, I will focus on three open source projects that can aid you in these tasks. Those projects are AsUnit, FlexUnit, and Xray. AsUnit and FlexUnit provide a framework for unit testing. Xray provides a mechanism for logging and runtime inspection of a SWF.

Unit testing

Before I get into the details of AsUnit and FlexUnit, you may find it helpful to understand what a unit test is. Unittests are small tests written in code by the developer to verify pieces of a program are working correctly independently of other parts of the application. Usually, these tests are automated and run at various points in the development cycle (such as before every build or after every version control submission).

A simple unit test might be written as follows:

tmpeeee-159_thumb


In this fictional example, the method called myMethod is expected to return 10 when it’s passed the value of 5. The test runs the method, checks the return value, and traces out an error if something goes wrong. You could continue in this manner, adding more and more tests and methods to test. But managing a large body of tests like this can quickly become a maintenance nightmare. Luckily, there are open source libraries to make writing and maintaining unit tests easy. Later in this topic, I will show you how to use AsUnit for ActionScript 2 development and FlexUnit for ActionScript 3 development. Ifyou find yourself doing both ActionScript 2 and ActionScript 3 development and you want to learn only a single library, it’s helpful to know that AsUnit also has ActionScript 3 support.

Many people believe these tests should be written before the development of the code they’re meant to test in a process often called test-driven development. By adequately defining the functionality that a piece of code must implement, the developer gains a deeper understanding of what and how it should be implemented. It also provides a solid benchmark for determining whether a chunk of code is working correctly.

Test cases, suites, and runners

There are a lot of unit testing frameworks out there covering just about every language one could write a computer program in. Some popular packages include JUnit for Java, Boostfor C++, and NUnit for C#. Many of the unit testing frameworks either are direct ports or have strong influences from Java’s JUnit. Because of this, they have a common organizational structure consisting of test runners, test suites, and test cases. Both AsUnit and FlexUnit follow this organizational model.

Test case

A test case is a group of individual tests that are generally related. Depending on the size of your application, you might choose to have a single test case, a test case for each class, or a separate test case per package that you want to test. For very detailed tests, it’s even possible to have multiple test cases for a single class. In both AsUnit and FlexUnit, a test case is a class with each individual test represented by a method in that class. Some pseudocode for a test case might look like this:

tmpeeee-160_thumb

 

 

tmpeeee-161_thumb

Test suite

A test suite is a grouping of test cases. It makes organizing similar test cases into logical chunks easier. In most unit test frameworks, a test suite is just a special kind of a test case that implements the same interface. Some pseudocode for a test suite follows:

tmpeeee-162_thumb

Test runner

The test runner is the entry point for a set of unit tests. If you were using MTASC, it would contain your main() method, or if you were using the Flash IDE, it might be the first frame of your initial timeline. The purpose of the runner is to initialize any test suites you may have and then run through and execute each suite. Some pseudocode for a test runner might look like this:

tmpeeee-163_thumb

Structuring tests

This three-tiered layering of the testing structure may seem like overkill, but it gives a great amount of flexibility. The following example shows how a developer might structure tests and take advantage of some of this flexibility.

1.    The developer creates a test case for every class in the project he wants to test.

2.    He creates three test suites:

a.    One of these suites contains only a single test case that represents the piece of the application on which the developer is currently working. He updates this suite whenever he moves on to another part of the application, which gives him a quick way to see whether his current work is good.

b.    The second suite contains all the test cases that require a connection to a server.

The third suite contains all the test cases that do not require a connection to a server. This allows him to run these tests whenever he wants without having to worry about maintaining the latest version of any server software (which another developer might be responsible for).

3.    He creates three test runners:

a.    The first will contain only the first test suite.

b.    The second will contain the second and third test suites.

G The last will contain only the third test suite.

If you refer to Figure 5-1, it becomes obvious which test cases will be run for each test runner that is executed. In this example, the developer might use Test Runner 1 often to quickly test what he’s working on at the moment. Test Runner 2 might be used right before a full build of the software to make sure all the components are working well together. Test Runner 3 might be run on a schedule or as a response to version control activity to make sure all the non-networked code is functioning correctly.

Segregating tests by networked/not networked is only one example of an organizational structure for your tests. Creating test suites that are grouped by functional area or by the developer responsible are other examples. Since a test case can be in multiple suites, you’re not limited to a single grouping. You could have test suites for both networked status and developer responsible and then select which suites to run in your test runners.

The relationships between test runners, suites, and cases for the example scenario

Figure 5-1. The relationships between test runners, suites, and cases for the example scenario

Creating n-tiered testing hierarchies

In both AsUnit and FlexUnit, a test suite is also a test case. This means a suite can be added to another suite to create complex trees of test cases, as shown in the following illustration.

tmpeeee-165_thumb

As you can see, the first test suite contains two test cases, plus another suite. That suite can then contain other cases or even other suites. This allows you to have a complex hierarchy of test cases.

Both FlexUnit and AsUnit provide all the features previously mentioned. They can manage complex hierarchies of tests, report the status of tests, and help you organize your code. In the next few sections, we’ll explain how to actually use those frameworks to write some unit tests.

Getting started with AsUnit

To install AsUnit, head over to the AsUnit web page at http://www.asunit.com. Download the “Framework” package from midway down the page. This includes AsUnit sources for old ActionScript 2 applications, for newer ActionScript 2 applications (Flash 7, 8, and 9 only), and for ActionScript 3 applications. Once you’ve downloaded the file, extract it to a temporary location. Since I’m focusing on the ActionScript 2 support in this section, copy the contents of your AS25 folder from the temporary location to a location your compiler can find.If you haven’t, simply placing it in your project folder will work equally well.

In this section, you chose the AS25 directory from the AsUnit package. This contains the code for the ActionScript 2 testing environment focused on development for Flash Player version 7 and newer. The AS2 directory contains the source for testing applications that require Flash Player version 6 and newer. The AS3 directory contains the ActionScript 3 sources suitable for Flash or Flex development.

Now that you have AsUnit installed, you need something to test. In previous topics, you were working on a website to view recipes. In such a website, it might be useful to have routines to convert units of measure. Let’s create a simple UnitConverter class with two methods for changing pounds to ounces and tablespoons to teaspoons:

tmpeeee-166_thumb

Since these methods are so simple, it makes sense to write them before the unit tests. For more complicated methods, it’s oftentimes easier to write stub functions that return dummy data and then write your unit tests. Then, you can use those unit tests to execute the code you’re working on in an isolated manner.

I intentionally made a mistake in the poundsToOunces method to help illustrate the benefits of using a unit test in the following sections. There are really 16 ounces to a pound, and not 13. We’ll fix this in a moment.

An overview of key AsUnit classes

These are the three main classes in AsUnityou will use on a regular basis:

■    asunit.framework.TestCase: This is the base class for each test case. You can extend this to create a new test case.

■    asunit.framework.TestSuite: This class can combine a set of test cases into a suite. As mentioned, it descends from TestCase, so you can also add a TestSuite object to a test suite.

■    asunit.textui.TestRunner: This is a basic test runner that can run one or more test suites.

You can learn more about these classes by browsing through their source code. In the next few sections, you’ll be using them to create your sample unit testing framework.

Creating an AsUnit test case

Now that you have something to test, let’s create an AsUnit test case. First, import the class you want to test and the TestCase class from the AsUnit framework:

tmpeeee-167_thumb

Now, create a new class called ConverterTest; for this example, I’ll show how to create it in the com.friendsofed.unittests package. If you choose to do this, make sure to put the class in an appropriate directory structure. This class should extend the TestCase class. The TestCase class takes a string in the constructor that represents the name of the method to test, so also create a constructor in your class that calls that.

tmpeeee-168_thumb

Next, create a method to test the poundsToOunces method. AsUnit automatically looks for methods that begin with the word test as valid tests to run. Let’s call this method testPoundsToOunces:

tmpeeee-169_thumb

Inside this method is where the real testing begins. The TestCase class defines a series of “assert” methods to make testing easy. These include the following:

■    assertEquals: Tests whether two objects are equal

■    assertSame: Tests whether two objects are the same object

■    assertNull: Tests whether a value is null

■    assertNotNull: Tests whether a value is not null

■    assertUndefined: Tests whether a value is undefined

■    assertNotUndefined: Tests whether a value is not undefined

■    assertTrue: Tests whether a value is true

■    assertFalse: Tests whether a value is false

The first parameter to all of these methods is a message to be displayed if the test fails. Then assertEquals and assertSame take two parameters; the others take a single parameter. When the method is run, it evaluates its parameters, decides whether the test passes or fails, and reports on any failures. For example, a call to assertTrue("This will always fail", false); will always cause the message “This will always fail” to be displayed when the test is run because its second parameter is not true. A call to assetEquals("These aren’t equal", a, b) will cause the message “These aren’t equal” to appear depending on whether the variables a and b are equal.

For the testPoundsToOunces test, use the assertEquals method, and feed in some set values and expected results as follows:

tmpeeee-170_thumb

In these examples, you’re passing in an expected return value and then calling the method. Since you’re using assetEquals, the test will pass if the two values are the same and fail if they aren’t. You’ll create a similar test method for the UnitConverter.tablespoonToTeaspoon method:

tmpeeee-171_thumbtmpeeee-172_thumb

You now have a valid test case that you can use for the rest of the project to verify that the UnitConverter class works correctly and to identify any regressions that might occur during the course of development.

Next post:

Previous post: