Improving Code Quality with Flash Builder 4 and PHPUnit

September 13, 2010

Tutorials

By: Nathan A. Good

Unit testing is becoming a fairly common element of software projects because of its promise of providing better quality to the code base. A unit test is a class or method that exercises your code for purposes of verifying that it functions correctly. During the course of your project, these tests remain as artifacts that can be used over and over again to verify that your code is always working correctly. The higher percentage of your production code you exercise, the higher degree of certainty you can have that your code is doing what it was designed to do.

This article is about improving your code quality in a sample project using Adobe Flash Builder 4 to build a rich user interface (UI) and connecting to PHP services via Zend Action Message Format (AMF). In this article, you learn how to write tests using two frameworks: FlexUnit and PHPUnit. You learn how to run the tests and view the results. Finally, you get additional techniques that you can use to improve your code quality.

To get the most out of this article, you should have the following software and versions installed:

  • PHP version 5.3 or later
  • Zend AMF
  • Adobe Flash Builder 4 (plug-in or stand-alone version)
  • Eclipse PHP Development Tools (PDT) plug-in

Unit Testing Overview

At first, writing unit tests can seem counterproductive. It’s easy to be confident in code that you write, especially if you’re an experienced software engineer. If you’re under pressure to get a product to market quickly, writing code that doesn’t directly make up your application can be easy to eliminate. However, the extra work put into writing unit tests can save hours of debugging in addition to the embarrassment or business impact of issues that could have been caught by unit tests.

Why Test?

Aside from giving you an indication that your code is complete, there are two good reasons to write unit tests: regression and documentation.

Most of your application’s life will not be spent in the development phase but rather in ongoing maintenance. The unit tests that you build during initial development can serve as a solid set of regression tests. These regression tests help you ensure that as you change code during the maintenance cycle, the original functionality works as originally intended.

Second, unit tests can provide a level of documentation for your code. If you write tests with useful names, the tests will give clues to why the code should work a certain way. Unit tests should also provide real-world examples of how some of your classes and methods should be used in the application, including pre- and post-conditions.

Available Frameworks

Entire frameworks written in many different languages make writing tests for your code significantly easier. Flash Builder is no exception. If you’re using Flash Builder 4 to build a rich Internet application (RIA), you already have built-in tools that integrate with FlexUnit, a framework for testing Flex code. The built-in FlexUnit integration allows you to write unit tests that exercise your Flex code.

Just as you can use FlexUnit to test your Flash Builder 4 code, you can use PHPUnit to test your PHP code.

Using PHPUnit

PHPUnit is a unit testing framework for PHP. It allows you to test your PHP code and see the results on the tests. PHPUnit also has some advanced features, such as the ability to generate test results in JUnit’s XML format, which can then be read by other tools.

Installing PHPUnit

To install PHPUnit, use the PHP Extension and Application Repository (PEAR) installer. PEAR comes with every version of PHP later than 4.3, which is perfect, because the latest versions of PHPUnit requires PHP version 5.1.4 or later. To use the pear command to install PHPUnit, type:

pear channel-discover pear.phpunit.de
pear channel-discover pear.symfony-project.com
pear install phpunit/PHPUnit

Note: If, for some reason, you would like to install PHPUnit manually, you can do so by following the online instructions.

After you’ve installed PHPUnit, verify that it is installed correctly by typing phpunit on the command line.

Building a Test with PHPUnit

Now that you’ve installed PHPUnit, you can use it to build a test for the class that your application’s PHP services use. The example service used in this article is a class with a method that calculates the amount of time between the start of the work day and the end of the work day.

Although the Adobe ActionScript code in the Flash Builder 4 project could certainly handle this simple operation, pretend for the sake of this article that there is some requirement for the time calculation to be done on the server side.

Creating an Example Class

The example class here is a PHP class that has a method on it for calculating the difference between the start time and end time, and then subtracting the time taken out for lunch. The class looks like this:

<?php
class TimeCalc
{

