+ SimpleTest pretty much follows the MVC pattern + (Model-View-Controller). + The reporter classes are the view and the model is your + test cases and their hiearchy. + The controller is mostly hidden from the user of + SimpleTest unless you want to change how the test cases + are actually run, in which case it is possible to + override the runner objects from within the test case. + As usual with MVC, the controller is mostly undefined + and there are other places to control the test run. +
+ +
+
+Reporting results in HTML
+
+
+ The default test display is minimal in the extreme. + It reports success and failure with the conventional red and + green bars and shows a breadcrumb trail of test groups + for every failed assertion. + Here's a fail... +
File test
+ Fail: createnewfile->True assertion failed.+
File test
++ For web page based displays there is the + HtmlReporter class with the following + signature... +
+class HtmlReporter extends SimpleReporter { + public HtmlReporter($encoding) { ... } + public makeDry(boolean $is_dry) { ... } + public void paintHeader(string $test_name) { ... } + public void sendNoCacheHeaders() { ... } + public void paintFooter(string $test_name) { ... } + public void paintGroupStart(string $test_name, integer $size) { ... } + public void paintGroupEnd(string $test_name) { ... } + public void paintCaseStart(string $test_name) { ... } + public void paintCaseEnd(string $test_name) { ... } + public void paintMethodStart(string $test_name) { ... } + public void paintMethodEnd(string $test_name) { ... } + public void paintFail(string $message) { ... } + public void paintPass(string $message) { ... } + public void paintError(string $message) { ... } + public void paintException(string $message) { ... } + public void paintMessage(string $message) { ... } + public void paintFormattedMessage(string $message) { ... } + protected string _getCss() { ... } + public array getTestList() { ... } + public integer getPassCount() { ... } + public integer getFailCount() { ... } + public integer getExceptionCount() { ... } + public integer getTestCaseCount() { ... } + public integer getTestCaseProgress() { ... } +} ++ Here is what some of these methods mean. First the display methods + that you will probably want to override... +
-
+
-
+ HtmlReporter(string $encoding)
+
+ is the constructor. + Note that the unit test sets up the link to the display + rather than the other way around. + The display is a mostly passive receiver of test events. + This allows easy adaption of the display for other test + systems beside unit tests, such as monitoring servers. + The encoding is the character encoding you wish to + display the test output in. + In order to correctly render debug output when + using the web tester, this should match the encoding + of the site you are trying to test. + The available character set strings are described in + the PHP html_entities() + function. +
+ -
+ void paintHeader(string $test_name)
+
+ is called once at the very start of the test when the first + start event arrives. + The first start event is usually delivered by the top level group + test and so this is where $test_name + comes from. + It paints the page titles, CSS, body tag, etc. + It returns nothing (void). +
+ -
+ void paintFooter(string $test_name)
+
+ Called at the very end of the test to close any tags opened + by the page header. + By default it also displays the red/green bar and the final + count of results. + Actually the end of the test happens when a test end event + comes in with the same name as the one that started it all + at the same level. + The tests nest you see. + Closing the last test finishes the display. +
+ -
+ void paintMethodStart(string $test_name)
+
+ is called at the start of each test method. + The name normally comes from method name. + The other test start events behave the same way except + that the group test one tells the reporter how large + it is in number of held test cases. + This is so that the reporter can display a progress bar + as the runner churns through the test cases. +
+ -
+ void paintMethodEnd(string $test_name)
+
+ backs out of the test started with the same name. +
+ -
+ void paintFail(string $message)
+
+ paints a failure. + By default it just displays the word fail, a breadcrumbs trail + showing the current test nesting and the message issued by + the assertion. +
+ -
+ void paintPass(string $message)
+
+ by default does nothing. +
+ -
+ string _getCss()
+
+ Returns the CSS styles as a string for the page header + method. + Additional styles have to be appended here if you are + not overriding the page header. + You will want to use this method in an overriden page header + if you want to include the original CSS. +
+
-
+
-
+ array getTestList()
+
+ is the first convenience method for subclasses. + Lists the current nesting of the tests as a list + of test names. + The first, most deeply nested test, is first in the + list and the current test method will be last. +
+ -
+ integer getPassCount()
+
+ returns the number of passes chalked up so far. + Needed for the display at the end. +
+ -
+ integer getFailCount()
+
+ is likewise the number of fails so far. +
+ -
+ integer getExceptionCount()
+
+ is likewise the number of errors so far. +
+ -
+ integer getTestCaseCount()
+
+ is the total number of test cases in the test run. + This includes the grouping tests themselves. +
+ -
+ integer getTestCaseProgress()
+
+ is the number of test cases completed so far. +
+
+class ShowPasses extends HtmlReporter { + + function paintPass($message) { + parent::paintPass($message); + print "&<span class=\"pass\">Pass</span>: "; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print implode("->", $breadcrumb); + print "->$message<br />\n"; + } + + function _getCss() { + return parent::_getCss() . ' .pass { color: green; }'; + } +} ++ +
+ One method that was glossed over was the makeDry() + method. + If you run this method, with no parameters, on the reporter + before the test suite is run no actual test methods + will be called. + You will still get the events of entering and leaving the + test methods and test cases, but no passes or failures etc, + because the test code will not actually be executed. +
++ The reason for this is to allow for more sophistcated + GUI displays that allow the selection of individual test + cases. + In order to build a list of possible tests they need a + report on the test structure for drawing, say a tree view + of the test suite. + With a reporter set to dry run that just sends drawing events + this is easily accomplished. +
+ + ++ Rather than simply modifying the existing display, you might want to + produce a whole new HTML look, or even generate text or XML. + Rather than override every method in + HtmlReporter we can take one + step up the class hiearchy to SimpleReporter + in the simple_test.php source file. +
++ A do nothing display, a blank canvas for your own creation, would + be... +
+require_once('simpletest/simple_test.php'); + +class MyDisplay extends SimpleReporter { + + function paintHeader($test_name) { + } + + function paintFooter($test_name) { + } + + function paintStart($test_name, $size) { + parent::paintStart($test_name, $size); + } + + function paintEnd($test_name, $size) { + parent::paintEnd($test_name, $size); + } + + function paintPass($message) { + parent::paintPass($message); + } + + function paintFail($message) { + parent::paintFail($message); + } +} ++ No output would come from this class until you add it. + + +
+
+The command line reporter
+
+
+ SimpleTest also ships with a minimal command line reporter. + The interface mimics JUnit to some extent, but paints the + failure messages as they arrive. + To use the command line reporter simply substitute it + for the HTML version... +
+<?php + require_once('simpletest/unit_tester.php'); + require_once('simpletest/reporter.php'); + + $test = &new GroupTest('File test'); + $test->addTestFile('tests/file_test.php'); + $test->run(new TextReporter()); +?> ++ Then invoke the test suite from the command line... +
+php file_test.php ++ You will need the command line version of PHP installed + of course. + A passing test suite looks like this... +
+File test +OK +Test cases run: 1/1, Failures: 0, Exceptions: 0 ++ A failure triggers a display like this... +
+File test +1) True assertion failed. + in createnewfile +FAILURES!!! +Test cases run: 1/1, Failures: 1, Exceptions: 0 ++ +
+ One of the main reasons for using a command line driven + test suite is of using the tester as part of some automated + process. + To function properly in shell scripts the test script should + return a non-zero exit code on failure. + If a test suite fails the value false + is returned from the SimpleTest::run() + method. + We can use that result to exit the script with the desired return + code... +
+<?php + require_once('simpletest/unit_tester.php'); + require_once('simpletest/reporter.php'); + + $test = &new GroupTest('File test'); + $test->addTestFile('tests/file_test.php'); + exit ($test->run(new TextReporter()) ? 0 : 1); +?> ++ Of course we don't really want to create two test scripts, + a command line one and a web browser one, for each test suite. + The command line reporter includes a method to sniff out the + run time environment... +
+<?php + require_once('simpletest/unit_tester.php'); + require_once('simpletest/reporter.php'); + + $test = &new GroupTest('File test'); + $test->addTestFile('tests/file_test.php'); + if (TextReporter::inCli()) { + exit ($test->run(new TextReporter()) ? 0 : 1); + } + $test->run(new HtmlReporter()); +?> ++ This is the form used within SimpleTest itself. + + + +
+ SimpleTest ships with an XmlReporter class + used for internal communication. + When run the output looks like... +
+<?xml version="1.0"?> +<run> + <group size="4"> + <name>Remote tests</name> + <group size="4"> + <name>Visual test with 48 passes, 48 fails and 4 exceptions</name> + <case> + <name>testofunittestcaseoutput</name> + <test> + <name>testofresults</name> + <pass>This assertion passed</pass> + <fail>This assertion failed</fail> + </test> + <test> + ... + </test> + </case> + </group> + </group> +</run> ++ You can make use of this format with the parser + supplied as part of SimpleTest itself. + This is called SimpleTestXmlParser and + resides in xml.php within the SimpleTest package... +
+<?php + require_once('simpletest/xml.php'); + + ... + $parser = &new SimpleTestXmlParser(new HtmlReporter()); + $parser->parse($test_output); +?> ++ The $test_output should be the XML format + from the XML reporter, and could come from say a command + line run of a test case. + The parser sends events to the reporter just like any + other test run. + There are some odd occasions where this is actually useful. + +
+ A problem with large test suites is thet they can exhaust + the default 8Mb memory limit on a PHP process. + By having the test groups output in XML and run in + separate processes, the output can be reparsed to + aggregate the results into a much smaller footprint top level + test. +
++ Because the XML output can come from anywhere, this opens + up the possibility of aggregating test runs from remote + servers. + A test case already exists to do this within the SimpleTest + framework, but it is currently experimental... +
+<?php + require_once('../remote.php'); + require_once('../reporter.php'); + + $test_url = ...; + $dry_url = ...; + + $test = &new GroupTest('Remote tests'); + $test->addTestCase(new RemoteTestCase($test_url, $dry_url)); + $test->run(new HtmlReporter()); +?> ++ The RemoteTestCase takes the actual location + of the test runner, basically a web page in XML format. + It also takes the URL of a reporter set to do a dry run. + This is so that progress can be reported upward correctly. + The RemoteTestCase can be added to test suites + just like any other group test. + + +