Version 2.0
You are browsing the unmaintained documentation for old Nette 2.0. See documentation for current Nette.

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
        );
    }
}