    public static function calculateTime($start_time, $end_time)
    {
        if (empty($start_time)) {
            throw new InvalidArgumentException("Illegal argument:  must supply a start time");
        }

        if (empty($end_time)) {
            throw new InvalidArgumentException("Illegal argument:  must supply an end time");
        }

        $start = DateTime::createFromFormat('H:i', $start_time);
        $end = DateTime::createFromFormat('H:i', $end_time);

        $interval = $end->diff($start);

        return $interval-&gt;format('%h:%M');

    }

}

Writing the Test

Now that you have the class written, you can write tests to verify the functionality of the methods on the class. Good tests should try more than just the “happy path,” which is the path through your code when all the inputs and conditions are at their expected values. Good tests should try to use out-of-bounds values or null values to make sure your code responds appropriately.

<?php

require_once 'PHPUnit/Framework.php';
require_once 'TimeCalc.php';

class TimeCalcTest extends PHPUnit_Framework_TestCase
{
    public function testCalculateTime()
    {
        $actualStart = '8:00';
        $actualEnd = '17:00';

        $expectedResult = '9:00';

        $this->assertEquals($expectedResult, TimeCalc::calculateTime($actualStart, $actualEnd));

    }


}

You may choose to write your code to defend itself against bad input by throwing meaningful exceptions. You should write unit tests to verify that these exceptions are being thrown when you think they should be thrown. These example test methods make sure that if the input is incorrect, the appropriate exceptions are being thrown.

To expect exceptions in your test, use the @expectedException annotation in the comment block as shown here:

    /**
     * @expectedException InvalidArgumentException
     */
    public function testCalculateTime_Start_Exception()
    {
        $empty = NULL;
        $actualEnd = '17:00';

        $expectedResult = '9:00';

        $this->assertEquals($expectedResult, TimeCalc::calculateTime($empty, $actualEnd));

    }
    
    /**
     * @expectedException InvalidArgumentException
     */
    public function testCalculateTime_End_Exception()
    {
        $actualStart = '8:00';
        $actualEnd = NULL;

        $expectedResult = '9:00';

        $this->assertEquals($expectedResult, TimeCalc::calculateTime($actualStart, $actualEnd));

    }

When these tests are run, you see the following output:

$ phpunit TimeCalcTest.php

PHPUnit 3.4.13 by Sebastian Bergmann.

...

Time: 0 seconds, Memory: 4.00Mb

OK (3 tests, 3 assertions)

Create a test failure by removing one of the thrown exceptions. When you run the test again, you’ll see:

$ phpunit TimeCalcTest.php 

PHPUnit 3.4.13 by Sebastian Bergmann.

.E.

Time: 0 seconds, Memory: 4.00Mb

There was 1 error:

1) TimeCalcTest::testCalculateTime_Start_Exception
InvalidArgumentException: Illegal argument:  must supply a start time

TimeCalc.php:8
TimeCalcTest.php:28

FAILURES!
Tests: 3, Assertions: 2, Errors: 1.

Running the Tests in the IDE

You can use the integrated development environment (IDE) to run the PHPUnit tests without having to use the command line. To run files in your workspace with PHPUnit, create an external tool configuration that will allow you to execute any open PHPUnit test from the IDE with the click of a button.

To add the run configuration, click Run > External Tools > External Tools Configuration. Click the New icon to add a new configuration, then perform the following steps:

  1. In the Name field, type a useful name for the external run configuration, such as Run with PHPUnit.
  2. In the Location field, type the full path name to the phpunit executable. This path will vary from system to system but will be similar to /usr/bin/phpunit or /usr/local/php5/bin/phpunit.
  3. For better performance, click the Build tab, and make sure Build before launch is not selected. There is no reason to build the projects in the workspace to run PHPUnit.
  4. Supply the currently selected file as the argument to PHPUnit. To do this, type ${resource_loc}, which is the full path name of the currently open or selected resource.
  5. Click Apply to save the external tool configuration.

Now, with the file open or selected in the PHP Explorer view, you can run the test by clicking the icon for running external tools or by clicking Run > External Tools > Run with PHPUnit. The results will be displayed in the console.

Using FlexUnit

FlexUnit is a unit testing solution for testing your Flex code. Before Flash Builder 4, FlexUnit was a separate entity that you would have had to download and include in your project. But in Flash Builder 4, FlexUnit is a part of the IDE.

