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.

Пример 2.19 Использование @coversDefaultClass для сокращений аннотаций
<?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()
{
    // ...
}

Эта аннотация особенно полезна в режиме строгого режима, когда непреднамеренно покрытый код приводит тесте к неудаче. См. Непреднамеренно покрытый код для получения дополнительной информации о строгом режиме покрытия.