Blog Post RSS ?

Blogs » Web Tech » pTest: PHP Unit Tester in 9 Lines Of Code
 

pTest: PHP Unit Tester in 9 Lines Of Code


  • Save to
    Del.icio.us

by Paul Annesley

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().

This post has 6 responses so far

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

    http://php.net/assert

     
  2. 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.

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

     
  4. 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?

     
  5. 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

     
  6. 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;
            }
    
    }
    
     

Sponsored Links

Leave a response

You are not logged in, log in with your SitePoint Forum username and password.

-OR- Post Anonymously

* Make sure any code samples are escaped (i.e. ‘<b>’ becomes ‘&lt;b&gt;’).

If not logged in, your comments will be placed in a moderation queue. This means your comment may not appear until one of our moderators approves it.

SitePoint Marketplace

Buy and sell Websites, templates, domain names, hosting, graphics and more.

Logo Design, Web page Design and more!

99designs

  • Custom logo designs created ‘just for you’.
  • Pick the design you like best.
  • Only pay if you’re satisfied with the result.

It's Back!
FREE PDF with any printed book!