Building a Test with FlexUnit

The sample Flex UI in this article allows a user to input the number of hours for each day in the week, then it uses the PHP services to do the actual calculation for each day. To demonstrate the unit testing on the Flash Builder 4 side, there is a class that adds up each day in the week for daily total, then has an additional method to display the average for each day.

Creating an Example Class

The example is shown here:

package util
{
    public class WeeklyHourCalculator
    {
        public function WeeklyHourCalculator()
        {
            // nothing to do here...
        }
        
        /**
         * Calculates the total number of hours for the week.
         */
        public static function totalWeeklyHours(... values) : Number
        {
            var total : Number = 0;
            
            for (var i : int = 0; i < values.length; i++)
            {
                total += values[i];
            }
            
            return total;
        }
        
        /**
         * Gets the average by adding the values up and dividing by the number
         * of non-zero values.
         */
        public static function averageDailyHours(... values) : Number
        {
            var total : Number = 0;
            var nonzero : Number = 0;
            
            for (var i : int = 0; i < values.length; i++)
            {
                total += values[i];
            }
            
            for (var i : int = 0; i < values.length; i++)
            {
                if (values[i] > 0)
                {
                    nonzero++;
                }
            }
            
            return (total / nonzero);
        }
    }
}

For brevity, I’ve left out the details of how the Flash Builder 4 interface connects to PHP. More about how to connect Flash Builder 4 with PHP using Zend AMF can be found at Zend AMF’s web site.

When the user enters data and clicks Calculate, the Flash Builder 4 MXML file uses the WeeklyHourCalculator class to calculate the total and the daily average.

Testing Methods

Flash Builder 4 contains a lot of features for making it easy to create unit tests using FlexUnit. To create the test class, click File > New > Test Case Class. The New Test Case Class Wizard appears.

Enter the name of the test. It’s a good convention to use the name of the class that you’re testing and append Test or Tests onto the end of the class name. Make sure to select the test’s source folder so that you have a clean separation between the files that should be distributed and files that are used for testing.

Select the Select class to test check box, and then click Browse to find the WeeklyHourCalculator class. Click Next.

Next, select the methods that you want to test. When you’re done selecting the methods, click Finish. Flash Builder 4 will have generated a FlexUnit test for you. If you run the tests now, they will fail because of the Assert.fail declaration in them:

package util
{
    import flexunit.framework.Assert;
    
    import util.WeeklyHourCalculator;
    
    public class WeeklyHourCalculatorTests
    {        
        [Before]
        public function setUp():void
        {
        }
        
        [After]
        public function tearDown():void
        {
        }
        
        [BeforeClass]
        public static function setUpBeforeClass():void
        {
        }
        
        [AfterClass]
        public static function tearDownAfterClass():void
        {
        }
        
        [Test]
        public function testAverageDailyHours():void
        {
            Assert.fail("Test method Not yet implemented");
        }
        
        [Test]
        public function testTotalWeeklyHours():void
        {
            Assert.fail("Test method Not yet implemented");
        }
        
    }
}

Change the assertions to something meaningful, such as:


                [Test]
                public function testAverageDailyHours():void
                {
                        var expectedValue : Number = 40;
                        Assert.assertEquals(expectedValue, WeeklyHourCalculator.totalWeeklyHours(8, 8, 8, 8, 8));
                }
                
                [Test]
                public function testTotalWeeklyHours():void
                {
                        var expectedValue : Number = 8;
                        Assert.assertEquals(expectedValue, WeeklyHourCalculator.averageDailyHours(8, 8, 8, 8, 8));
                }
                
                [Test]
                public function testTotalWeeklyHours_ThreeSupplied():void
                {
                        var expectedValue : Number = 8;
                        Assert.assertEquals(expectedValue, WeeklyHourCalculator.averageDailyHours(8, 8, 8));
                }
                
                [Test]
                public function testTotalWeeklyHours_FourSupplied():void
                {
                        var expectedValue : Number = 6.5;
                        Assert.assertEquals(expectedValue, WeeklyHourCalculator.averageDailyHours(2, 6, 8, 10));
                }
                
                [Test]
                public function testTotalWeeklyHours_OneSupplied():void
                {
                        var expectedValue : Number = 1;
                        Assert.assertEquals(expectedValue, WeeklyHourCalculator.averageDailyHours(1));
                }

