Расширение PHPUnit
Улучшение конкретных тестовых случаев
Вы можете расширить PHPUnit, улучшая конкретные тестовые случаи методами, которые добавляют функциональность.
Например, вы можете использовать утверждение в конкретном тестовом случае для проверки того, что значение, созданное тестируемой системой, соответствует регулярному выражению.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class OrderIdGeneratorTest extends TestCase
{
public function testGenerateGeneratesId(): void
{
$orderIdGenerator = new OrderIdGenerator;
$orderId = $orderIdGenerator->generate();
$this->assertMatchesRegularExpression(
'/^[a-f0-9]{8}-[a-f0-9]{4}$/',
sprintf(
'Failed asserting that "%s" is a valid order ID.',
$orderId,
),
);
}
}
Вы можете улучшить этот конкретный тестовый случай, выделив утверждение, специфичное для домена.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class OrderIdGeneratorWithDomainSpecificAssertionTest extends TestCase
{
public function testGenerateGeneratesId(): void
{
$orderIdGenerator = new OrderIdGenerator;
$orderId = $orderIdGenerator->generate();
$this->assertStringIsOrderId($orderId);
}
private function assertStringIsOrderId(string $value): void
{
$this->assertMatchesRegularExpression(
'/^[a-f0-9]{8}-[a-f0-9]{4}$/',
$value,
sprintf(
'Failed asserting that "%s" is a valid order ID.',
$value,
),
);
}
}
Извлечение абстрактных тестовых случаев
Вы можете расширить PHPUnit, извлекая абстрактные тестовые случаи для совместного использования функциональности с другими конкретными тестовыми случаями с помощью вертикального наследования.
Например, вы можете перенести специфичное для домена утверждение из вышеприведенного примера в абстрактный тестовый случай.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
abstract class AbstractTestCase extends TestCase
{
final protected function assertStringIsOrderId(string $value): void
{
$this->assertMatchesRegularExpression(
'/^[a-f0-9]{8}-[a-f0-9]{4}$/',
$value,
sprintf(
'Failed asserting that "%s" is a valid order ID.',
$value,
),
);
}
}
Затем вы можете улучшить конкретный тестовый случай, расширив абстрактный тестовый случай.
<?php declare(strict_types=1);
final class OrderIdGeneratorExtendingAbstractTestCaseTest extends AbstractTestCase
{
public function testGenerateGeneratesId(): void
{
$orderIdGenerator = new OrderIdGenerator;
$orderId = $orderIdGenerator->generate();
$this->assertStringIsOrderId($orderId);
}
}
Извлечение трейтов
Вы можете расширить PHPUnit, извлекая трейты для совместного использования функциональности с конкретными тестовыми случаями с помощью горизонтального наследования.
Например, вы можете перенести специфичное для домена утверждение из вышеприведенного примера в трейт.
<?php declare(strict_types=1);
trait AssertionTrait
{
final protected function assertStringIsOrderId(string $value): void
{
$this->assertMatchesRegularExpression(
'/^[a-f0-9]{8}-[a-f0-9]{4}$/',
$value,
sprintf(
'Failed asserting that "%s" is a valid order ID.',
$value,
),
);
}
}
Затем вы можете улучшить конкретный тестовый случай, используя трейт.
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class OrderIdGeneratorUsingAssertionTraitTest extends TestCase
{
use AssertionTrait;
public function testGenerateGeneratesId(): void
{
$orderIdGenerator = new OrderIdGenerator;
$orderId = $orderIdGenerator->generate();
$this->assertStringIsOrderId($orderId);
}
}
Расширение тестового запуска
Вы можете расширить PHPUnit, реализовав и зарегистрировав расширение.
Реализация расширения
Расширение PHPUnit — это класс, реализующий интерфейс PHPUnit\Runner\Extension\Extension.
Интерфейс расширения объявляет метод bootstrap(), принимающий конфигурацию PHPUnit, фасад расширения и коллекцию параметров расширения.
<?php declare(strict_types=1);
namespace Vendor\ExampleExtensionForPhpunit;
use PHPUnit\Runner\Extension\Extension;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
use PHPUnit\TextUI\Configuration\Configuration;
final class ExampleExtension implements Extension
{
public function bootstrap(
Configuration $configuration,
Facade $facade,
ParameterCollection $parameters
): void {
if ($configuration->noOutput()) {
return;
}
$message = 'the-default-message';
if ($parameters->has('message')) {
$message = $parameters->get('message');
}
$facade->registerSubscriber(new ExampleSubscriber($message));
$facade->registerTracer(new ExampleTracer);
}
}
Конфигурация PHPUnit — это экземпляр PHPUnit\TextUI\Configuration\Configuration, предоставляющий доступ к конфигурации PHPUnit после слияния параметров из значений по умолчанию, файла конфигурации XML и командных параметров.
Вы можете проверить объект конфигурации, чтобы настроить поведение вашего расширения. Например, вы можете расширить PHPUnit расширением, которое отображает вывод на консоли. В этом случае вас может заинтересовать, хочет ли пользователь PHPUnit использовать цвета или предпочитает монохромный вывод.
Коллекция параметров — это экземпляр PHPUnit\Runner\Extension\ParameterCollection, предоставляющий доступ к параметрам расширения, которые пользователь предоставил через файл конфигурации XML PHPUnit. Вы можете использовать коллекцию параметров, чтобы пользователи расширения могли настроить поведение вашего расширения.
Примечание
Вы должны самостоятельно проверить и обработать значения из коллекции параметров. PHPUnit не имеет функций для проверки или преобразования значений из коллекции параметров в другие типы.
Фасад расширения — это экземпляр PHPUnit\Runner\Extension\Facade, позволяющий регистрировать подписчиков на события и трассировщики событий с помощью методов registerSubscribers(), registerSubscriber() и registerTracer().
Фасад расширения также предоставляет следующие методы для расширений тестового запуска, чтобы указать тестовому запуску, что они намерены заменить стандартное поведение или требуют активации определенного функционала:
Метод replacesProgressOutput() может использоваться для отключения стандартного вывода прогресса тестового запуска при выполнении тестов.
Метод replacesResultOutput() может использоваться для отключения стандартного вывода результатов тестового запуска после завершения выполнения тестов.
Метод replacesOutput() объединяет эффекты методов replacesProgressOutput() и replacesResultOutput() (см. выше).
Метод requiresCodeCoverageCollection() может использоваться для активации сбора информации о покрытии кода.
Метод requiresExportOfObjects() может использоваться для активации экспорта объектов для событий, таких как Test\AssertionSucceeded и Test\AssertionFailed, например.
Реализация подписчика на события
Подписчик на события — это класс, реализующий интерфейс подписчика на события.
Интерфейс подписчика на события объявляет единственный метод notify(), принимающий экземпляр соответствующего класса события.
<?php declare(strict_types=1);
namespace Vendor\ExampleExtensionForPhpunit;
use PHPUnit\Event\TestRunner\ExecutionFinished;
use PHPUnit\Event\TestRunner\ExecutionFinishedSubscriber;
final class ExampleSubscriber implements ExecutionFinishedSubscriber
{
public function __construct(private readonly string $message)
{
}
public function notify(ExecutionFinished $event): void
{
print __METHOD__ . PHP_EOL . $this->message . PHP_EOL;
}
}
После регистрации подписчика на события с помощью фасада расширения PHPUnit уведомит подписчика при отправке события соответствующего класса события.
Примечание
Вы не можете создать подписчика на события, реализующего более одного интерфейса подписчика на события одновременно.
Если вы хотите подписаться на несколько событий, вам нужно реализовать как минимум по одному подписчику на события для каждого события, которое вас интересует.
Реализация трассировщика событий
Трассировщик событий — это класс, реализующий интерфейс PHPUnit\Event\Tracer\Tracer.
Интерфейс трассировщика объявляет единственный метод trace(), принимающий событие.
<?php declare(strict_types=1);
namespace Vendor\ExampleExtensionForPhpunit;
use PHPUnit\Event\Event;
use PHPUnit\Event\Tracer\Tracer;
final class ExampleTracer implements Tracer
{
public function trace(Event $event): void
{
// ...
}
}
После регистрации трассировщика событий с помощью фасада расширения PHPUnit уведомит трассировщик о каждом событии.
Подсказка
Вы не уверены, нужно ли вам реализовать трассировщик событий или нескольких подписчиков на события?
Если вас интересуют все события, которые PHPUnit отправляет во время выполнения CLI-приложения, вам, вероятно, следует реализовать и зарегистрировать трассировщик событий.
Если вас интересуют определённые события, которые PHPUnit отправляет во время выполнения CLI-приложения, вам, вероятно, нужно реализовать и зарегистрировать одного или нескольких подписчиков на события.
Понимание событий
Событие — это класс, реализующий интерфейс PHPUnit\Event\Event.
Интерфейс PHPUnit\Event\Event объявляет метод telemetryInfo(), который предоставляет доступ к телеметрической информации, и метод asString(), который возвращает строковое представление события.
Каждый тип события может реализовывать дополнительные методы, которые предоставляют доступ к информации, доступной, когда PHPUnit регистрирует и отправляет событие.
Вы можете использовать, проверять и обрабатывать эти события в подписчиках на события или трассировщиках.
Список всех событий, которые в настоящее время отправляет PHPUnit, вы найдете в приложении.
Примечание
PHPUnit в настоящее время не поддерживает регистрацию пользовательских событий.
Регистрация расширения
Вы можете зарегистрировать одно или несколько расширений PHPUnit из PHAR или из пакета Composer, используя элементы extensions, bootstrap и параметры файла конфигурации PHPUnit в формате XML Файл конфигурации PHPUnit.
Регистрация расширения из PHAR
При установке PHPUnit в виде PHAR лучше загружать расширения из PHAR.
Вы можете использовать атрибут extensionsDirectory элемента phpunit, чтобы настроить директорию, из которой PHPUnit должен загружать расширения в формате PHAR.
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
extensionsDirectory="../phpunit-extensions/"
>
<!-- ... -->
<extensions>
<bootstrapclass="Vendor\ExampleExtensionForPhpunit\ExampleExtension">
<parametername="message"value="the-message"/>
</bootstrap>
</extensions>
<!-- ... -->
</phpunit>
Регистрация расширения из пакета Composer
При установке PHPUnit как пакета Composer лучше загружать расширения из пакетов Composer.
Вам не нужно настраивать атрибут extensionsDirectory, так как расширения из пакетов Composer будут доступны через механизм автозагрузки Composer.
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
>
<!-- ... -->
<extensions>
<bootstrapclass="Vendor\ExampleExtensionForPhpunit\ExampleExtension">
<parametername="message"value="the-message"/>
</bootstrap>
</extensions>
<!-- ... -->
</phpunit>
Отладка PHPUnit
Опция командной строки --log-events-text запускаемого модуля PHPUnit позволяет выводить текстовое представление каждого события в поток. В примере ниже мы используем --no-output для отключения как стандартного вывода о прогрессе, так и стандартного вывода результатов. Затем мы используем --log-events-text php://stdout для записи информации о событиях в стандартный вывод:
phpunit --no-output --log-events-text php://stdout
PHPUnit Started (PHPUnit 10.0.0 using PHP 8.2.1 (cli) on Linux)
Test Runner Configured
Test Suite Loaded (2 tests)
Event Facade Sealed
Test Runner Started
Test Suite Sorted
Test Runner Execution Started (2 tests)
Test Suite Started (ExampleTest, 2 tests)
Test Preparation Started (ExampleTest::testOne)
Test Prepared (ExampleTest::testOne)
Assertion Succeeded (Constraint: is true)
Test Passed (ExampleTest::testOne)
Test Finished (ExampleTest::testOne)
Test Preparation Started (ExampleTest::testTwo)
Test Prepared (ExampleTest::testTwo)
Assertion Failed (Constraint: is identical to 'foo', Value: 'bar')
Test Failed (ExampleTest::testTwo)
Failed asserting that two strings are identical.
Test Finished (ExampleTest::testTwo)
Test Suite Finished (ExampleTest, 2 tests)
Test Runner Execution Finished
Test Runner Finished
PHPUnit Finished (Shell Exit Code: 1)
В качестве альтернативы, опция командной строки --log-events-verbose-text может быть использована для включения информации о потреблении ресурсов (время с момента запуска запускателя тестов, время с момента предыдущего события и использование памяти):
phpunit --no-output --log-events-verbose-text php://stdout
[00:00:00.000046482 / 00:00:00.000006987] [4194304 bytes] PHPUnit Started (PHPUnit 10.0.0 using PHP 8.2.1 (cli) on Linux)
[00:00:00.048195557 / 00:00:00.048149075] [4194304 bytes] Test Runner Configured
[00:00:00.067646038 / 00:00:00.019450481] [6291456 bytes] Test Suite Loaded (2 tests)
[00:00:00.075942220 / 00:00:00.008296182] [6291456 bytes] Event Facade Sealed
[00:00:00.076452360 / 00:00:00.000510140] [6291456 bytes] Test Runner Started
[00:00:00.084421682 / 00:00:00.007969322] [6291456 bytes] Test Suite Sorted
[00:00:00.084664485 / 00:00:00.000242803] [6291456 bytes] Test Runner Execution Started (2 tests)
[00:00:00.085240320 / 00:00:00.000575835] [6291456 bytes] Test Suite Started (ExampleTest, 2 tests)
[00:00:00.086992385 / 00:00:00.001752065] [6291456 bytes] Test Preparation Started (ExampleTest::testOne)
[00:00:00.087443560 / 00:00:00.000451175] [6291456 bytes] Test Prepared (ExampleTest::testOne)
[00:00:00.088237489 / 00:00:00.000793929] [6291456 bytes] Assertion Succeeded (Constraint: is true)
[00:00:00.089076305 / 00:00:00.000838816] [6291456 bytes] Test Passed (ExampleTest::testOne)
[00:00:00.091027624 / 00:00:00.001951319] [6291456 bytes] Test Finished (ExampleTest::testOne)
[00:00:00.091110095 / 00:00:00.000082471] [6291456 bytes] Test Preparation Started (ExampleTest::testTwo)
[00:00:00.091158739 / 00:00:00.000048644] [6291456 bytes] Test Prepared (ExampleTest::testTwo)
[00:00:00.091991799 / 00:00:00.000833060] [6291456 bytes] Assertion Failed (Constraint: is identical to 'foo', Value: 'bar')
[00:00:00.099242925 / 00:00:00.007251126] [8388608 bytes] Test Failed (ExampleTest::testTwo)
Failed asserting that two strings are identical.
[00:00:00.099386498 / 00:00:00.000143573] [8388608 bytes] Test Finished (ExampleTest::testTwo)
[00:00:00.099437634 / 00:00:00.000051136] [8388608 bytes] Test Suite Finished (ExampleTest, 2 tests)
[00:00:00.103014760 / 00:00:00.003577126] [8388608 bytes] Test Runner Execution Finished
[00:00:00.103207309 / 00:00:00.000192549] [8388608 bytes] Test Runner Finished
[00:00:00.105879902 / 00:00:00.002672593] [8388608 bytes] PHPUnit Finished (Shell Exit Code: 1)
Обертывание запускателя тестов
Класс PHPUnit\TextUI\Application является точкой входа для собственного запускателя тестов командной строки PHPUnit. Он не предназначен для повторного использования разработчиками, которые хотят обернуть PHPUnit для создания чего-то вроде ParaTest.
Для фактического запуска тестов PHPUnit\TextUI\Application использует PHPUnit\TextUI\TestRunner::run().
PHPUnit\TextUI\TestRunner::run() требует PHPUnit\TextUI\Configuration\Configuration, PHPUnit\Runner\ResultCache\ResultCache и PHPUnit\Framework\TestSuite.
PHPUnit\TextUI\Configuration\Configuration может быть создан с помощью PHPUnit\TextUI\Configuration\Builder::build(). Вам необходимо передать $_SERVER['argv'] в этот метод. Затем метод анализирует аргументы/опции командной строки и загружает файл конфигурации XML, если он может быть загружен.
PHPUnit\Framework\TestSuite может быть создан из PHPUnit\TextUI\Configuration\Configuration с помощью PHPUnit\TextUI\Configuration\TestSuiteBuilder::build().
Несмотря на то, что он помечен как @internal, PHPUnit\TextUI\TestRunner предназначен для повторного использования разработчиками, которые хотят обернуть запускатель тестов PHPUnit.
© 2005–2025 Sebastian Bergmann
Licensed under the Creative Commons Attribution 3.0 Unported License.
https://docs.phpunit.de/en/12.0/extending-phpunit.html