2. Аннотации
Аннотация — специальная форма синтаксических метаданных, которые могут добавлены
в исходный код некоторых языков программирования. Хотя у PHP нет собственной
языковой возможности для аннотирования исходного кода, использование тегов, таких как
@annotation arguments
в блоке документации (альтернативное название — докблок), было принято
в сообществе PHP для аннотации кода. В PHP блоки документации
«рефлексивны»: к ним можно получить доступ с помощью
метода API Reflection getDocComment()
на уровне функции,
класса, методе и атрибуте. Такие приложения, как PHPUnit, используют
информацию во время выполнения для настройки их поведения.
Примечание
Комментарий документации в PHP должен начинаться с /**
и заканчиваться
*/
. Аннотации в любом другом стиле комментария будут проигнорированы.
В этом приложении представлены все разновидности аннотаций, поддерживаемые PHPUnit.
@author
Аннотация @author
— это псевдоним для аннотации
@group
(см. @group), позволяющая фильтровать тесты
на основе их авторов.
@after
Аннотацию @after
можно использовать для указания методов,
которые должны вызываться после каждого тестового метода в тестовом классе.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @after
*/
public function tearDownSomeFixtures()
{
// ...
}
/**
* @after
*/
public function tearDownSomeOtherFixtures()
{
// ...
}
}
@afterClass
Аннотацию @afterClass
можно использовать для указания
статических методов, которые должны вызываться после того, как все тестовые методы
в тестовом классе были запущены для очистки общих фикстур.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @afterClass
*/
public static function tearDownSomeSharedFixtures()
{
// ...
}
/**
* @afterClass
*/
public static function tearDownSomeOtherSharedFixtures()
{
// ...
}
}
@backupGlobals
Операции резервного копирования и восстановления глобальных переменных могут быть полностью отключены для всех тестов в тестовом классе следующим образом:
use PHPUnit\Framework\TestCase;
/**
* @backupGlobals disabled
*/
class MyTest extends TestCase
{
// ...
}
Аннотацию @backupGlobals
также можно использовать на уровне
тестового метода. Это позволяет выполнять тонкую настройку операций
резервного копирования и восстановления:
use PHPUnit\Framework\TestCase;
/**
* @backupGlobals disabled
*/
class MyTest extends TestCase
{
/**
* @backupGlobals enabled
*/
public function testThatInteractsWithGlobalVariables()
{
// ...
}
}
@backupStaticAttributes
Аннотацию @backupStaticAttributes
можно использовать для
резервного копирования всех значений статических свойств во всех объявленных классах перед
каждым тестом с последующим их восстановлением. Она может использоваться на уровне тестового класса
или тестового метода:
use PHPUnit\Framework\TestCase;
/**
* @backupStaticAttributes enabled
*/
class MyTest extends TestCase
{
/**
* @backupStaticAttributes disabled
*/
public function testThatInteractsWithStaticAttributes()
{
// ...
}
}
Примечание
Аннотация @backupStaticAttributes
ограничивается внутренне PHP
и при определённых условиях может привести
к непреднамеренному сохранению статических значений и утечке памяти
в последующих тестах.
См. Глобальное состояние дополнительной информации.
@before
Аннотацию @before
можно использовать для указания методов,
которые должны вызываться перед каждым тестовым методом в тестовом классе.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @before
*/
public function setupSomeFixtures()
{
// ...
}
/**
* @before
*/
public function setupSomeOtherFixtures()
{
// ...
}
}
@beforeClass
Аннотацию @beforeClass
можно использовать для указания
статических методов, которые должны вызываться до выполнения любых тестов в тестовом
классе для настройки общих фикстур.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @beforeClass
*/
public static function setUpSomeSharedFixtures()
{
// ...
}
/**
* @beforeClass
*/
public static function setUpSomeOtherSharedFixtures()
{
// ...
}
}
@codeCoverageIgnore*
Аннотации @codeCoverageIgnore
,
@codeCoverageIgnoreStart
и
@codeCoverageIgnoreEnd
могут использоваться
для исключения строк кода из анализа покрытия.
Для использования см. Игнорирование блоков кода.
@covers
Аннотация @covers
может использовать в тестовом коде для
указания, какие методы собирается тестировать данный тестовый метод:
/**
* @covers BankAccount::getBalance
*/
public function testBalanceIsInitiallyZero()
{
$this->assertSame(0, $this->ba->getBalance());
}
Если эта аннотация задана, будет учитываться информация о покрытии кода только для указанных методов.
appendixes.annotations.covers.tables.annotations
показывает
синтаксис аннотации @covers
.
@coversDefaultClass
Аннотацию @coversDefaultClass
можно использовать
для указания пространства имени по умолчанию или класса. Таким образом, длинные имена не нужно
повторно указывать для каждой аннотации @covers
. См.
Пример 2.19.
<?php
use PHPUnit\Framework\TestCase;
/**
* @coversDefaultClass \Foo\CoveredClass
*/
class CoversDefaultClassTest extends TestCase
{
/**
* @covers ::publicMethod
*/
public function testSomething()
{
$o = new Foo\CoveredClass;
$o->publicMethod();
}
}
@coversNothing
Аннотацию @coversNothing
можно использовать в тестовом
коде для указания, что информация о покрытии кода не должна учитываться
для данного тестового класса.
Это можно использовать для интеграционного тестирования. См. Тест, который указывает, что ни один метод не должен быть покрыт для примера.
Данную аннотацию можно использовать на уровне классе или метода и переопределить любые теги @covers
.
@dataProvider
Тестовый метод может принимать произвольное количество аргументов. Эти аргументы должны
быть предоставлены одним или несколькими методами провайдера данных (provider()
в
Использование провайдера данных, который возвращает массив массивов).
Используемый метод провайдера данных задаётся с помощью аннотации
@dataProvider
.
См. Провайдеры данных для получения подробной информации.
@depends
PHPUnit поддерживает объявление явных зависимостей между тестовыми
методами. Такие зависимости не определяют порядок, в котором должны выполняться тестовые методы,
но они позволяют возвращать экземпляр
фикстуры теста продюсером и передавать его зависимым потребителям.
Использование аннотации @depends для описания зависимостей показывает,
как использовать аннотацию @depends
для выражения
зависимостей между тестовыми методами.
См. Зависимости тестов для подробной информации.
@doesNotPerformAssertions
Предотвращает выполнение теста, не выполняющего никаких утверждений, для того чтобы не считать его рискованным.
@expectedException
Использование метода expectException()
показывает, как использовать аннотацию @expectedException
для проверки того, было ли выброшено исключение внутри тестируемого кода.
См. Тестирование исключений для получения подробной информации.
@expectedExceptionCode
Аннотация @expectedExceptionCode
в сочетании
с @expectedException
позволяет делать утверждения по
коду ошибке выбрасываемого исключения, таким образом, сужая конкретное исключение.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @expectedException MyException
* @expectedExceptionCode 20
*/
public function testExceptionHasErrorCode20()
{
throw new MyException('Сообщение исключения', 20);
}
}
Для облегчения тестирования и уменьшения дублирования можно указать
константу класса в
@expectedExceptionCode
, используя синтаксис
«@expectedExceptionCode ClassName::CONST
».
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @expectedException MyException
* @expectedExceptionCode MyClass::ERRORCODE
*/
public function testExceptionHasErrorCode20()
{
throw new MyException('Сообщение исключения', 20);
}
}
class MyClass
{
const ERRORCODE = 20;
}
@expectedExceptionMessage
Аннотация @expectedExceptionMessage
работает аналогично
@expectedExceptionCode
, поскольку она может сделать
утверждение на сообщении исключения.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @expectedException MyException
* @expectedExceptionMessage Сообщение исключения
*/
public function testExceptionHasRightMessage()
{
throw new MyException('Сообщение исключения', 20);
}
}
Ожидаемое сообщение может быть подстрокой сообщения исключения. Это может быть полезно, для того чтобы только утверждать, что переданное определённое имя или параметр встречается в исключении, не фокусируясь на полном совпадении сообщения исключения в тесте.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @expectedException MyException
* @expectedExceptionMessage broken
*/
public function testExceptionHasRightMessage()
{
$param = 'broken';
throw new MyException('Некорректный параметр "' . $param . '".', 20);
}
}
Для облегчения тестирования и уменьшения дублирования можно указать
константу класса в
@expectedExceptionMessage
, используя синтаксис
«@expectedExceptionMessage ClassName::CONST
».
Для примера можно посмотреть на @expectedExceptionCode.
@expectedExceptionMessageRegExp
Ожидаемое сообщение также можно указать в виде регулярного выражения, используя
аннотацию @expectedExceptionMessageRegExp
. Это
полезно в ситуациях, когда подстрока не подходит для соответствия
заданному сообщению.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @expectedException MyException
* @expectedExceptionMessageRegExp /Аргумент \d+ не может быть целым ? \w+/u
*/
public function testExceptionHasRightMessage()
{
throw new MyException('Аргумент 2 не может быть целым числом');
}
}
@group
Тест может быть отмечен как принадлежащий одной или нескольким группам, используя аннотацию
@group
следующим образом:
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @group specification
*/
public function testSomething()
{
}
/**
* @group regresssion
* @group bug2204
*/
public function testSomethingElse()
{
}
}
Аннотацию @group
можно задать для тестового
класса. Затем она будет «унаследована» всеми методами этого тестового класса.
Тесты могут быть выбраны для выполнения на основе групп с использованием
опций командной строки исполнителя тестов --group
и --exclude-group
или используя соответствующие директивы конфигурационного XML-файла.
@large
Аннотация @large
— псевдоним для @group large
.
Если пакет PHP_Invoker
установлен и включён
строгий режим, большой тест завершится неудачно, если для его выполнения
потребуется более 60 секунд. Этот тайм-аут настраивается через атрибут
timeoutForLargeTests
в конфигурационном XML-файле.
@medium
Аннотация @medium
— псевдоним для @group medium
. Средний тест не должен
зависеть от теста, отмеченного как @large
.
Если пакет PHP_Invoker
установлен и включён
строгий режим, средний тест завершится неудачно, если для его выполнения
потребуется более 10 секунд. Этот тайм-аут настраивается через атрибут
timeoutForMediumTests
в конфигурационном XML-файле.
@preserveGlobalState
Когда тест запускается в отдельном процессе, PHPUnit попытается
сохранить глобальное состояние из родительского процесса,
сериализуя все глобальные переменные в родительском процессе и десериализуя их
в дочернем процессе. Это может вызвать проблемы, если родительский процесс
содержит глобальные переменные, которые невозможно сериализовать.
Для исправления этого, вы можете запретить PHPUnit сохранять глобальное состояние с помощью аннотации
@preserveGlobalState
.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function testInSeparateProcess()
{
// ...
}
}
@requires
Аннотация @requires
можно использовать для пропуска тестов, когда общие
предварительные условия, такие как версия PHP или установленные расширения, не выполняются.
Полный список возможностей и примеров можно найти в Возможные примеры использования @requires
@runTestsInSeparateProcesses
Указывает, что все тесты в тестовом классе должны выполняться в отдельном процессе PHP.
use PHPUnit\Framework\TestCase;
/**
* @runTestsInSeparateProcesses
*/
class MyTest extends TestCase
{
// ...
}
Примечание: По умолчанию PHPUnit пытается сохранить глобальное состояние из родительского процесса, сериализуя все глобальные переменные в родительском процессе и десериализуя их в дочернем процессе. Это может вызвать проблемы, если родительский процесс содержит глобальные переменные, которые невозможно сериализовать. См. @preserveGlobalState для получения информации по изменению этого поведения.
@runInSeparateProcess
Указывает, что тест должен выполняться в отдельном процессе PHP.
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @runInSeparateProcess
*/
public function testInSeparateProcess()
{
// ...
}
}
Примечание: По умолчанию PHPUnit пытается сохранить глобальное состояние из родительского процесса, сериализуя все глобальные переменные в родительском процессе и десериализуя их в дочернем процессе. Это может вызвать проблемы, если родительский процесс содержит глобальные переменные, которые невозможно сериализовать. См. @preserveGlobalState для получения информации по изменению этого поведения.
@small
Аннотация @small
— это псевдоним для
@group small
. Небольшой тест не должен зависеть от теста,
отмеченного как @medium
или @large
.
Если пакет PHP_Invoker
установлен и включён
строгий режим, небольшой тест тест завершится неудачно, если для его выполнения
потребуется более 1 секунды. Этот тайм-аут настраивается через атрибут
timeoutForSmallTests
в конфигурационном XML-файле.
Примечание
Тесты должны быть явно аннотированы либо @small
,
@medium
или @large
для включения ограничения времени выполнения.
@test
В качестве альтернативы добавления префиксов именам тестовым методам
test
, вы можете использовать аннотацию @test
в блоке документации метода, чтобы отметить его как тестовый метод.
/**
* @test
*/
public function initialBalanceShouldBe0()
{
$this->assertSame(0, $this->ba->getBalance());
}
@testdox
Указывает альтернативное описание, используемое при создании предложений для agile-документации.
Аннотацию @testdox
можно применять как к тестовым классам, так и к тестовым методам.
/**
* @testdox A bank account
*/
class BankAccountTest extends TestCase
{
/**
* @testdox has an initial balance of zero
*/
public function balanceIsInitiallyZero()
{
$this->assertSame(0, $this->ba->getBalance());
}
}
Примечание
До PHPUnit 7.0 (из-за бага в разборе аннотации) использование
аннотации @testdox
также активировало поведение
аннотацию @test
.
@testWith
Вместо реализации метода для использования с @dataProvider
,
вы можете определить набор данных, используя аннотацию @testWith
.
Набор данных состоит из одного или нескольких элементов. Для определения набора данных с несколькими элементами, определите каждый элемент на отдельной строке. Каждый элемент набора данных должен быть массив, определённым в JSON.
См. Провайдеры данных для получения дополнительной информации о передачи набора данных в тест.
/**
* @param string $input
* @param int $expectedLength
*
* @testWith ["test", 4]
* ["longer-string", 13]
*/
public function testStringLength(string $input, int $expectedLength)
{
$this->assertSame($expectedLength, strlen($input));
}
Представление объекта в JSON будет преобразовано в ассоциативный массив.
/**
* @param array $array
* @param array $keys
*
* @testWith [{"day": "monday", "conditions": "sunny"}, ["day", "conditions"]]
*/
public function testArrayKeys($array, $keys)
{
$this->assertSame($keys, array_keys($array));
}
@ticket
Аннотация @ticket
— это псевдоним для аннотации @group
(см. @group) и позволяет фильтровать тесты на основе
их идентификатора тикета.
@uses
Аннотация @uses
указывает код, который будет
выполняться тестом, но не предназначен для покрытия тестом. Хорошим
примером может быть объект значения (value object), который необходим для тестирования единицы (модуля) кода.
/**
* @covers BankAccount::deposit
* @uses Money
*/
public function testMoneyCanBeDepositedInAccount()
{
// ...
}
Эта аннотация особенно полезна в режиме строгого режима, когда непреднамеренно покрытый код приводит тесте к неудаче. См. Непреднамеренно покрытый код для получения дополнительной информации о строгом режиме покрытия.