Chapter 18. PHPUnit and Phing

Phing (PHing Is Not GNU make) is a project build system based on Apache Ant. In the context of PHP, where you do not need to build and compile your sources, the intention of Phing is to ease the packaging, deployment, and testing of applications. For these tasks, Phing provides numerous out-of-the-box operation modules ("tasks") and an easy-to-use, object-oriented model for adding your own custom tasks.

Phing can be installed using the PEAR Installer. As with PHPUnit before, the PEAR channel (pear.phing.info) that is used to distribute Phing needs to be registered with the local PEAR environment before you can install a package from it:

pear channel-discover pear.phing.info

Now the PEAR Installer can be used to install Phing:

pear install phing/phing

Phing uses simple XML build files that specify a target tree where various tasks get executed. One of the out-of-the-box tasks that comes with Phing is the <phpunit> task that runs test cases using the PHPUnit framework. It is a functional port of Apache Ant's JUnit task.

Example 18.1 shows a Phing build.xml file that specifies a <project> named "BankAccount". The project's default <target> is called test. Using the <phpunit> task, this target runs all test cases that can be found in source files that match the *Test.php condition. This is done by using a <batchtest> element that collects the included files from any number of nested <fileset> elements. In this example, the tests declared in the class BankAccountTest in the source file BankAccountTest.php will be run.

Example 18.1: Phing build.xml file for the BankAccount tests

<?xml version="1.0"?>

<project name="BankAccount" basedir="." default="test">
  <target name="test">
    <phpunit haltonfailure="true" printsummary="true">
      <batchtest>
        <fileset dir=".">
          <include name="*Test.php"/>
        </fileset>
      </batchtest>
    </phpunit>
  </target>
</project>


Invoking Phing in the directory that contains build.xml (Example 18.1), BankAccount.php (Example 12.3), and BankAccountTest.php (Example 12.1) will run the tests by executing the project's default target, tests:

phing
Buildfile: /home/sb/build.xml

BankAccount > test:
 [phpunit] Tests run: 4, Failures: 0, Errors: 0, Time elapsed: 0.00067 sec

BUILD FINISHED

Total time: 0.0960 seconds

Table 18.1 shows the parameters that can be used to configure the <phpunit> task.

Table 18.1. Attributes for the <phpunit> element

NameTypeDescriptionDefault
codecoverageBooleanCollect Code Coverage information.false
haltonerrorBooleanStops the build process if an error occurs during the test run.false
haltonfailureBooleanStops the build process if a test fails. Errors are considered failures as well.false
printsummaryBooleanPrints one-line statistics for each test case.false


The following example shows the <phpunit> task's output when a test fails:

phing
Buildfile: /home/sb/build.xml

BankAccount > test:
 [phpunit] Tests run: 4, Failures: 1, Errors: 0, Time elapsed: 0.00067 sec
Execution of target "test" failed for the following reason:
/home/sb/build.xml:5:37: One or more tests failed

BUILD FAILED
/home/sb/build.xml:5:37: One or more tests failed
Total time: 0.0968 seconds

Formatting Feedback

Besides the required <batchtest> element, the <phpunit> element allows for another nested element: <formatter> is used to write test results in different formats. Output will always be sent to a file, unless you set the usefile attribute to false. The name of the file is predetermined by the formatter and can be changed by the outfile attribute. There are three predefined formatters:

brief

Prints detailed information in plain text only for test cases that failed.

plain

Prints one-line statistics in plain text for all test cases.

xml

Writes the test results in XML format.

Table 18.2 shows the parameters that can be used to configure the <formatter> task.

Table 18.2. Attributes for the <formatter> element

NameTypeDescriptionDefault
typeStringUse a predefined formatter (xml, plain, or brief). 
classnameStringName of a custom formatter class. 
usefileBooleanDetermines whether output should be sent to a file.true
todirStringDirectory the file is written to. 
outfileStringName of the file that is written to.Depends on the formatter used.


To generate a test report in HTML format, you can use the <phpunitreport> task, which applies an XSLT stylesheet to the XML logfile created by the <formatter> task. Phing ships with two XSLT stylesheets -- phpunit-frames.xsl and phpunit-noframes.xsl -- that generate HTML reports with or without frames, respectively.

Example 18.2 shows a build.xml file for Phing that runs the tests from the BankAccountTest class and generates a test report in HTML format using the phpunit-frames.xsl XSLT stylesheet. The HTML files generated for the report will be written to the report/ directory that is created by the prepare <target> and deleted by the clean <target>.

Example 18.2: Applying an XSLT stylesheet to get a test report

<?xml version="1.0"?>

<project name="BankAccount" basedir="." default="report">
  <target name="prepare">
    <mkdir dir="report"/>
  </target>

  <target name="clean"> 
    <delete dir="report"/>
  </target>

  <target name="report" depends="prepare">
    <phpunit>
      <batchtest>
        <fileset dir=".">
          <include name="*Test.php"/>
        </fileset>
      </batchtest>

      <formatter type="xml" todir="report" outfile="logfile.xml"/>
    </phpunit>

    <phpunitreport infile="report/logfile.xml"
                   styledir="/usr/lib/php/data/phing/etc"
                   format="frames"
                   todir="report"/>
  </target>
</project>


The following example shows the output of the phing command as it runs:

phing
Buildfile: /home/sb/build.xml

BankAccount > prepare:
    [mkdir] Created dir: /home/sb/report

BankAccount > report:

BUILD FINISHED

Total time: 0.1112 seconds

Table 18.3 shows the parameters that can be used to configure the <phpunitreport> task.

Table 18.3. Attributes for the <phpunitreport> element

NameTypeDescriptionDefault
infileStringThe filename of the XML results file to use.testsuites.xml
formatStringThe format of the generated report. Must be frames or noframes.noframes
styledirStringThe directory where the stylesheets are located. The stylesheets must conform to the following conventions: the stylesheet for the frames format must be named phpunit-frames.xsl, the stylesheet for the noframes format must be named phpunit-noframes.xsl. 
todirStringThe directory where the files resulting from the transformation should be written to. 


In addition to the test report that we just generated, Phing can generate a code-coverage report. For this, we need the <coverage-setup> and <coverage-report> tasks. The former prepares a database in which code-coverage information is stored while the tests are run; the latter formats such a database into a report in HTML format using XSLT stylesheets.

Example 18.3 shows a build.xml file for Phing that runs the tests from the BankAccountTest class and generates a code-coverage report in HTML format.

Example 18.3: Generating a code-coverage report

<?xml version="1.0"?>

<project name="BankAccount" basedir="." default="coverage-report">
  <target name="prepare">
    <mkdir dir="coverage-report"/>
  </target>

  <target name="clean"> 
    <delete dir="coverage-report"/>
  </target>

  <target name="coverage-report" depends="prepare">
    <coverage-setup database="./coverage-report/database">
      <fileset dir=".">
        <include name="*.php"/>
        <exclude name="*Test.php"/>
      </fileset>
    </coverage-setup>

    <phpunit codecoverage="true">
      <batchtest>
        <fileset dir=".">
          <include name="*Test.php"/>
        </fileset>
      </batchtest>
    </phpunit>

    <coverage-report outfile="coverage-report/coverage.xml">
      <report styledir="/usr/lib/php/data/phing/etc"
              todir="coverage-report"/>
    </coverage-report>
  </target>
</project>