Testing
Even good programmers make mistakes. The difference between a good and a bad programmer is that the former one quickly reveals errors with the help of automated tests.
- “Those who do not test are condemned to repeat their mistakes.”
- “Whenever you get rid of an error, another pops up.” (Murphy's law)
- “Test, test, test” (Martin Ilyich Fowler)
Have you ever written a similar PHP code?
$obj = new MyClass;
$result = $obj->process($input);
var_dump($result);
In other words, have you printed the result of a function call in order to visually verify that the function returns the expected result? Surely you do it many times a day. Do you expect that the class will not break in the future in the case that everything has worked correctly and you have only deleted the debugging line of code? Murphy's law guarantees the opposite :-)
If you had not deleted the test, you can run it anytime in the future to verify that everything still works as it should. It is desirable to modify the previous code slightly so as it does not require a human review:
function assertTrue($condition)
{
echo $condition ? 'OK' : 'ERROR: test failed';
}
$obj = new MyClass;
$result = $obj->process($input);
assertTrue( $result === array(1, 2, 3) );
You may create a large amount of these tests over the time, so it would be nice if we were able to run them automatically. This can be done with the help of a testing framework, such as Nette\test used to test Nette Framework itself or very popular PHPUnit.
PHPUnit
At first, install PHPUnit. Type to the command line:
pear channel-discover pear.phpunit.de pear install phpunit/PHPUnit
Whether the installation was successful can be found out by typing ‘phpunit’ to the command line and checking that the program runs.
In the conception of PHPUnit a test is each class which is inherited from PHPUnit_Framework_TestCase
with the
extension Test
in the title (for example MyClassTest
), which is stored in a file with the same name as
the class name. The actual testing takes place in its methods with the prefix test
, for example
testProcess()
. Before any such method is called, the setUp()
method is invoked, which is supposed to set
up a test environment to its default state.
To verify returned results we use so called asserts. PHPUnit offers a variety of them, probably the most widely used are
assertTrue($result)
, assertFalse($result)
and assertEquals($expectedValue, $result)
.
The file MyClassTest.php
may look like this:
require_once 'PHPUnit/Framework.php';
class MyClassTest extends PHPUnit_Framework_TestCase
{
public function testProcess()
{
$obj = new MyClass;
$result = $obj->process($input);
$this->assertEquals( array(1, 2, 3), $result );
}
}
The test can be run by navigating to the directory with the test file in the command line and typing the command:
phpunit .
Presenter testing
How to test a function of a presenter? Each presenter has a function run()
, which converts a request (object
Nette\Application\Request
) into a response (object Nette\Application\IResponse
).
To run properly, presenter requires an object of Nette\DI\Container
containing configured services. The easiest way to inject the configured DI Container is by using the static class
Nette\Environment
, which is highly suggested not to use because it remains in the framework mainly for
backwards-compatibility reasons. The usage of Nette\Environment
is justifiable only in PHPUnit tests.
So we create a request and then we use asserts to check the returned answer.
class HomepagePresenterTest extends PHPUnit_Framework_TestCase
{
public function testRenderDefault()
{
$presenter = new HomepagePresenter;
$presenter->setContext(Nette\Environment::getContext());
$request = new Nette\Application\Request('Homepage', 'GET');
$response = $presenter->run($request);
$this->assertType(
'Nette\Application\Responses\TextResponse',
$response
);
}
}