Advanced PHPUnit: Annotations

PHPUnit, borrowing a handy feature from Java’s JUnit testing library allows you to use certain directives beginning with “@” in comments of your test or application code to access test-related functionality. These directives, known as annotations, even expose features not otherwise accessible to PHPUnit users. While references to an annotation will generally appear in the PHPUnit manual section relevant to its functionality, I could not find a comprehensive list of all available annotations in the current release and what effect they have. The following list contains (to the best of my knowledge) all of the annotations implemented as of PHPUnit 3.3, as well as some of the benefits and drawbacks of using each:

@assert

When put into a function’s docblock, this annotation causes the skeleton generator to automatically create tests with the given assertion. This can be useful for simple tests where the arguments can fit in the allowed format, especially when you want to test multiple input values since you can put multiple @assert annotations for one function. Since PHPUnit copies the argument list directly out of the annotation, you can even construct objects to supply as arguments. However, this annotation falls short when the method under test requires more complicated setup for its parameters than can fit in a single line. In general, though, using @assert and the skeleton generator can save a fair amount of time developing tests for new classes as you create them.

@codeCoverageIgnoreStart & @codeCoverageIgnoreEnd

These instructions cause the code block between them to be marked as executed by code coverage reporting. This allows you to except sections of code from testing that depend upon state which should be impossible to reach in normal test situations (e.g., error handling code for failures in external systems and native PHP functions). This annotation functions slightly differently than the others, in that it will probably be used in inline comments rather than function or class PHPDoc blocks. Also, it is worth noting that the code lines will be marked in the coverage report as “covered”. This may give the false impression that the code is actually tested rather than being marked as explicitly untested.

@dataProvider

This indicates the given function provides an array of argument arrays to be iterated over by the test function. This comes in handy when you need to provide a series of different data sets to the function under test. In running the tests, PHPUnit will treat each argument array as a different test and provide you with the list of arguments used when the test fails, still allowing you to effectively localize failures. Using @dataProvider, however, means that you have the test code physically separated from the fixture creation code, which means you may have to scroll back and forth to figure out exactly what the data being provided to the test method represents.

@expectedException

The @expectedException annotation, very similar to the setExpectedException() method on PHPUnit’s TestCase class, marks that the function expects a particular type of exception to be thrown. One notable difference with this syntax from setExpectedException() is that if you specify an expected message using the annotation, the parsing assumes the exception’s message contains no spaces. Thus, if you tend to throw exceptions with messages having spaces in them, the annotation will only match on the first word of the message (and will expect the second word to be the exception code). With the setExpectedException() method, the message to match against is passed in as a string and suffers no such limitations. Also, with the setExpectedException() method, you can set the expected exception immediately prior to calling the method under test, whereas with this annotation anything in the test method prior to the call to the method under test could be throwing the exception and the test would still pass.

@group

This marks the test method as a member of a particular test group, allowing you to easily run or exclude only a focused subset of related tests. This could be used, for example, to mark certain tests as related to a particular customer issue or to create a smaller “smoke test” when your test suite gets too unwieldy. To run multiple groups of tests simultaneously, separate the group names with commas.

Run “phpunit --group <group1,group2,...> AllTests.php” to only run tests from the given groups.
Run “phpunit --exclude-group <group1,group2,...> AllTests.php” to run all tests except those from the given groups.
Run “phpunit --list-groups AllTests.php” to get a list of all available groups in that test suite.

@test

This annotation is just a different syntax for marking that a given function in a test class is a test. If you put this in a function’s block comment, you do not need the function name to begin with “test…” for PHPUnit to recognize it as such. This extra freedom allows you to decide on your favorite naming scheme, such as matching the name of the function in the test class to the name in the actual class or putting “Test” at the end of the method name rather than the beginning.