Simple Unittesting in PHP
testing (2), php (2)If Perl is the Swiss Army chainsaw of the internet, PHP might be the dull butterknife from that silverware set your Aunt Girda gave you because she felt guilty about throwing it away. Sure, you're never going to cut down that redwood, but when you're cutting butter, butterknives have good points too. Today's butter is unittesting for small PHP projects.
Setting up a light unittesting setup is pretty trivial as long as your language supports using functions as parameters. Which, fortunately, PHP does. For example, a none-too-impressive implementation ofmap
可以这样写:
functionmy_echo($x){echo"my_echo:$x";}functionmap($func,$lst){$mapped=array();foreach($lstas$item){$mapped[]=$func($item);}return$mapped;}
map("my_echo",array('a','b','c'));?>
With that in mind, let's consider what basic unittesting consists of:
- Comparing actual and expected values.
- Defining testcases.
- Running testcases.
- Reporting results.
With an emphasis onbasic, we can get all of that for remarkably cheap:
$verbose=$_GET['verbose'];$pass=0;$fail=0;functionassert_($a,$b){if($a!=$b){echo""error">error:$a!=$b";global$fail;$fail+=1;}else{global$pass;$pass+=1;echo'.';}}
functiontest_x(){assert_(1,2);assert_("yes","yes");}
functiontest_y(){assert_(count(array(1,2,3)),strlen("abc"));}
$tests=array('text_x','text_y');
foreach($testsas$test){set_up();if($verbose){echo"$test
";}$test();tear_down();}
echo"Passed
$passtests. Failed$failtests.
";?>
If you're bothered by the reliance on global state, or that it would be hard to extend, we improve upon it a bit by using PHP5 style OO (at a loss of simplicity).
classSimpleTest{public$passed;public$failed;public当前美元;public$msgs;publicfunction__construct(){$this->passed=0;$this->failed=0;$this->msgs=array();}
publicfunctionassert($a,$b){if($a==$b){$this->passed++;echo'.';}else{$this->failed++;echo'X';$this->msgs[]="$this->current:$a!=$b";}}
publicfunctionrun_tests($tests){foreach($testsas$test){$this->current=$test;$test($this);}foreach($msgsas$msg){echo"$msg
";}echo"$this->passedpassed tests, and$this->failedfailed tests.";}}functiontest_math($st){$st->assert(1*2,2);$st->assert(5+0,5);}
functiontest_strings($st){$st->assert("test","test");$st->assert(strlen("test"),4);}
$st=newSimpleTest();$st->run_tests(array("test_math","test_strings");?>
If you're in the mood for some overengineering, it's a pretty short leap from here to a flexible and extensible unittesting framework. For a start, we can rewrite therun_tests
method to look through all user defined functions for those that begin withtest_
and then run those tests.
publicfunctionrun_tests(){$all_funcs=get_defined_functions();$user_funcs=$all_funcs['user'];foreach($user_funcsas$func){if(strpos($func,"test_")===0){$this->current=$func;$func($this);}}}?>
In either case, running the tests is as simple as reloading the page (or could be done at the command line), and doing this exercise has firmly dragged PHP from my mental junk bin (into some kind of halfway home for programming languages). The equivalent code written in Python strikes me as much more pleasant, but the difference isn't as overwhelming as I might have once imagined.