pTest: PHP Unit Tester in 9 Lines Of Code

By | | Programming

6

I was recently working on a command line PHP tool, and didn’t have easy access to our normal PHP unit testing framework built around SimpleTest. After a few lines of non-test-driven-development, I started to freak out a bit – I guess I’ve fallen for the view that if code doesn’t have tests, it’s broken.

I didn’t need support for mock objects or complicated assertions – just a bare basic assertTrue() would do the trick. So, I present “pTest”, in 9 lines of code:


/**
 * pTest - PHP Unit Tester
 * @param mixed $test Condition to test, evaluated as boolean
 * @param string $message Descriptive message to output upon test
 */
function assertTrue($test, $message)
{
	static $count;
	if (!isset($count)) $count = array('pass'=>0, 'fail'=>0, 'total'=>0);
	$mode = $test ? 'pass' : 'fail';
	printf("%s: %s (%d of %d tests run so far have %sed)\n",
		strtoupper($mode), $message, ++$count[$mode], ++$count['total'], $mode);
}

Here’s a few contrived test cases to demonstrate:


assertTrue(1 + 1 == 2, 'one plus one should equal two');
assertTrue(false, 'false should be true (this one will fail)');
assertTrue(!false, 'false should be false');

And the sample output:

PASS: one plus one should equal two (1 of 1 tests run so far have passed)
FAIL: false should be true (this one will fail) (1 of 2 tests run so far have failed)
PASS: false should be false (2 of 3 tests run so far have passed)

If you’re viewing the output in a web browser, you could easily wrap each output message in a <p> tag. Or if you’re using a non-broken browser you could serve the response with header('Content-Type: text/plain');.

Obviously this isn’t to be taken too seriously. Then again, I found it extremely useful to drop into the few command line scripts I was working on.

Another use I can see for such a simple testing function is introducing the uninitiated into the world of unit testing. I know that before I got into it, the biggest barrier was the complexity of the frameworks. Perhaps pTest will give beginners a starting point, from which they can move onto more feature-rich frameworks like PHPUnit or SimpleTest once they outgrow the humble assertTrue().

Learn Responsive Web Design

Join Learnable $29 Includes all SitePoint books

Paul Annesley

Paul is a Rails and PHP developer in the SitePoint group of companies.

More Posts

{ 6 comments }

Christopher Thompson August 14, 2007 at 10:15 am

I wrote a small unit tester as part of a forum discussion a while back that I called TinyTest. Although it is 50 lines, not 9, it provides assertTrue(), assertFalse(), pass() and fail() plus standard green/red output of the results. The original ideas was to have a unit tester small enough that you could supply it with posted code and tests.


class UnitTester {
        var $_passes = 0;
        var $_fails = 0;
        var $_test_stack = array();
        var $_trace_pos = 0;
        var $output = '';
        function assertTrue($result, $pos=0) {
                if ($result) {
                        $this->pass();
                } else {
                        $this->_trace_pos = $pos + 1;
                        $this->fail();
                }
        }
        function assertFalse($result, $pos=0) {
                if ($result) {
                        $this->_trace_pos = $pos + 1;
                        $this->fail();
                } else {
                        $this->pass();
                }
        }
        function pass() {
                ++$this->_passes;
        }
        function fail() {
                ++$this->_fails;
                $test_data = debug_backtrace();
                $this->_test_stack[] = $test_data[$this->_trace_pos];
                $this->_trace_pos = 0;
        }
        var $passed_heading_style = 'color: white; background-color: green; margin-top: 2; padding: 4 4 4 4;';
        var $failed_heading_style = 'color: white; background-color: red; margin-top: 2; padding: 4 4 4 4;';
        var $failed_test_style = 'color: white; background-color: red; margin-top: 2; padding-left: 4;';
        function __destruct() {
                if ($this->_fails > 0) {
                        $output = '<div style="' . $this->failed_heading_style . '">Failed ' . $this->_fails . ' of ' . ($this->_fails + $this->_passes) . ' tests. </div>';
                        $n = 1;
                        foreach ($this->_test_stack as $this_data) {
                                $message = $n++ . ". Failed: {$this_data['function']} on line {$this_data['line']} of file {$this_data['file']}. ";
                                $output .= '<div style="' . $this->failed_test_style . '">' . $message . '</div>';
                        }
                } else {
                        $output = '<div style="' . $this->passed_heading_style . '">Passed ' .$this->_passes . ' tests.</div>';
                }
            echo $output;
        }
}

shiflett August 14, 2007 at 4:11 am

You might want to check out TAP, the Test Anything Protocol:

http://testanything.org/

If you’re going to write a simple library like this, making it TAP-compliant means your output is going to be consumable by a number of mature testing tools.

There’s a PHP library that comes bundled with Apache-Test that outputs TAP and is almost as simple as yours:

http://shiflett.org/code/test-more.php

deathshadow August 13, 2007 at 5:23 pm

Good lord, people write whole units for this stuff? You know, after three decades of doing this ****, I am still amazed people ever need to waste time on this level of internal reporting.

Unit tester – took me a while just to figure out you meant result tracking… is that the new name for a four decade old method of code auditing?

z0s0 August 13, 2007 at 4:04 pm

Yeah… reckon it must be one of PHP’s least used builtins.

Paul Annesley August 13, 2007 at 2:02 pm

Good point.. although configuring it to result in output similar to assertTrue() (without making any assumptions about PHP’s global configuration) probably uses more lines of code ;)

With a bit of work it can certainly be set up nicely – with added benefits like line number reporting, and output of the evaled assertion code.

But I think the simplicity and self contained nature of pTest probably makes it less daunting to beginners to PHP and unit testing.

z0s0 August 13, 2007 at 1:47 pm

Interestingly, PHP actually has an assertion tester built-in:

http://php.net/assert

Comments on this entry are closed.