Posted on 19/6/08 by
Tim Koschützki
Hey folks,
most of us familiar with unit testing with CakePHP and SimpleTest know that
SimpleTest always executes all methods starting with the string "test".
This can be problematic sometimes:
- As you unit test your application you realize that the same test fails again and again and you cannot make it work and therefore want to tackle it in isolation from the other tests.
- Many tests fail and you want to tackle them one at a time without getting distracted by the failure messages of the others.
- Say you debug a model class of yours and have many tests for the same model function. Now, in order to get the one failing test working, you want to output some debug information within that model method. However, as there are many tests for that method, the debug information will always show for every test and your screen is full of debug information and you spend five or more seconds searching for the correct one.
- Executing all test functions takes a tremendous amount of time and you just need to get one test to work. You obviously don't want to spend 30 seconds waiting just to get the same failure message again and again. ; )
At this point you have two options:
- Create a new testcase, copy all the code that is needed to setUp() it properly, copy the test function over and observe and debug it there. Then copy back when you are done and then for every test method. This isn't very fast.
- Put an 'x' before every test method name except before the one you want to debug. This really slows you down if there are many test functions.
- Read the following tip and make your life a lot easier. ; )
As the CakeTestCase class extends SimpleTest's UnitTestCase class, it inherits all their functionality. Fortunately, SimpleTest internally has a useful mechanism to choose which functions to execute in the test class.
The name of this method is getTests(), which returns an array of methods that it shall execute. By default it looks like this:
function getTests
() {
$methods =
array();
foreach (get_class_methods(get_class($this)) as $method) {
if ($this->_isTest
($method)) {
$methods[] =
$method;
}
}
return $methods;
}
function _isTest
($method) {
if (strtolower(substr($method,
0,
4)) ==
'test') {
return ! SimpleTestCompatibility::
isA($this,
strtolower($method));
}
return false;
}
Pretty straightforward. Fetches all method names from the current class and tests if they start with 'test' (and looks if they are tests). So we see all that getTests() really does is return an array of test methods.
Let's look at an example on how to put this to use. Say you have the following tests (which absolutely make no sense whatsoever) in your custom CakePHP Test class:
class MyHumbleTest extends CakeTestCase {
function setUp() {}
function testOfIdenticalClass() {
$this->assertIdentical('class', 'class');
}
function testOfIdenticalName() {
$this->assertIdentical('name', 'name');
}
function tearDown() {}
}
.. and then some. Now you want to check the testOfIdenticalName method alone. You would add a getTests() implementation:
class MuHumbleTest
extends CakeTestCase
{
function setUp
() {}
function testOfIdenticalClass
() {
$this->
assertIdentical('class',
'class');
}
function testOfIdenticalName
() {
$this->
assertIdentical('name',
'name');
}
function getTests
() {
return array('testOfIdenticalName');
}
function tearDown
() {}
}
Very cool and handy, yes? Life is getting easier with such things, especially with many test functions.
One thing to note is that if you want to keep fixtures working, you have to tell it to still execute the start(), startCase(), endCase() an end() callbacks:
class MuHumbleTest
extends CakeTestCase
{
function setUp
() {}
function testOfIdenticalClass
() {
$this->
assertIdentical('class',
'class');
}
function testOfIdenticalName
() {
$this->
assertIdentical('name',
'name');
}
function getTests
() {
$methods =
array('testOfIdenticalName');
return am
(a
('start',
'startCase'),
$methods, a
('endCase',
'end'));
}
function tearDown
() {}
}
One thing to note is that in order for Cake's start(), startCase() callbacks to work you need to add them, too:
-- Tim Koschuetzki aka DarkAngelBGE