Introduction to Unit Testing

Unit testing is relatively new to ActionScript projects. Although FlexUnit has been around for a while it wasn't intuitive to set up and there were a lot of inconsistencies with the documentation. Lucky for us, FlexUnit is now built into Flash Builder 4. Although the documentation is still sparse, this tutorial will be a good primer for setting up a Test Suite, going over several unit test examples and showing how to run/analyze them.

For those of you who aren't familiar with unit testing let's see what Wikipedia has to say

In computer programming, unit testing is a software verification and validation method where the programmer gains confidence that individual units of source code are fit for use... Unit tests are typically written and run by software developers to ensure that code meets its design and behaves as intended. - wikipedia

In this tutorial I'll show you how I set up a few simple unit tests on my Flash Camo framework. You'll need to download a copy of Flash Builder 4 (in Beta right now) to follow along. Also, you'll want to download the latest version of Flash Camo (2.2.1) from here.

Step 1: Setting up a Unit Test Project

Finally we'll need to tell our project where to find our SWC, right-click on the project and go into its properties. Go to the ActionScript Build Path and select the Library Path tab. Click on Add SWC Folder and point it to the lib/swcs directory.

Now that we have everything setup, we can begin doing some basic unit testing. It's important to note that you cannot do unit testing directly in a Flex Library Project. That's not an issue for this project but if you want to test a library of code what I usually do is set up a new project (like we are doing here) then link the two projects together and create all the tests in the new project.

If you are not working in a Flex Library project and want to test your code you can simply create your tests in the same project. I would suggest keeping the two separate, this way you can clearly see what are test classes and what are real classes. We'll get into this a little later when you see how we set up the test.

Step 2: Drafting a Plan

Before I start anything, I take a moment to figure out exactly what I'm going to do. This is very critical when setting up unit tests. This kind of development is called Test Driven Development. I think I see another definition coming up:

Test-driven development (TDD) is a software development technique that uses short development iterations based on pre-written test cases that define desired improvements or new functions. Each iteration produces code necessary to pass that iteration's tests. Finally, the programmer or team refactors the code to accommodate changes. A key TDD concept is that preparing tests before coding facilitates rapid feedback changes. - wikipedia

Since this is a short intro we're going to use an existing code library to test against. However, as you build your own application you should be writing tests along the way to validate that the code works and that any changes/refactoring doesn't break your code's implementation. Here's an outline of the tests we'll perform:

Create an instance of a CamoPropertySheet.

Validate that it can parse CSS.

Test the number of selectors it found.

Test that the CamoPropertySheet can be converted back to a string.

Test what happens when we request a selector that was not found.

Validate clearing a CamoPropertySheet.

If you're new to Flash Camo you can check out the intro I wrote (part 1 and part 2) but you can easily do this tutorial without any knowledge of how the framework works. Again, this is simply going to be a code library for us to test with.

Step 3: Creating Our First Test

Now that we have a plan for doing our testing, let's create our first test. Right-click on your project and select New > Test Case Class

You will now be presented with the creation wizard. It should be familiar to anyone who has created a class in Flex/Flash Builder before. Here is what the window looks like:

Let's talk about a few of the new fields in the wizard. We can start with the fact that Superclass is already filled in for us: flexunit.framework.TestCase. You can't change this and probably shouldn't. All this does is extend the base test class from the Unit Test Framework. Next you'll see a few check boxes for code generation. Leave all of these checked by default. Finally there is a field for the Class we want to test.

Since we're going to test Flash Camo's CamoPropertySheet let's fill the following values into the form:

Hit finish and we should have our first test ready for us to add some code to.

Step 4: Anatomy of a TestCase Class

Let's take a look at the code Flash Builder has generated for us:

We start out by having a private property called classToTestRef which is set to the value of our CamoPropertySheet. This allows the test to create an instance of this class and forces the compiler to import it when we run our test.

The next important method is setUp. This is where we'll create an instance of our test class, configure it and make sure everything is ready for us to perform a test.

The last method here is tearDown. This is where we'll destroy our test class' instance when the test is complete. This is very important when running multiple tests.

You may have noticed at the end of the class there's another method called testSampleMethod which is commented out. This is an example of how you would set up a single test.

Each test will be a method we add to this class. When we run the Unit Test Harness it will automatically call all of our methods dynamically, of course starting with setUP and finishing with tearDown.

Now that we have a basic understanding of the TestClass setup let's look at running it.

Step 5: Running the Unit Test

Before we can run this we'll need at least one test. Let's uncomment the testSampleMethod for this example.

