dagfinn | 03 August, 2007 13:44
I've been using mock objects when testing code that uses files and other stuff that's hard to test because there are too many effects on the code's surroundings and you have to test . I've been doing that on an ad hoc basis. Now Mike Naberezny has an interesting generalized class for wrapping PHP modules to make it possible to replace them with mocks.
That solves some of those challenges, but there are some functions that are not amenable to that approach. exit() is one of them. Let's try using anonymous functions. exit() is a particularly hot candidate, since it plays havoc with tests. If the code under test exits before the test run is finished, it's like hitting the poor test framework over the head with a brick. It's out cold and has no chance to report the results from the test.
An anonymous function to replace exit() can be created with create_function(), as in the constructor of this class:
<?php
class Suicidal {
private $exitfunc;
function __construct($exitfunc) {
$this->exitfunc = $exitfunc ? $exitfunc
: create_function('','exit();');
}
function goodbyeCruelWorld() {
call_user_func($this->exitfunc);
}
}
The goodbyeCruelWorld() method exits nicely when it's run. But we have a back door that allows us to do something else. We can pass a different anonymous function in the constructor, as in this test case:
class ExitTest extends UnitTestCase {
function setUp() {
$GLOBALS["exit_called"] = FALSE;
}
function testMockExit() {
$exitfunc = create_function(
'','$GLOBALS["exit_called"] = TRUE;');
$suicidal = new Suicidal($exitfunc);
$suicidal->goodbyeCruelWorld();
$this->assertTrue($GLOBALS["exit_called"]);
}
}
We're replacing the exit function with one that just sets a global variable that lets us check that the function has actually been called. The pattern we're using here is an alternative to a mock object, called a Test Spy.
Globals are often frowned upon, and for good reasons, but in this context, they are perfectly innocuous. It's a small restricted test environment; the risk of getting lost and not knowing which variable was set where is negligible.
We just have to be careful to avoid interference between tests. The way to do that is to reset all globals appropriately in setUp() and/or tearDown(). By the way, this applies equally to superglobals such as $_GET and $_POST.
| « | August 2007 | » | ||||
|---|---|---|---|---|---|---|
| Su | Mo | Tu | We | Th | Fr | Sa |
| 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 | |