+ The core system is a regression testing framework built around + test cases. + A sample test case looks like this... +
+class FileTestCase extends UnitTestCase {
+}
+
+ If no test name is supplied when chaining the constructor then
+ the class name will be taken instead.
+ This will be the name displayed in the test results.
+
+ + Actual tests are added as methods in the test case whose names + by default start with the string "test" and + when the test case is invoked all such methods are run in + the order that PHP introspection finds them. + As many test methods can be added as needed. + For example... +
+require_once('../classes/writer.php');
+
+class FileTestCase extends UnitTestCase {
+ function FileTestCase() {
+ $this->UnitTestCase('File test');
+ }
+
+ function setUp() {
+ @unlink('../temp/test.txt');
+ }
+
+ function tearDown() {
+ @unlink('../temp/test.txt');
+ }
+
+ function testCreation() {
+ $writer = &new FileWriter('../temp/test.txt');
+ $writer->write('Hello');
+ $this->assertTrue(file_exists('../temp/test.txt'), 'File created');
+ }
+}
+
+ The constructor is optional and usually omitted.
+ Without a name, the class name is taken as the name of the test case.
+
+ + Our only test method at the moment is testCreation() + where we check that a file has been created by our + Writer object. + We could have put the unlink() + code into this method as well, but by placing it in + setUp() and + tearDown() we can use it with + other test methods that we add. +
++ The setUp() method is run + just before each and every test method. + tearDown() is run just after + each and every test method. +
++ You can place some test case set up into the constructor to + be run once for all the methods in the test case, but + you risk test inteference that way. + This way is slightly slower, but it is safer. + Note that if you come from a JUnit background this will not + be the behaviour you are used to. + JUnit surprisingly reinstantiates the test case for each test + method to prevent such interference. + SimpleTest requires the end user to use setUp(), but + supplies additional hooks for library writers. +
++ The means of reporting test results (see below) are by a + visiting display class + that is notified by various assert...() + methods. + Here is the full list for the UnitTestCase + class, the default for SimpleTest... +
| assertTrue($x) | Fail if $x is false | +
| assertFalse($x) | Fail if $x is true | +
| assertNull($x) | Fail if $x is set | +
| assertNotNull($x) | Fail if $x not set | +
| assertIsA($x, $t) | Fail if $x is not the class or type $t | +
| assertNotA($x, $t) | Fail if $x is of the class or type $t | +
| assertEqual($x, $y) | Fail if $x == $y is false | +
| assertNotEqual($x, $y) | Fail if $x == $y is true | +
| assertIdentical($x, $y) | Fail if $x == $y is false or a type mismatch | +
| assertNotIdentical($x, $y) | Fail if $x == $y is true and types match | +
| assertReference($x, $y) | Fail unless $x and $y are the same variable | +
| assertCopy($x, $y) | Fail if $x and $y are the same variable | +
| assertWantedPattern($p, $x) | Fail unless the regex $p matches $x | +
| assertNoUnwantedPattern($p, $x) | Fail if the regex $p matches $x | +
| assertNoErrors() | Fail if any PHP error occoured | +
| assertError($x) | Fail if no PHP error or incorrect message | +
| assertErrorPattern($p) | Fail unless the error matches the regex $p | +
+ Some examples... +
+$variable = null; +$this->assertNull($variable, 'Should be cleared'); ++ ...will pass and normally show no message. + If you have + set up the tester to display passes + as well then the message will be displayed as is. +
+$this->assertIdentical(0, false, 'Zero is not false [%s]'); ++ This will fail as it performs a type + check as well as a comparison between the two values. + The "%s" part is replaced by the default + error message that would have been shown if we had not + supplied our own. + This also allows us to nest test messages. +
+$a = 1; +$b = $a; +$this->assertReference($a, $b); ++ Will fail as the variable $a is a copy of $b. +
+$this->assertWantedPattern('/hello/i', 'Hello world');
+
+ This will pass as using a case insensitive match the string
+ hello is contained in Hello world.
+
+trigger_error('Disaster');
+trigger_error('Catastrophe');
+$this->assertError();
+$this->assertError('Catastrophe');
+$this->assertNoErrors();
+
+ This one takes some explanation as in fact they all pass!
+
+ + PHP errors in SimpleTest are trapped and placed in a queue. + Here the first error check catches the "Disaster" + message without checking the text and passes. + This removes the error from the queue. + The next error check tests not only the existence of the error, + but also the text which here matches so another pass. + With the queue now empty the last test will pass as well. + If any unchecked errors are left at the end of a test method then + an exception will be reported in the test. + Note that SimpleTest cannot catch compile time PHP errors. +
++ The test cases also have some convenience methods for debugging + code or extending the suite... +
| setUp() | Runs this before each test method | +
| tearDown() | Runs this after each test method | +
| pass() | Sends a test pass | +
| fail() | Sends a test failure | +
| error() | Sends an exception event | +
| sendMessage() | Sends a status message to those displays that support it | +
| signal($type, $payload) | Sends a user defined message to the test reporter | +
| dump($var) | Does a formatted print_r() for quick and dirty debugging | +
| swallowErrors() | Clears the error queue | +
+ Of course additional test methods can be added to create + specific types of test case too so as to extend framework... +
+require_once('simpletest/unit_tester.php');
+
+class FileTester extends UnitTestCase {
+ function FileTester($name = false) {
+ $this->UnitTestCase($name);
+ }
+
+ function assertFileExists($filename, $message = '%s') {
+ $this->assertTrue(
+ file_exists($filename),
+ sprintf($message, 'File [$filename] existence check'));
+ }
+}
+
+ Here the SimpleTest library is held in a folder called
+ simpletest that is local.
+ Substitute your own path for this.
+
+ + This new case can be now be inherited just like + a normal test case... +
+class FileTestCase extends FileTester {
+
+ function setUp() {
+ @unlink('../temp/test.txt');
+ }
+
+ function tearDown() {
+ @unlink('../temp/test.txt');
+ }
+
+ function testCreation() {
+ $writer = &new FileWriter('../temp/test.txt');
+ $writer->write('Hello');
+ $this->assertFileExists('../temp/test.txt');
+ }
+}
+
+
+ + If you want a test case that does not have all of the + UnitTestCase assertions, + only your own and assertTrue(), + you need to extend the SimpleTestCase + class instead. + It is found in simple_test.php rather than + unit_tester.php. + See later if you + want to incorporate other unit tester's + test cases in your test suites. +
+ +
+
+Running a single test case
+
+
+ You won't often run single test cases except when bashing + away at a module that is having difficulty and you don't + want to upset the main test suite. + Here is the scaffolding needed to run the a lone test case... +
+<?php
+ require_once('simpletest/unit_tester.php');
+ require_once('simpletest/reporter.php');
+ require_once('../classes/writer.php');
+
+ class FileTestCase extends UnitTestCase {
+ function FileTestCase() {
+ $this->UnitTestCase('File test');
+ }
+ }
+
+ $test = &new FileTestCase();
+ $test->run(new HtmlReporter());
+?>
+
+ This script will run as is, but will output zero passes
+ and zero failures until test methods are added.
+
+
+