Once you've uncommented the testSampleMethod let's right-click on our project and select Run As > Execute FlexUnit Test.

You should now see the following window prompting us to select which test we want to run.

As you see, this is set up for when we have lots to test but for now we only have one testSampleMethod to run. Click Select All and hit OK.

After the test is run, your web browser will pop up with the following page:

If you go back to Flash Builder you'll also see this in the FlexUnit Results panel:

See how easy this was to run? We've performed our first unit test and already we have a single error. Before we move on to fix this error let's talk about these two windows.

The webpage that was open should automatically close but sometimes it doesn't. This is a simple swf that performs our tests in FLash and outputs some data the Flash Builder reads to display to final results of the test. You can ignore this webpage for the most part.

The FlexUnit Results panel is where all of the results will be shown as well as a place that allows you to organize and filter the test feedback. We'll go over this a little later in the tutorial when we actually have something to test.

Step 6: Set Up

Before we can really get into testing we'll need to setup our test class. let's add the following code to the setUp method:

Here's what's happening in this setup: in order for us to test our CamoPropertySheet we are going to need some CSS as a string. Normally I would load in the CSS from an external file but since we're doing a simple test I just create a new XML block and put the CSS text inside of the first node. Normally, you don't have to wrap CSS for the CamoPropertySheet inside of XML but when working with large strings inside of the editor I find it easier to use xml since you can wrap the text and it retains some formatting.

Next you'll see that we set our rawCSS property to the xml's string value. This converts the xml into a string. Then we create a new CamoPropertySheet. Finally, we tell the sheet to parse the rawCSS.

That's all there is to setting up this particular class. The setup is different for each class you test. It is important to demonstrate that we're doing the bare minimum to get a class ready to be tested and we can't test a class without values can we?

Step 7: Our First Test

Let's get right to it. Once a CamoPropertySheet has successfully parsed a css string we can request an array of Selector names to verify everything has indeed been parsed. For those not familiar with CSS jargon, a selector is the name of a css style ie baseStyle{...} would have a selector called baseStyle.

As you can see we get an array of selector names. Next we get the total and introduce our first test assetEquals. In the next step I'll explain the assertMethods in more detail, but let's just run this and see if the test passes.

When you run the test you should see the following in the FlexUnit Results panel:

Nice, our test passed. We received the exact number of selectors that we were expecting. Let's look at what assert tests we can use.

Step 8: Assertions

In unit testing we run assertions. Each assertion handles a particular type of test. Here is a brief overview of the most common assertions you will probably use:

assertEquals - test to see if one value equals another.

assertFalse - test to see if value equals false.

assertNotNull - test to see if value is not equal to null.

assertNotUndefined - test if value is not undefined.

assertNull - test if value is null.

assertStrictlyEquals - test to see if two values strictly equal each other.

assertTrue - test to see if value is true.

assertUndefined - test to see if value is undefined.

Now, before we test an example of each one let's set up our tearDown method.

Step 9: Tear Down

This is going to be a very short step but it's a very important one. Let's add the following line to our tearDown method after super.tearDown():

sheet = null;

What this basically does is remove the reference to our CamoPropertySheet so the Garbage Collector can remove it.

You should always set up your tearDown especially when running multiple test classes or a large test suite.

Step 10: Assert Equals

We've already seen an example of this before in Step 7, but let's go through and add another assertEquals. Here is the next test we'll perform:

Compress CSS text (remove white spaces, special characters and other obstacles the css parser may not be able to recognize) since the CamoPropertySheet automatically compresses css test when it is parsed.

Convert the CamoPropertySheet into text (this will be a compressed version of the rawCSS we used earlier).

Compare that the CamoPropertySheet text is equal to our compressed css string.

As you can see, we're simply requesting a fake style name testSelector. We check to see if the selector's name is the default name applied when no selector is found. Finally we pass the exists variable to the assertFalse method. When you run this you should now see 3 passes totaling a success.

Step 12: Assert Not Null

Next we want to make sure that the text value from our CamoPropertySheet is never null. Let's look at how to structure our test:

call toString on our CamoPropertySheets instance and test to see if it is not null

This is pretty straight forward so when you run the test we should now have 5 successes. Every time we run the test we can check to see the names of our method tests by clicking on the Default Suite folder our FlexUnit Results Panel back in Flash Builder.

Step 13: Assert Not Undefined

In this next test we're going to follow up on the empty selector test to verify that every selector has a selectorName.

