PHP Test Chassis
PHP Test is a very lightweight unit testing framework which allows you to unit or regression test applications using a variety of text, file, PHP, XML or XSL inputs and expected outputs, with configurable fail behaviour and overrideable success/fail comparison functions.
Downloads for the software described here are available on the downloads page.
I must rant a little to explain why I am releasing this simple but useful bit of code. I’m rather tired of downloading PHP applications that aren’t regression tested against different versions of PHP, against their own dependencies, or against simple bugs that could easily be avoided if the authors just ran a test suite before releasing. PHP itself could benefit from this too; PHP already has extensive regression testing prior to each release, yet it is amazing what bugs slip through the net. In early months of 2006 (PHP 5.0.x and 5.1.x series), we have had releases of PHP with implementations of basic date handling functions like strftime() not working correctly, and $_POST superglobals that aren’t populated properly under all conditions. These kinds of bugs break thousands if not millions of web sites and we would expect them to be tested before release on any product as widely deployed as PHP – particularly a development tool which is a dependency of millions of users.
We all know that writing test code is boring, doesn’t give a feeling of accomplishment and often can be longer than the code itself being tested. I created this simple tool to try and allieviate the problem a little bit for myself and other PHP developers.
TestBase is an abstract base class providing a test harness which you can use in your test programs in one of two ways:
- Derive from it to create a test, implementing the one abstract function
Test()with your test code, echo’ing the result to the standard output, provide the expected result in the constructor call and invoke Run() to execute the test.
- Derive from it to create a new testing mechanism, providing new parameters in your derived constructor, calling the parent constructor at the end of initialisation, and implementing
Test()as a generic test executor. Then create instances of this class using parameters (eg. in the constructor) to create each test, and invoke Run() on each instance to execute the test.
The library supplies three derived testing mechanisms by default, which you can invoke directly and use in your own test programs:
TestCode– Takes a string containing valid PHP code to be evaluated as input, and uses the output of the evaluated code as the result
TestFile– Takes a file containing valid PHP code to be evaluated as input, and the expected result, separated by delimiters, and uses the output of the evaluated code as the result
TestXSL– Takes the filename of an XSL stylesheet, and optionally an XML input source, and uses the output of the XSL transformation as the result
All of the test mechanisms support the following:
- Expected result supplied as a string in the constructor
- Tests can be given a descriptive name for display purposes
- Leading and trailing whitespace differences from the expected and actual results can be optionally ignored
- Test can be configured to run automatically on instantiation, or wait until you manually call Run()
- Test progress message output can be optionally suppressed
- Added 20-Jul-2006: The actual result can be tested for validity against expected result test code, instead of against a fixed expected result string value.
- Added 20-Jul-2006: The comparison function which tests the actual result against the expected result can be overridden in a derived test class (the default comparison is simple value equality)
TestXSL also supports the following features:
- For tests of templates which don’t reference an XML source, the XML source can be omitted (saving you from creating a blank XML file).
- With no XML source, the XML
"<TestMode/>"is supplied to the stylesheet. This easily allows you to setup an
/TestModeto trigger a separate entry point for testing from your main entry point.
- Parameters can be supplied to the XSL stylesheet.
- The XML declaration in the result can be optionally ignored.
To make managing groups of related tests easier, you can also create a
TestSet. TestSets support the following features:
- A descriptive name can be assigned for display purposes
- Tests can be supplied as an array in the constructor, and/or added one at a time to the TestSet instance with
- The TestSet can be configured to halt on the first test failure, or continue running all tests
- Output from individual tests (except for failure messages) can be optionally suppressed
The test code is also regression-tested against itself on startup.
The test library is intended to be included in your own test programs with:
and your test programs should be invoked from the command-line:
However you can also run the tests from a browser window and in this case output buffering is disabled so you can see a running progress of the tests without having to wait for all tests to complete.
Sometimes, calculating the expected result of a test is non-trivial or impossible, and the only way to test a result’s validity is by running calculations or other code against it. The
$expectedResultType parameter in TestBase’s constructor can take a value of
'code'. With text, the default behaviour is to test for textual equality between the expected and actual results. With code, the supplied expected result is a valid PHP code block to be evaluated with
eval(), which should test against
$this->Result and return
false to indicate whether or not the test result is valid.
Sometimes it is desirable to make comparisons other than textual equality in order to ascertain the validity of a test result. The default comparative behaviour can be overridden in classes deriving from
TestBase by rewriting the
TestResult() method. This method should perform a comparison between
$this->expectedResult and return
false to indicate whether or not the test result is valid.
The code is commented in the PHPDoc style so should be fairly self-explanatory to use. A real-world usage example can be found in my XSL date-time library article.
I hope you find the test chassis useful!
Please send feedback via the contact page or leave a comment below.