3. Annotations
An annotation is a special form of syntactic metadata that can be added to
the source code of some programming languages. Until PHP 8, PHP had no dedicated
language feature for attaching metadata to units of code. The usage of tags such as
@annotation arguments
in a documentation block has been established in the PHP
community to annotate source code since the time of PHP 4. Documentation blocks can
be accessed through the Reflection API’s getDocComment()
method on the function,
class, method, and attribute level. Applications such as PHPUnit can use this
information at runtime to access annotations in documentation blocks.
Note
A doc comment in PHP must start with /**
and end with
*/
. Annotations in any other style of comment will be
ignored.
Prior to PHPUnit 10, annotations in special PHP comments, so-called “DocBlocks” or “doc-comments”, were the only means of attaching metadata to code units. These annotations are documented in this appendix.
PHPUnit will first look for metadata in attributes before it looks for annotations in comments. When metadata is found in attributes, metadata in comments is ignored. Support for metadata in comments is closed for further development: bugs will be fixed, but no new functionality will be implemented based on annotations.
Note
Support for annotations in comments in test code is deprecated and will be removed in PHPUnit 12. Use attributes instead.
@after
The @after
annotation can be used to specify methods
that should be called after each test method in a test case class.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase
{
/**
* @after
*/
public function tearDownSomeFixtures(): void
{
// ...
}
/**
* @after
*/
public function tearDownSomeOtherFixtures(): void
{
// ...
}
}
@afterClass
The @afterClass
annotation can be used to specify
static methods that should be called after all test methods in a test
class have been run to clean up shared fixtures.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase
{
/**
* @afterClass
*/
public static function tearDownSomeSharedFixtures(): void
{
// ...
}
/**
* @afterClass
*/
public static function tearDownSomeOtherSharedFixtures(): void
{
// ...
}
}
@backupGlobals
PHPUnit can optionally backup all global and super-global variables before each test and restore this backup after each test.
The @backupGlobals enabled
annotation can be used on the class level to enable this operation for all tests of a test case class:
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* @backupGlobals enabled
*/
final class MyTest extends TestCase
{
// ...
}
The @backupGlobals
annotation can also be used on the
test method level. This allows for a fine-grained configuration of the
backup and restore operations:
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* @backupGlobals enabled
*/
final class MyTest extends TestCase
{
public function testThatInteractsWithGlobalVariables()
{
// ...
}
/**
* @backupGlobals disabled
*/
public function testThatDoesNotInteractWithGlobalVariables(): void
{
// ...
}
}
@backupStaticAttributes
PHPUnit can optionally backup all static attributes in all declared classes before each test and restore this backup after each test.
The @backupStaticAttributes enabled
annotation can be used on the class level to enable this operation for all tests of a test case class:
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* @backupStaticAttributes enabled
*/
final class MyTest extends TestCase
{
// ...
}
The @backupStaticAttributes
annotation can also be used on the
test method level. This allows for a fine-grained configuration of the
backup and restore operations:
use PHPUnit\Framework\TestCase;
/**
* @backupStaticAttributes enabled
*/
class MyTest extends TestCase
{
public function testThatInteractsWithStaticAttributes(): void
{
// ...
}
/**
* @backupStaticAttributes disabled
*/
public function testThatDoesNotInteractWithStaticAttributes(): void
{
// ...
}
}
Note
@backupStaticAttributes
is limited by PHP internals
and may cause unintended static values to persist and leak into
subsequent tests in some circumstances.
See Global State for details.
@before
The @before
annotation can be used to specify methods
that should be called before each test method in a test case class.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase
{
/**
* @before
*/
public function setupSomeFixtures(): void
{
// ...
}
/**
* @before
*/
public function setupSomeOtherFixtures(): void
{
// ...
}
}
@beforeClass
The @beforeClass
annotation can be used to specify
static methods that should be called before any test methods in a test
class are run to set up shared fixtures.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase
{
/**
* @beforeClass
*/
public static function setUpSomeSharedFixtures(): void
{
// ...
}
/**
* @beforeClass
*/
public static function setUpSomeOtherSharedFixtures(): void
{
// ...
}
}
@codeCoverageIgnore*
The @codeCoverageIgnore
,
@codeCoverageIgnoreStart
and
@codeCoverageIgnoreEnd
annotations can be used
to exclude lines of code from the coverage analysis.
For usage see Ignoring Code Blocks.
Note
Please note that while annotations for test code have been deprecated and support
for them will be removed in PHPUnit 12, the @codeCoverageIgnore
,
@codeCoverageIgnoreStart
, and @codeCoverageIgnoreEnd
annotations have not
been deprecated and support for them will not be removed.
@covers
The @covers
annotation can be used in the test code to
specify which parts of the code it is supposed to test:
/**
* @covers \BankAccount
*/
public function testBalanceIsInitiallyZero(): void
{
$this->assertSame(0, $this->ba->getBalance());
}
If provided, this effectively filters the code coverage report to include executed code from the referenced code parts only. This will make sure that code is only marked as covered if there are dedicated tests for it, but not if it used indirectly by the tests for a different class, thus avoiding false positives for code coverage.
This annotation can be added to the docblock of the test class or the individual test methods. The recommended way is to add the annotation to the docblock of the test class, not to the docblock of the test methods.
When the requireCoverageMetadata
configuration option in the
configuration file is set to true
,
every test method needs to have an associated @covers
annotation
(either on the test class or the individual test method).
appendixes.annotations.covers
shows
the syntax of the @covers
annotation.
The section Targeting Units of Code
provides longer examples for using the annotation.
Please note that this annotation requires a fully-qualified class name (FQCN). To make this more obvious to the reader, it is recommended to use a leading backslash (even if this not required for the annotation to work correctly).
@covers ClassName
(recommended)
Specifies that the annotated test class covers all methods of a given class.
@covers ClassName::methodName
(not recommended)
Specifies that the annotated test class covers the specified method.
@covers ::functionName
(recommended)
Specifies that the annotated test class covers the specified global function.
@coversDefaultClass
The @coversDefaultClass
annotation can be used to
specify a default namespace or class name. That way long names don’t need to be
repeated for every @covers
annotation. See
Example 3.1.
Please note that this annotation requires a fully-qualified class name (FQCN). To make this more obvious to the reader, it is recommended to use a leading backslash (even if this not required for the annotation to work correctly).
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* @coversDefaultClass \Foo\CoveredClass
*/
final class CoversDefaultClassTest extends TestCase
{
/**
* @covers ::publicMethod
*/
public function testSomething(): void
{
$o = new Foo\CoveredClass;
$o->publicMethod();
}
}
@coversNothing
The @coversNothing
annotation can be used in the
test code to specify that no code coverage information will be
recorded for the annotated test case.
This can be used for integration testing. See A test that specifies that it does not want to contribute to code coverage for an example.
The annotation can be used on the class and the method level and
will override any @covers
tags.
@dataProvider
A test method can accept arbitrary arguments. These arguments are to be
provided by one or more data provider methods (provider()
in
Using a data provider that returns an array of arrays).
The data provider method to be used is specified using the
@dataProvider
annotation.
See Data Providers for more details.
@depends
PHPUnit supports the declaration of explicit dependencies between test
methods. Such dependencies do not define the order in which the test
methods are to be executed but they allow the returning of an instance of
the test fixture by a producer and passing it to the dependent consumers.
Using the Depends attribute to express dependencies shows
how to use the @depends
annotation to express
dependencies between test methods.
See Test Dependencies for more details.
@doesNotPerformAssertions
Prevents a test that performs no assertions from being considered risky.
@group
A test can be tagged as belonging to one or more groups using the
@group
annotation like this
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase
{
/**
* @group specification
*/
public function testSomething(): void
{
}
/**
* @group regression
* @group bug2204
*/
public function testSomethingElse(): void
{
}
}
The @group
annotation can also be provided for the test
class. It is then “inherited” to all test methods of that test class.
Tests can be selected for execution based on groups using the
--group
and --exclude-group
options
of the command-line test runner or using the respective directives of the
XML configuration file.
@large
The @large
annotation is an alias for
@group large
.
If the PHP_Invoker
package is installed and strict
mode is enabled, a large test will fail if it takes longer than 60
seconds to execute. This timeout is configurable via the
timeoutForLargeTests
attribute in the XML
configuration file.
@medium
The @medium
annotation is an alias for
@group medium
. A medium test must not depend on a test
marked as @large
.
If the PHP_Invoker
package is installed and strict
mode is enabled, a medium test will fail if it takes longer than 10
seconds to execute. This timeout is configurable via the
timeoutForMediumTests
attribute in the XML
configuration file.
@preserveGlobalState
When a test is run in a separate process, PHPUnit will
attempt to preserve the global state from the parent process by
serializing all globals in the parent process and unserializing them
in the child process. This can cause problems if the parent process
contains globals that are not serializable. To fix this, you can prevent
PHPUnit from preserving global state with the
@preserveGlobalState
annotation.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase
{
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testInSeparateProcess(): void
{
// ...
}
}
@requires
The @requires
annotation can be used to skip tests when common
preconditions, like the PHP Version or installed extensions, are not met.
Type |
Possible Values |
Examples |
Another example |
---|---|---|---|
|
Any PHP version identifier along with an optional operator |
@requires PHP 7.1.20 |
@requires PHP >= 7.2 |
|
Any PHPUnit version identifier along with an optional operator |
@requires PHPUnit 7.3.1 |
@requires PHPUnit < 8 |
|
A regexp matching PHP_OS |
@requires OS Linux |
@requires OS WIN32|WINNT |
|
Any OS family |
@requires OSFAMILY Solaris |
@requires OSFAMILY Windows |
|
Any valid parameter to function_exists |
@requires function imap_open |
@requires function ReflectionMethod::setAccessible |
|
Any extension name along with an optional version identifier and optional operator |
@requires extension mysqli |
@requires extension redis >= 2.2.0 |
|
Name and value for a |
@requires setting date.timezone Europe/Berlin |
The following operators are supported for PHP, PHPUnit, and extension version constraints: <
, <=
, >
, >=
, =
, ==
, !=
, <>
.
Versions are compared using PHP’s version_compare function. Among other things, this means that the =
and ==
operator can only be used with complete X.Y.Z
version numbers and that just X.Y
will not work.
@runTestsInSeparateProcesses
Indicates that all tests in a test class should be run in a separate PHP process.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* @runTestsInSeparateProcesses
*/
final class MyTest extends TestCase
{
// ...
}
Note: By default, PHPUnit will attempt to preserve the global state from the parent process by serializing all globals in the parent process and unserializing them in the child process. This can cause problems if the parent process contains globals that are not serializable. See @preserveGlobalState for information on how to fix this.
@runInSeparateProcess
Indicates that a test should be run in a separate PHP process.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class MyTest extends TestCase
{
/**
* @runInSeparateProcess
*/
public function testInSeparateProcess(): void
{
// ...
}
}
@small
The @small
annotation is an alias for
@group small
. A small test must not depend on a test
marked as @medium
or @large
.
If the PHP_Invoker
package is installed and strict
mode is enabled, a small test will fail if it takes longer than 1
second to execute. This timeout is configurable via the
timeoutForSmallTests
attribute in the XML
configuration file.
Note
Tests need to be explicitly annotated by either @small
,
@medium
, or @large
to enable run time limits.
@test
As an alternative to prefixing your test method names with
test
, you can use the @test
annotation in a method’s DocBlock to mark it as a test method.
/**
* @test
*/
public function initialBalanceShouldBe0(): void
{
$this->assertSame(0, $this->ba->getBalance());
}
@testdox
Specifies an alternative description used when generating the agile documentation sentences.
The @testdox
annotation can be applied to both test classes and test methods.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* @testdox A bank account
*/
final class BankAccountTest extends TestCase
{
/**
* @testdox has an initial balance of zero
*/
public function balanceIsInitiallyZero(): void
{
$this->assertSame(0, $this->ba->getBalance());
}
}
Note
Prior to PHPUnit 7.0 (due to a bug in the annotation parsing), using
the @testdox
annotation also activated the behaviour
of the @test
annotation.
When using the @testdox
annotation at method level with a @dataProvider
you may use the method parameters as placeholders in your alternative description.
$_dataName
is available in addition to use the actual name of the current data. That would be data set 1
up to 4 in below example.
/**
* @dataProvider additionProvider
* @testdox Adding $a to $b results in $expected
*/
public function testAdd($a, $b, $expected)
{
$this->assertSame($expected, $a + $b);
}
public static function additionProvider()
{
return [
'data set 1' => [0, 0, 0],
'data set 2' => [0, 1, 1],
'data set 3' => [1, 0, 1],
'data set 4' => [1, 1, 3]
];
}
@testWith
Instead of implementing a method for use with @dataProvider
,
you can define a data set using the @testWith
annotation.
A data set consists of one or many elements. To define a data set with multiple elements, define each element in a separate line. Each element of the data set must be an array defined in JSON.
See Data Providers to learn more about passing a set of data to a test.
/**
* @testWith ["test", 4]
* ["longer-string", 13]
*/
public function testStringLength(string $input, int $expectedLength): void
{
$this->assertSame($expectedLength, strlen($input));
}
An object representation in JSON will be converted into an associative array.
/**
* @testWith [{"day": "monday", "conditions": "sunny"}, ["day", "conditions"]]
*/
public function testArrayKeys(array $array, array $keys): void
{
$this->assertSame($keys, array_keys($array));
}
@ticket
The @ticket
annotation is an alias for the
@group
annotation (see @group) and allows to filter tests based
on their ticket ID.
@uses
The @uses
annotation specifies code which will be
executed by a test, but is not intended to be covered by the test. A good
example is a value object which is necessary for testing a unit of code.
/**
* @covers \BankAccount
* @uses \Money
*/
public function testMoneyCanBeDepositedInAccount(): void
{
// ...
}
Example 7.1 shows another example.
In addition to being helpful for persons reading the code, this annotation is useful in strict coverage mode where unintentionally covered code will cause a test to fail. See Unintentionally Covered Code for more information regarding strict coverage mode.
Please note that this annotation requires a fully-qualified class name (FQCN). To make this more obvious to the reader, it is recommended to use a leading backslash (even if this is not required for the annotation to work correctly).