In my Beyond Frameworks talk, I explained how a component-based architecture can help answer some of the important (i.e. expensive!) questions you might face when creating long-lived apps that rely on a PHP framework. In this series of blog posts, I’m going to look at how to go about creating and working with components.

Earlier today, I created some very basic unit tests for my repustateApi component. Unit tests are an essential part of developing your component, but how do you run them?

Running Your Component’s Code From The Command-Line

Many old-skool developers choose to work largely from the command-line. You can happily run PHPUnit by hand from the command line yourself each time, but if you’re taking advantage of the extra data that PHPUnit can report back on, that soon gets to be a lot of typing! This is where the build.xml file in our skeleton comes in handy …

To run your unit tests from the command line, all you need to do is run the command phing test:

stuart:~/Devel/sental/repustateApi$ phing test
Buildfile: /home/stuart/Devel/sental/repustateApi/build.xml
 [property] Loading /home/stuart/Devel/sental/repustateApi/

 > lint:

  [phplint] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate
Api/Client.php: No syntax errors detected

 > run-unittests:

   [delete] Deleting directory /home/stuart/Devel/sental/repustateApi/review/
    [mkdir] Created dir: /home/stuart/Devel/sental/repustateApi/review/code-c
    [exec] Executing command: phpunit --bootstrap=/home/stuart/Devel/sental/