The first two lines are self explanatory; we simply ask for two selectors and one of which we know does not exist. When we do the assert however you'll notice we're passing in two values instead of the normal one we've done up until this point. This is not a unique example, in fact each of the assert methods allow you pass in any number of values to test. Here we simply make sure that selectorA and selectorB are not undefined.

Step 14: Assert Strictly Equals

Here is an example of how to strictly compare two objects. Here I'm using strings which may not be the best use of this example but it's good to see the test in action. What are we going to do?

Clone the CamoPropertySheet.

Test that the string value of our CamoPropertySheet is equal to the value of a cloned CamoPropertySheet.

As you can see we call the clone method of the CamoPropertySheet to get back an exact copy of the PropertySheet. Next we run it through the assert test by calling the toString method on each. If the returned CSS test is the same we have a success for the test.

Step 15: Assert True

Now we want to test that when we request a selector, it has a property we are expecting. Here is the test:

As you can see here we are expecting our baseStyle selector to have the x property. If this exists we can assume that it was correctly parsed from the CSS string. Since it exists we have passed this test.

Each of these tests becomes self explanatory as to how you implement them. Let's look into what happens when we fail a test in the next two steps.

Step 16: Assert Undefined

We're going to test for undefined now but Flash Camo has been designed to not return undefined. So the following test will fail. Let's check out what we're going to test for.

Now let's run this test and go onto the next step to discuss the results.

Step 17: Failing A Test

If you did the previous step and ran the unit test, you should see the following in the FlexUnit Results panel:

Notice how we have 1 failure from our testClear method?

If you double-click on the failed test in the Test Results panel you will jump to the source of the test that failed. This is a great way to correct your mistake or alter the test so it doesn't fail. There isn't much more to failing a test then this. Every test that fails will show up in this panel, you can tell the panel to only show failed tests by clicking on the red exclamation mark above where it tells you how many errors you had.

If you run the test again you'll see that it will pass. Now you have 7 out of 7 passed tests and this class is successfully working. Let's talk about setting up unit tests for your own custom classes.

Step 18: Auto Generating Test Classes

Up until this point we have been testing a precompiled library, but you may be interested in how this will work on your own classes. We're going to alter the doc class a little then run a custom unit test on it. To get started, replace all of the code in the UnitTestIntro class with the following:

Once you have the code in place, right-click on UnitTestIntro and select New > Test Case Class. If you look at the wizard this time you'll see all of the fields are filled in for us:

This time, instead of clicking Finish, hit next and look at the following window:

Here you can select all of the public methods of that class to test. Notice how our getters for firstName and lastName are not part of this list. Unit testing can only be performed on public methods. Also, you will see every inherited method of the class so we have Sprite/DisplayObject methods here since our doc class extends Sprite. Select isLoggedIn and hit finish.

If you scroll down to the bottom of the new test class that was just generated you'll see it has automatically added in a testMethod for isLoggedIn.

When testing your own code Flash Builder can help automate the process of scaffolding your tests. This is a great help when dealing with large classes that have lots of methods.

Step 19: Best Practices

By now you should have a solid understanding of how Unit Testing in Flash Builder works. You may even be ready to start setting up your own test. There are a lot of things I wasn't able to cover in this short tutorial so here are some things to keep in mind when creating your tests.

Keep your code small and easily testable. Make short methods, break code down into "units" to help facilitate testing. It's ok to have lots of methods in your classes. Not only does it help you when unit testing but also when extending classes and dealing with inheritance overriding.

Test a behavior and not a method. This tutorial showed you how I would interact with the instance of the CamoPropertySheet. I was testing behavior responses for the underlying parsing/retrieval system. Make sure you are not testing that functions simply return values but that the underlying logic is correct. Did something get parsed, did the dependent methods do what they were expected to do?

Keep your test names clean. You should be able to easily understand what is going on by simply looking at the name of the test method. Remember this code is not compiled into your final application so if you have incredibly long method names, that's ok as long as they're descriptive.

Don't rely on the console window for your test. This means you shouldn't expect a developer to watch trace outputs to see if the test is working correctly. Instead make the test fail or succeed and not output its results.

Do a search for Unit Testing in other languages to see how it has been implemented elsewhere. Also pick up a book on Test Driven Development (TDD).

Conclusion

As you can see, setting up Unit Testing is very simple, but creating applications revolving around Test Driven Development is an art form in its own right. Hopefully after this intro you'll be comfortable setting up a simple test to validate that your code works as expected. As you rely more and more on Unit testing the number of bugs in your code will dramatically go down. As long as you remember to code towards passing a test, keeping your methods small and validating your unit tests often, you'll be well on your way to building more stable code.