To run the FlexUnit tests from the IDE, use the project’s context menu to select Execute FlexUnit Tests. The tests will run in your browser in a separate FlexUnitApplication.mxml application. This is extremely convenient for debugging: You can debug particular tests if you run into a problem, allowing you to target specific functionality instead of debugging the entire application at once.

Automating the Tests

You can use any of several methods to automate the tests to run so that you don’t have to run them manually. To automate the compiling of the Flash Builder 4 UI and running the unit tests for both FlexUnit and PHPUnit, I use an Apache Ant script for each project. Ant is a mature, commonly used replacement for make implemented in Java. Ant is a good choice for automating these tests, because Ant has tasks already written for compiling Flash Builder 4 code. Ant also has integration with many source code management systems and third-party build tools.

Running the Tests

To run the PHPUnit tests in Ant, use the exec Ant task. Simply create a new file at the base of your project called build.xml. Shown here is an example of the Ant build.xml script running the phpunit command to execute the PHPUnit tests:

<?xml version="1.0" encoding="utf-8"?>
<project name="MyPHPBuild" default="run-phpunit-tests" basedir=".">
    <!-- check code out for source control, etc. -->
    <target name="run-phpunit-tests">
        <exec dir="${basedir}" executable="phpunit" failonerror="true">
            <arg line="--log-xml ${basedir}/build/logs/results.xml TimeCalcTest"/>
        </exec>
    </target>
    <!-- then package up code -->
</project>

Create a similar build.xml file in the Flash Builder 4 project. Before you can run the FlexUnit tests, you should use Ant to compile the Flash Builder 4 code. Together, the mxmlc and flexunit Ant tasks will look like this:

<?xml version="1.0" encoding="utf-8"?>
<project name="MyFlexUIBuild" basedir=".">
    <!-- Put the SDK home that came with your Flash Builder 4 installation here. -->
    <property name="FLEX_HOME" value="/Applications/Adobe Flash Builder 4 Plug-in/sdks/4.0.0" />
    <taskdef resource="flexTasks.tasks" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar" />
    <taskdef resource="flexUnitTasks.tasks" classpath="${basedir}/flexUnitTasks-4.0.0.jar" />
    <target name="compile-flex">
        <mxmlc file="${basedir}/src/Main.mxml" 
            output="${basedir}/bin-debug/Main.swf"
            actionscript-file-encoding="UTF-8"
            keep-generated-actionscript="true"
            incremental="true">

            <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml"/>
            <source-path path-element="${FLEX_HOME}/frameworks"/>
            <compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
                <include name="libs" />
                <include name="../bundles/{locale}" />
            </compiler.library-path>
            <default-size width="500" height="600" />
        </mxmlc>
    </target>
    <target name="run-flexunit-tests" depends="compile-flex">
            <flexunit swf="${basedir}/src/FlexUnitApplication.mxml"
                toDir="."
                haltonfailure="false"
                verbose="true"
                localTrusted="true" />
    </target>
</project>

Viewing Test Results

When you run the Ant scripts to run the tests for the project, the unit test reports will be printed to output files. You can view these output files in the directories, have send them via FTP to a location using Ant targets, or you can use third-party build tools to import the reports.

Digging Deeper

Running the unit tests in an automated fashion is just the beginning of applying build best practices to your Flash Builder 4 and PHP projects on the road to improving code quality. Other tools, such as a continuous integration build tool, can help you completely automate the process. In addition to automating the test execution, consider automating other reporting, such as unit test coverage.

Continuous Integration

Continuous integration building is the method of building your code and running tests on a continual basis. For most projects, this means using a tool that monitors your source code repository for changes. When the tool detects changes to your repository, it gets the latest version of the code, compiles it, and runs unit tests. These tools can be configured to send team members e-mail messages or other notifications if the build fails.

