1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
<?xml version="1.0"?>
<page title="Grouping tests" here="Grouping tests">
<long_title>
PHP unit testing tutorial - Grouping together unit
tests and examples of writing test cases
</long_title>
<content>
<p>
Next up we will fill in some blanks and create a test suite.
</p>
<p>
<a class="target" name="another"><h2>Another test</h2></a>
</p>
<p>
Adding another test can be as simple as adding another method
to a test case...
<php><![CDATA[
class TestOfLogging extends UnitTestCase {
function TestOfLogging() {
$this->UnitTestCase('Log class test');
}
function testCreatingNewFile() {
@unlink('../temp/test.log');
$log = new Log('../temp/test.log');
$this->assertFalse(file_exists('../temp/test.log'), 'Created before message');
$log->message('Should write this to a file');
$this->assertTrue(file_exists('../temp/test.log'), 'File created');<strong>
@unlink('../temp/test.log');</strong>
}<strong>
function testAppendingToFile() {
@unlink('../temp/test.log');
$log = new Log('../temp/test.log');
$log->message('Test line 1');
$messages = file('../temp/test.log');
$this->assertWantedPattern('/Test line 1/', $messages[0]);
$log->message('Test line 2');
$messages = file('../temp/test.log');
$this->assertWantedPattern('/Test line 2/', $messages[1]);
@unlink('../temp/test.log');
}</strong>
}
]]></php>
The <code>assertWantedPattern()</code>
test case method uses Perl style regular expressions for
matching.
</p>
<p>
All we are doing in this new test method is writing a line to a file and
reading it back twice over.
We simply want to confirm that the logger appends the
text rather than writing over the old file.
A little pedantic, but hey, it's a tutorial!
</p>
<p>
In fact this unit test actually passes straight away...
<div class="demo">
<h1>Log class test</h1>
<div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
<strong>4</strong> passes and <strong>0</strong> fails.</div>
</div>
The trouble is there is already a lot of repetition here,
we have to delete the test file before and after every test.
With outrageous plagarism from <a href="http://www.junit.org/">JUnit</a>,
SimpleTest has <code>setUp()</code> and
<code>tearDown()</code> methods
which are run before and after every test respectively.
File deletion is common to all the test methods so we
should move that operation there.
</p>
<p>
Our tests are green so we can refactor...
<php><![CDATA[
class TestOfLogging extends UnitTestCase {
function TestOfLogging() {
$this->UnitTestCase('Log class test');
}<strong>
function setUp() {
@unlink('../temp/test.log');
}
function tearDown() {
@unlink('../temp/test.log');
}
function testCreatingNewFile() {</strong>
$log = new Log('../temp/test.log');
$this->assertFalse(file_exists('../temp/test.log'), 'Created before message');
$log->message('Should write this to a file');
$this->assertTrue(file_exists('../temp/test.log'), 'File created');<strong>
}
function testAppendingToFile() {</strong>
$log = new Log('../temp/test.log');
$log->message('Test line 1');
$messages = file('../temp/test.log');
$this->assertWantedPattern('/Test line 1/', $messages[0]);
$log->message('Test line 2');
$messages = file('../temp/test.log');
$this->assertWantedPattern('/Test line 2/', $messages[1]);<strong>
}</strong>
}
]]></php>
The test stays green.
We can add non-test methods to the test case as long as the method
name does not start with the string "test".
Only the methods that start "test" are run.
This allows further optional refactoring...
<php><![CDATA[
class TestOfLogging extends UnitTestCase {
function TestOfLogging() {
$this->UnitTestCase('Log class test');
}
function setUp() {
@unlink('../temp/test.log');
}
function tearDown() {
@unlink('../temp/test.log');
}<strong>
function getFileLine($filename, $index) {
$messages = file($filename);
return $messages[$index];
}</strong>
function testCreatingNewFile() {
$log = new Log('../temp/test.log');
$this->assertFalse(file_exists('../temp/test.log'), 'Created before message');
$log->message('Should write this to a file');
$this->assertTrue(file_exists('../temp/test.log'), 'File created');
}
function testAppendingToFile() {
$log = new Log('../temp/test.log');
$log->message('Test line 1');<strong>
$this->assertWantedPattern('/Test line 1/', $this->getFileLine('../temp/test.log', 0));</strong>
$log->message('Test line 2');<strong>
$this->assertWantedPattern('/Test line 2/', $this->getFileLine('../temp/test.log', 1));</strong>
}
}
]]></php>
It is a matter of taste whether you prefer this version
to the previous one. There is a little more code, but
the logic of the test is clearer.
</p>
<p>
<a class="target" name="group"><h2>A group test</h2></a>
A test case does not function alone for very long.
When coding for real we usually want to run as many tests as
quickly and as often as we can.
This means grouping them together into test suites that
could easily include every test in the application.
</p>
<p>
Firstly we have to clean out the test running code from
our existing test case...
<php><![CDATA[
<?php<strong>
require_once('../classes/log.php');
class TestOfLogging extends UnitTestCase {
...
}</strong>
?>
]]></php>
We no longer need the <code>SIMPLE_TEST</code>
constant.
Next we create a group test called <em>all_tests.php</em>
in the <em>tests</em> folder...
<php><![CDATA[
<strong><?php
if (! defined('SIMPLE_TEST')) {
define('SIMPLE_TEST', 'simpletest/');
}
require_once(SIMPLE_TEST . 'unit_tester.php');
require_once(SIMPLE_TEST . 'reporter.php');
require_once('log_test.php');
$test = &new GroupTest('All tests');
$test->addTestCase(new TestOfLogging());
$test->run(new HtmlReporter());
?></strong>
]]></php>
We hardly notice the difference when things work...
<div class="demo">
<h1>All tests</h1>
<div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
<strong>4</strong> passes and <strong>0</strong> fails.</div>
</div>
Group tests add to the test case count.
Adding new test cases is very straight forward.
Simply include the test cases file and add any contained test
cases individually.
You can also nest group tests within other group tests
(although you should avoid loops).
</p>
<p>
In the <a href="gain_control_tutorial.php">next page</a>
we will add these more quickly.
</p>
</content>
<internal>
<link>
<a href="#another">Adding another test</a> to the test case
and refactoring.
</link>
<link>
The crude way to <a href="#group">group unit tests</a>.
</link>
</internal>
<external>
<link>
<a href="gain_control_tutorial.php">Next</a> is controlling
how the class under test interacts with the rest
of the system.
</link>
<link>
<a href="first_test_tutorial.php">Previous</a> is the creation
of a first test.
</link>
<link>
You need <a href="simple_test.php">SimpleTest</a> to run these examples.
</link>
</external>
<meta>
<keywords>
software development,
php programming,
programming in php,
test first,
software development tools,
php tutorial,
free php scripts,
architecture,
php resources,
mock objects,
junit,
php testing,
unit test,
phpunit,
PHP unit testing
</keywords>
</meta>
</page>
|