repustateApi/src/tests/unit-tests/bootstrap.php --coverage-html /home/stuart/
Devel/sental/repustateApi/review/code-coverage --coverage-clover /home/stuart
/Devel/sental/repustateApi/review/logs/phpunit.xml /home/stuart/Devel/sental/
repustateApi/src/tests/unit-tests 2>&1
     [exec] PHPUnit 3.5.3 by Sebastian Bergmann.
     [exec] .EE
     [exec] Time: 0 seconds, Memory: 4.00Mb
     [exec] There were 2 errors:
     [exec] 1) Gradwell\RepustateApi\ClientTest::testCanRetrieveSentimentScor
     [exec] Exception: oh dear
     [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate
     [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate
     [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate
     [exec] /home/stuart/Devel/sental/repustateApi/src/tests/unit-tests/php/G
     [exec] 2) Gradwell\RepustateApi\ClientTest::testCanRetrieveSentimentScor
     [exec] Exception: oh dear
     [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate
     [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate
     [exec] /home/stuart/Devel/sental/repustateApi/src/php/Gradwell/Repustate
     [exec] /home/stuart/Devel/sental/repustateApi/src/tests/unit-tests/php/G
     [exec] FAILURES!
     [exec] Tests: 3, Assertions: 1, Errors: 2.
     [exec] Writing code coverage data to XML file, this may take a moment.
     [exec] Generating code coverage report, this may take a moment.
Execution of target "run-unittests" failed for the following reason: /home/st
uart/Devel/sental/repustateApi/build.xml:108:40: Task exited with code 2

/home/stuart/Devel/sental/repustateApi/build.xml:108:40: Task exited with cod
e 2
Total time: 0.5951 seconds

That went bang in a spectacular way! At this stage, where we have tests but don’t yet have a working Repustate API client, that’s exactly what should happen. As I work through the code, and satisfy the tests, we should quickly get to the point where we have more tests, and that they all pass.

Here’s an example of another component – our CommandLineLib – which has many tests that all succeed, just so that you can see what a successful test run should look like:

Buildfile: /home/stuart/Devel/GWC/CommandLineLib/build.xml
 [property] Loading /home/stuart/Devel/GWC/CommandLineLib/

 > lint:

  [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman
dLineLib/CommandLineParser.php: No syntax errors detected
  [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman
dLineLib/ParsedSwitches.php: No syntax errors detected
  [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman
dLineLib/DefinedArg.php: No syntax errors detected
  [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman
dLineLib/DefinedSwitch.php: No syntax errors detected
  [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman
dLineLib/DefinedSwitches.php: No syntax errors detected
  [phplint] /home/stuart/Devel/GWC/CommandLineLib/src/php/Phix_Project/Comman
dLineLib/ParsedSwitch.php: No syntax errors detected

 > run-unittests:

   [delete] Deleting directory /home/stuart/Devel/GWC/CommandLineLib/review/c
    [mkdir] Created dir: /home/stuart/Devel/GWC/CommandLineLib/review/code-co
     [exec] Executing command: phpunit --bootstrap=/home/stuart/Devel/GWC/Com
mandLineLib/src/tests/unit-tests/bootstrap.php --coverage-html /home/stuart/D
evel/GWC/CommandLineLib/review/code-coverage --coverage-clover /home/stuart/D
evel/GWC/CommandLineLib/review/logs/phpunit.xml /home/stuart/Devel/GWC/Comman
dLineLib/src/tests/unit-tests 2>&1
     [exec] PHPUnit 3.5.3 by Sebastian Bergmann.
     [exec] ............................................................ 60 /
     [exec] ..................
     [exec] Time: 1 second, Memory: 5.75Mb
     [exec] OK (78 tests, 407 assertions)
     [exec] Writing code coverage data to XML file, this may take a moment.
     [exec] Generating code coverage report, this may take a moment.
     [echo] The code coverage report is in /home/stuart/Devel/GWC/CommandLine

 > test:


Total time: 3.7912 seconds

Command-Line Test Run Explained

The component’s build.xml file contains targets – sequences of instructions that you can invoke by using phing. When you run the command phing test, here’s what happens:

  • phing loads the build.xml file in the current working directory. So make sure you’re in your component’s top-level folder when you run phing!
  • build.xml sucks in the file where we set the component’s name and version number. These properties are in a separate file so that it’s safe to replace build.xml with new versions as we improve the component’s skeleton files in the future (via the phix php-library:upgrade command).
  • phing runs the ‘test’ target in build.xml, which tells it to actually run the ‘lint’ and ‘run-unittests’ targets.
  • The ‘lint’ target does a syntax check on all of your PHP files. As your component grows, and the time it takes to run your unit tests grows too, a quick syntax check can save a developer wasting a surprising amount of time.
  • If the syntax check is successful, the ‘run-unittests’ target runs next.
  • The ‘run-unittests’ target invokes PHPUnit, telling it to use the skeleton’s bootstrap file, and to write out the results in XML format to feed into other tools later on. It also tells PHPUnit to write out code coverage data as HTML. We’ll look at that in a later blog post.

Running Your Unit Tests From Netbeans

If the command-line isn’t for you, don’t worry … the component skeleton is also designed to make it very easy to run your unit tests from inside Netbeans. I’m assuming that it will be just as easy to do this from other IDEs that support PHPUnit, but I haven’t tested any myself.

First of all, you need to setup your project in Netbeans to tell it where your tests are. Open the project’s properties dialog box:

and then set the Test Folder to be your ‘unit-tests’ folder:

Next, open the PHPUnit settings in the project’s properties dialog box, and tick the ‘Use Bootstrap’ box:

and then, tell Netbeans to use the bootstrap file inside the ‘unit-tests’ folder:

so that the PHPUnit settings look like this:

Now we’re all set to run the tests from inside Netbeans. In the project browser, simply right-click on the Client.php file, and select ‘Test’ from the popup menu. Netbeans will run the tests for you, and tell you exactly where they broke:

I hope you find these tools useful, and time-saving!

I’m going to beaver away over the weekend getting several components completed, and will be back on Monday to go through more of the things you need to know if you’re going to successfully go Beyond Frameworks and shift to a component-based architecture. Have a great weekend!


  1. A semana no mundo PHP (01/04/2011) | raphael.dealmeida says:
    April 1st, 2011 at 5:23 pm

    [...] How To Run Your Component’s Unit Tests [...]

  2. Patrícia says:
    February 9th, 2012 at 8:29 pm

    My tests didn’t work. How did the file “bootstrap.php” appeared on the “unit-tests” folder? I think this may be the problem, because I don’t have it…

    The log shows:

    Buildfile: /opt/lampp/htdocs/repustateApi/build.xml
    [property] Loading /opt/lampp/htdocs/repustateApi/
    [property] Loading /opt/lampp/htdocs/repustateApi/dist/lastBuilt

    > lint:

    [phplint] /opt/lampp/htdocs/repustateApi/src/php/Gradwell/RepustateApi/Client.php: No syntax errors detected

    > run-unittests:

    [delete] Deleting directory /opt/lampp/htdocs/repustateApi/review/code-coverage
    [mkdir] Created dir: /opt/lampp/htdocs/repustateApi/review/code-coverage
    [exec] PHPUnit 3.6.10 by Sebastian Bergmann.
    [exec] Usage: phpunit [switches] UnitTest [UnitTest.php]
    [exec] phpunit [switches]
    [exec] –log-junit Log test execution in JUnit XML format to file.
    [exec] –log-tap Log test execution in TAP format to file.
    [exec] –log-json Log test execution in JSON format.
    [exec] –coverage-clover Generate code coverage report in Clover XML format.
    [exec] –coverage-html Generate code coverage report in HTML format.
    [exec] –coverage-php Serialize PHP_CodeCoverage object to file.
    [exec] –coverage-text= Generate code coverage report in text format.
    [exec] Default to writing to the standard output.
    [exec] –testdox-html Write agile documentation in HTML format to file.
    [exec] –testdox-text Write agile documentation in Text format to file.
    [exec] –filter Filter which tests to run.
    [exec] –group … Only runs tests from the specified group(s).
    [exec] –exclude-group … Exclude tests from the specified group(s).
    [exec] –list-groups List available test groups.
    [exec] –loader TestSuiteLoader implementation to use.
    [exec] –printer TestSuiteListener implementation to use.
    [exec] –repeat Runs the test(s) repeatedly.
    [exec] –tap Report test execution progress in TAP format.
    [exec] –testdox Report test execution progress in TestDox format.
    [exec] –colors Use colors in output.
    [exec] –stderr Write to STDERR instead of STDOUT.
    [exec] –stop-on-error Stop execution upon first error.
    [exec] –stop-on-failure Stop execution upon first error or failure.
    [exec] –stop-on-skipped Stop execution upon first skipped test.
    [exec] –stop-on-incomplete Stop execution upon first incomplete test.
    [exec] –strict Run tests in strict mode.
    [exec] -v|–verbose Output more verbose information.
    [exec] –debug Display debbuging information during test execution.
    [exec] –process-isolation Run each test in a separate PHP process.
    [exec] –no-globals-backup Do not backup and restore $GLOBALS for each test.
    [exec] –static-backup Backup and restore static attributes for each test.
    [exec] –bootstrap A “bootstrap” PHP file that is run before the tests.
    [exec] -c|–configuration Read configuration from XML file.
    [exec] –no-configuration Ignore default configuration file (phpunit.xml).
    [exec] –include-path Prepend PHP’s include_path with given path(s).
    [exec] -d key[=value] Sets a php.ini value.
    [exec] -h|–help Prints this usage information.
    [exec] –version Prints the version and exits.
    [exec] –debug Output debugging information.
    Execution of target “run-unittests” failed for the following reason: /opt/lampp/htdocs/repustateApi/build.xml:210:40: Task exited with code 2

    /opt/lampp/htdocs/repustateApi/build.xml:210:40: Task exited with code 2
    Total time: 0.1993 seconds

  3. Stuart Herbert says:
    February 18th, 2012 at 3:35 pm

    @patricia: Phix puts the bootstrap.php file in the unit-tests folder, because it’s a bootstrap file only for the contents of the unit-tests folder. Looking at the output you’ve pasted, my guess is that you’re missing the phpunit.xml.dist file, which should exist in the top-level folder of your component. This should have been created by Phix; if you don’t have one there, that’s either a bug in Phix, or someone has removed it from your component.

  4. nihiliad says:
    February 21st, 2012 at 7:55 pm

    Shouldn’t “phix php-library:update” be “phix php-library:upgrade” instead? The php-library:update command doesn’t seem to exist in the latest version of phix, anyway.

  5. Stuart Herbert says:
    February 23rd, 2012 at 8:15 am

    @nihiliad: good spot, thank you! fixed.