Posts tagged ‘Testing’

Process Isolation in PHPUnit

I was recently writing a unit test for an autoloader when I came across a somewhat unintuitive behavior in PHPUnit.

One requirement of the test suite was that some test methods had to be run in a separate process since class declarations reside in the global scope and persist until the process terminates. So, I slapped a @runInSeparateProcess annotation in the docblock of a test method with that requirement, ran the test suite… and watched that test method fail because the class was still being declared.

It took some head-scratching and tracing through the source of PHPUnit itself to figure out what was going on. When you run the phpunit executable, it’s actually instantiating PHPUnit_TextUI_TestRunner. The eventual result of this is that the run() method inherited by your subclass of PHPUnit_Framework_TestCase is called.

Depending on the value of the also-inherited $preserveGlobalState instance property, which can be set via the setPreserveGlobalState() method, multiple measures are undertaken to preserve the state of the current process. One such measure is including files for all the classes currently defined in that process, which is what was tripping me up because $preserveGlobalState has a default value of true.

$preserveGlobalState must contain its intended value before the run() method is called. The easiest way that I’ve found to facilitate this is to override the run() method in your subclass, call setPreserveGlobalState() there, then call the parent class implementation of run(). I’ve included a code sample below to illustrate this.

class MyTestCase extends PHPUnit_Framework_TestCase
{
    public function run(PHPUnit_Framework_TestResult $result = NULL)
    {
        $this->setPreserveGlobalState(false);
        return parent::run($result);
    }
}

So, if you try to use the @runInSeparateProcess or @runTestsInSeparateProcesses annotations that PHPUnit offers, be aware that the global state will be preserved by default. You will need to explicitly set it to not be so if running tests in separate processes is to have the effect that you are probably intending.

Database Testing with PHPUnit and MySQL

Update 2012/01/15: I finally got around to submitting a patch to document this feature in the PHPUnit manual. Sebastian has merged it, so it will hopefully be available in the online manual soon.

Update #2 2012/01/23: I got around to checking the online version of the manual and the current build includes my patch. Enjoy.

I recently made a contribution to the PHPUnit project that I thought I’d take a blog post to discuss. One of the extensions bundled with PHPUnit adds support for database testing. This extension was contributed by Mike Lively and is a port of the DbUnit extension for the JUnit Java unit testing framework. If you’re interested in learning more about database unit testing, check out this presentation by Sebastian Bergmann on the subject.

One of the major components of both extensions is the data set. Database unit tests involve loading a seed data set into a database, executing code that performs an operation on that data set such as deleting a record, and then checking the state of the data set to confirm that the operation had the desired effect. DbUnit supports multiple formats for seed data sets. The PHPUnit Database extension includes support for DbUnit’s XML and flat XML formats plus CSV format as well.

If you’re using MySQL as your database, CSV has been the only format supported by both the mysqldump utility and the PHPUnit Database extension up to this point. My contribution adds support for its XML format to the extension. While this support was developed to work in the PHPUnit 3.4.x branch, it won’t be available in a stable release until 3.5.0. In the meantime, this is how you can use it now.

  1. Go to the commit on Github and apply the additions and modifications included in it to your PHPUnit installation.
  2. From a shell, get your XML seed data set and store it in a location accessible to your unit test cases.
    mysqldump --xml -t -u username -p database > seed.xml
  3. Create a test case class that extends PHPUnit_Extensions_Database_TestCase. Implement getConnection() and getDataSet() as per the documentation where the latter will include a method call to create the data set from the XML file as shown below.
    $dataSet = $this->createMySQLXMLDataSet('/path/to/seed.xml');
  4. At this point, you can execute operations on the database to get it to its expected state following a test, produce an XML dump of the database in that state, and then compare that dump to the actual database contents in a test method to confirm that the two are equal.
    $expected = $this->createMySQLXMLDataSet('/path/to/expected.xml');
    $actual = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
    // Specify a SELECT query as the 2nd parameter here to limit the data set, else the entire table is used
    $actual->addTable('tablename');
    $this->assertDataSetsEqual($expected, $actual);

That’s it! Hopefully this proves useful to someone else.

Unit Tests and Code Coverage with PHPT

My initial experiences with unit testing and PHP were with PHPUnit. While it’s a great tool and I have to give kudos to Sebastian for contributing so much to its development, I’ve come to appreciate the simplicity of PHPT tests. Recently, I wrote some for a project and realized that I wasn’t aware of how to generate code coverage reports. Many thanks to the very helpful patrons in the #pear channel on EFNet for helping me to get this working.

First, the stock Kubuntu installation of PEAR is version 1.6.1. Their first recommendation was to upgrade to 1.7.2, which was easy enough:

sudo apt-get install php-pearpear update-channelspear upgrade-all

Next, PEAR bug 13958 can prevent code coverage reports from being generated properly. This bug has an available patch, which you can apply by doing the following:

cd /usr/local/share/PEARwget -c http://pear.php.net/bugs/...patch PEAR/RunTest.php phpt_coverage_bug.patch.txt

Now to install PHPUnit. I ran into an odd issue here where following the installation documentation resulted in PHPUnit 1.3.3 stable being installed, which is obviously a fairly old version. Even pear clear-cache didn’t seem to solve the issue. I checked to confirm that PEAR was using PHP 5. The only way I was able to get around this was to explicitly specify the latest version.

sudo pear install phpunit/phpunit-3.2.19

The current version of PHPUnit, 3.2.19, also has bugs 482, 483, and 484 related to generating PHPT code coverage reports. I had to apply the patch for bug 482 to fix issues I was having, but the others may also be necessary depending on the code being tested. These issues should be fixed when PHPUnit 3.2.20 is released. In the meantime, if you want to avoid manually applying patches and you’re feeling adventurous, you can use an git checkout.

As far as I can tell, PHPUnit itself does not contain a runner for its PHPT suite extension. However, there is a runner script to do this in PEAR CVS. Download the script to the directory containing your PHPT test files and execute it with a command similar to this.

phpunit --coverage-html ../Tests-Results \    Console_CommandLine_AllTests AllTests.php

In the case of this particular command, the Tests-Results directory parallel to the directory containing your PHPT files should now contain an index.html file with your test results. There you have it. Happy testing folks!