Eclipse integration

Developing Unit Tests

This is a very terse documentation of the PHPUnit features we usually use in our unit tests. It is meant to document PKP-specific usage of PHPUnit and as a cheat sheet for the most important PHPUnit functionality. It does not replace the PHPUnit manual. Please refer to the manual when you are using PHPUnit for the first time.

Nomenclature

Unit test classes are called after the class they are testing, e.g. the test to test the "Config" class would be "ConfigTest".

PHPUnit works well with our usual PKP file name nomenclature. Files are named after the test class name with a ".inc.php" postfix, e.g. the ConfigTest class would be found in the file "ConfigTest.inc.php".

We'll use the same folder structure inside the tests directory that we use for the classes we test.

Tests for PKP library classes will be in "lib/pkp/tests". Tests for PKP application classes will be in "/tests".

Which base class to extend

Unit tests that require database access extend "DatabaseTestCase".

All other test cases extend "PKPTestCase".

Test Documentation

One of the functions of a test case is documenting the class specification in a very detailed and precise way. Please make sure that your source code contains enough method and inline documentation so that an outsider can quickly understand how the class' API is to be used. This is not necessary if the test method name speaks for itself.

Use a @see annotation in the class comment that points to the tested class.

How to test exceptions

A test method that is annotated with "@expectedException ExceptionClass" will fail if it does not throw the declared exception.

What if my test class calls a DAO?

We always try to test classes with as little dependencies as possible. We want our unit tests to concentrate on one class without relying on the functionality of other classes.

An example: We do not want to rely on a functioning database infra-structure when testing application objects that draw data from the DAO application layer.

Luckily PHPUnit provides us with a very elegant and lean solution to this dependency problem. See the following test code for an example:

class SubmissionTest extends PKPTestCase {
[...]
public function testGetUser() {
// Our system under test (SUT)
$submission = new Submission();
$submission->setUserId(5);
// Mock the UserDAO
$mockUserDAO =& $this->getMock('UserDAO', array('getUser'));
DAORegistry::registerDAO('UserDAO', $mockUserDAO);
// Set up the mock getUser() method
$result = new PKPUser();
$result->setId(5);
$mockUserDAO->expects($this->once()) // getUser should be called once
->method('getUser')
->with($this->equalTo(5)) // expected input
->will($this->returnValue($result)); // mock output
// The following assertion will pass if and only if the following
// conditions are met:
// * UserDAO->getUser() is called exactly once
// * UserDAO->getUser() is called with one parameter equal to 5
// * $submission->getUser() returns exactly the object we return from
// our mock object.
self::assertEquals($result, $submission->getUser());
}
[...]
}

What if my test class depends on calls to static class methods?

From a testing point of view, static method calls are evil. It is impossible to mock static method calls with the system under test being unaware of it. Static method calls are not so nice for other reasons as well: They make it difficult to "drop in" different implementations of a class at runtime. This restricts design flexibility and code maintainability. It is usually better to use the singleton pattern instead.

If you cannot avoid static method calls or if you have to rely on legacy classes then you can manually create a mock class that will be included rather than the standard PKP implementation:

create a file called "Mock<Classname>.inc.php" in the same directory as your unit test class.

in this file implement a mock version of <Classname>.

implement only the methods required either during PKP framework initialization or for your own test.

the test framework will automatically drop in the mock class instead of the original class

If you need the same mock class several times then you can implement it in the 'tests/mock' folder and drop a file with the same name into your current unit test directory that imports the central mock class from the 'tests/mock' folder.

Here is an example for a mock Locale class. The class is placed in the file MockLocale.inc.php in the '/lib/pkp/tests/mock' directory. It is being included from many different unit test directories. One example is '/lib/pkp/tests/classes/core'.

PHPUnit annotations

Declaring Coverage

The @covers annotation indicates to the test coverage analyser the class or method to be covered by a given unit test. The @covers annotation is mandatory for all tests. The function format is always preferable to the class format. You'll very rarely write unit tests that cover a whole class.

method format: @covers ClassName::methodName

class format: @covers ClassName

Documenting Test Dependency

The @depends defines a logical dependency of one test case from another. You use the depends annotation if you want to document such dependency or if you want to use results from one test case as input to another test case. Test execution order is only dependent on the order of the methods in the test class, not on the @depends annotation.

Running Unit Tests

Select the test file you want to execute. You can do this in two ways.

Select the editor tab that contains the test class:

Select the file with the test class in the project explorer

Make sure that the file / edit tab is marked blue as in the screen shots. Otherwise you'll get an error when executing your test.

Next you have to decide whether you want to simply run or whether you want to debug your test.

To run your test select the phpunit-ext external tool configuration from the external tools' favorites drop-down:

You can see the test's output in the console view.

To debug your test set breakpoints at appropriate places in the code, then select the phpunit debug configuration from your debug configurations' favorites drop-down:

You can see test results in the "Debug Output" view. Sometimes the console output is not complete in the debug output. Use the external tool if you want the complete console output.

That's it. Now you're ready to develop and execute your tests. Enjoy!

What if my test fails with an error?

You've got various possibilities to debug your test. Usually the test will show a backtrace when there was an error. If this is not the case then have a look in "tests/results/error.log". Errors that occur within the PKP library will be logged there.

If this still doesn't help then try to debug your test. Set a break point and execute the debugger. See Configure Eclipse for PHPUnit for debug configuration instructions.

Installing and Configuring the Build Environment

Phing

On the command line execute

pear channel-discover pear.phing.info
pear install phing/phing

The section about the build environment is not complete yet. Please ignore it for the time being!