The idea behind using continuous integration building is that the team members will receive almost immediate feedback if they have committed code to the repository that does not compile or pass unit test.

Continuous integration builds are a great way to help measure the level of quality of your code base. By placing priority on making sure the builds are successful, you can feel confident that the code in the repository is in the best state possible.

Even code written in languages where there are no compile steps per se can benefit from a continuous integration build. PHP projects will benefit from continuous integration builds if the unit tests are executed and if the code is exported from the source code repository into a structure or package that is suitable for placement on a server.

Some examples of continuous integration tools are CruiseControl, Hudson, TeamCity, and BuildMaster.

Running the Tests Together

When building an application that has two obviously different projects, such as the Flash Builder 4 project and the PHP project, it’s useful to create a third project—such as a project called MyBuild—that contains a script capable of building or preparing all of the projects and running all of the unit tests.

To make a build project, create a new generic project in Flash Builder 4 (click File > New > Project, and then click General > Project). Inside this new project, create a single build.xml file that will serve as an Ant script that will call the other projects’ build scripts. The new Ant script will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<project name="MyBuild" default="run-all-tests" basedir=".">

    <property name="workspace.dir" value="${basedir}/.." />
    <property name="php.project.dir" value="${workspace.dir}/MyPHPBuild" />
    <property name="flex.project.dir" value="${workspace.dir}/MyFlexUIBuild" />

    <target name="build">
        <ant antfile="${flex.project.dir}/build.xml" target="compile-flex" inheritall="false" />
    </target>

    <target name="run-all-tests" depends="build">
        <ant antfile="${php.project.dir}/build.xml" target="run-phpunit-tests" inheritall="false" />
        <ant antfile="${flex.project.dir}/build.xml" target="run-flexunit-tests" inheritall="false" />
    </target>

    <!-- optionally collect the unit test logs and pull them into the base directory -->

    <!-- optionally tag the repository with a build number -->

</project>

The inheritall attribute set to false will ensure that the variables, such as basedir, from this script do not inherit to the other Ant scripts. If you would rather set variables in the central build script, set inheritall to true and use unique variables in your PHP and Flash Builder 4 project scripts. You must avoid using relative paths in the scripts, because the paths are relative by default from the build project’s base directory.

Code Coverage

Unit test coverage is a way of measuring how much of your code is actually exercised when the unit tests are executed. The better the coverage, the more confident you can be that the code in your application is tested to meet expectations.

Tools are available that allow you to verify that your code is thoroughly covered by your unit tests. One such tool for FlexUnit is flexcover.

PHPUnit does test coverage analysis for you. To see the code coverage analysis for your PHP unit tests, run:

phpunit --coverage-html ./report MyTest

where MyTest is the name of your unit test.

Summary

Unit tests, when written properly, help you ensure that your code functions according to requirements. You can verify the test coverage with tools such as flexcover or PHPUnit. When combined with automated testing, such as that executed in continuous integration builds, you can have a much higher degree of confidence that your project’s code base compiles and meets requirements.

Unit tests also offer other advantages, such as easier debugging, and they provide extra documentation. As the project evolves, unit tests offer regression testing to help ensure that new changes don’t break existing functionality.

Author Bio:

Nathan A. Good lives in the Twin Cities area of Minnesota. Professionally, he does software development, software architecture, and systems administration. When he’s not writing software, he enjoys building PCs and servers, reading about and working with new technologies, and trying to get his friends to make the move to open source software. He’s written and co-written many books and articles, including Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach andFoundations of PEAR: Rapid PHP Development.

About Cal Evans

Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term "computer" loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC's. For the past 10 years Cal has worked with PHP and MySQL on Linux OSX, and when necessary, Windows. He has built on a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys building and managing development teams using his widely imitated but never patented management style of "management by wandering around". Cal is currently based in Nashville, TN and is gainfully unemployed as the Chief Marketing Officer of Blue Parabola, LLC. Cal is happily married to wife 1.28, the lovely and talented Kathy. Together they have 2 kids who were both bright enough not to pursue a career in IT. Cal blogs at http://blog.calevans.com and is the founder and host of Day Camp 4 Developers

View all posts by Cal Evans

Comments are closed.