Зачем нужны тесты и какие виды тестов должен писать разработчик вы можете узнать здесь.
Модульные тесты главным образом пишутся в качестве хорошей практики, помогающей разработчикам выявлять и исправлять баги, проводить рефакторинг кода и служить в качестве документации для тестируемого программного модуля (программы). Для достижения этих преимуществ модульные тесты в идеале должны охватывать все возможные пути исполнения программы. Один модульный тест обычно покрывает один конкретный путь в одной функции или методе. Однако тестовые методы необязательно должны быть инкапсулированными и независимыми. Часто существуют неявные зависимости между тестовыми методами, скрытые в сценарии реализации теста.
Адриан Кун (Adrian Kuhn) и другие:
Установка PHPUnit и написание первый тест
Для начала нужно установить версию php7.2+ и composer. Затем в проекте устанавливаем phpunit:
composer require --dev phpunit/phpunit
Создаем пример для теста Duck.php:
<?php
class Duck {
public function say(): string {
return 'krya-krya';
}
}
Создаем папку tests и добавляем туда тестируем класс DuckTest.php (по умолчанию PHPUnit смотрит на все файлы в которые заканчиваются на Test.php).
<?php
require_once __DIR__ . '/../Duck.php';
class Krya_Test extends PHPUnitFrameworkTestCase {
public function test_say() {
$krya = new Duck();
$this->assertSame( 'krya-krya', $krya->say() );
}
}
И запускаем тесты:
vendor/bin/phpunit tests/
Если вы увидели такое сообщение, то все сделано верно.
PHPUnit 9.0.1 by Sebastian Bergmann and contributors.
. 1 / 1 (100%)
Time: 42 ms, Memory: 4.00 MB
OK (1 test, 1 assertion)
Базовые вещи, которые нужно знать:
Фикстуры (Fixture)
Фиксутра — настройка окружения и возврат его в исходное состояние. Звучит страшно, но на самом деле это просто методы, которые позволяют настроить окружение для теста. В классе PHPUnitFrameworkTestCase для этого есть методы:
- setUp выполняются перед каждым тестом тестового класса;
- tearDown выполняются после каждого теста тестового класса;
- setUpBeforeClass — выполняется перед первым тестом в тестовом классе;
- tearDownAfterClass — выполняется после запуска последнего теста тестового класса.
Пример:
<?php
class FixtureTest extends PHPUnitFrameworkTestCase {
private $counter;
protected function setUp(): void {
$this->counter = 0;
parent::setUp();
}
public function test_fixture_1() {
$this->counter++;
$this->assertSame( 1, $this->counter );
}
public function test_fixture_2() {
$this->assertSame( 0, $this->counter );
}
}
Получается в каждом тестируемом методе свойство counter будет равно 0.
Утверждение (Asserts)
Утверждения — это методы класса TestCase, которые помогают проверить тест. Все эти методы начинаются с assert (assertTrue, assertSame, assertClassHasAttribute и т.д.). После выполнения тестов будет показано кол-во выполненных тестов и кол-во выполненных утверждений. В случае неверного утверждения тест считается проваленным.
Тестовые двойники (Заглушки, Stubs, Mocks)
Stubs — объект, поведение которого мы описывает в тесте, но по какой-то причине не можем его вызвать через new. Например в момент тестирования абстрактного класс SomeClass:
<?php
use PHPUnitFrameworkTestCase;
class StubTest extends TestCase
{
public function testStub()
{
$stub = $this->createMock(SomeClass::class);
$stub->method('doSomething')
->willReturn('foo');
$this->assertSame('foo', $stub->doSomething());
}
}
В тесте мы создали объект SomeClass и присвоили ему метод doSomething, который при вызове вернет строку «foo».
Mocks — объект, который мы хотим использовать в тестируемом объекте. Например у нас есть класс Duck в конструктор которого должен попасть объект Headdress:
<?php
use PHPUnitFrameworkTestCase;
class MockTest extends TestCase
{
public function testStub()
{
$headdress = $this->createMock(Headdress::class);
$duck = new Duck( $headdress );
$this->assertSame('krya-krya', $duck->say() );
}
}
Настройка окружения для всех тестов (Bootstrap)
Для настройки окружения для всех тестов можно создать файл bootstrap.php и объявить там необходимые вещи. Например в этом файле мы можем подключить библиотеки необходимые для тестирования, изменить глобальные и супер-переменные, объявить константы и т.д.
Пример bootstrap.php файла:
define( 'PLUGIN_PATH', __DIR__ '/../' );
require_once PLUGIN_PATH . '/vendor/autoload.php';
Конфигурационный XML-файл
Мы можем настроить выполнение команд с помощью конфигурационного XML-файла, в котором можно изменить стандартные настройки phpunit, указать папку с тестами, путь к бутстап файлу, настроить фильтры и другое.
Пример XML-файла:
<phpunit
bootstrap="./bootstrap.php"
colors="true"
>
<testsuites>
<testsuite name="Config-example-for-tests">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">../../</directory>
<exclude>
<directory>../../src</directory>
<directory>../../tests</directory>
<directory>../../vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
Атрибутами или вложенными тегами к <phpunit> могут быть любые команды, которые есть в phpunit. В нашем случае
vendor/bin/phpunit /tests/ --bootsrap /tests/bootsrap.php --colors --test-suffix=".php" ...
Меняется на:
vendor/bin/phpunit --configuration phpunit.xml
Покрытие тестами (Tests coverage)
С помощью этого инструмента очень легко понять насколько качественно написаны тесты, сколько файлов покрыты тестами и какие строки в них покрыты. Данный инструмент очень сильно помогает отслеживать качество ваших тестов.
Важно!!! Необходимо к вашему php-cli подключить xdebug иначе coverage будет недоступен и вы получите уведомление об отсутствии модуля для тестирования.
Чтобы его использовать достаточно дописать к phpunit —coverage-{type} атрибут. Например очень удобно использовать HTML-формат покрытия:
vendor/bin/phpunit --configuration phpunit.xml --coverage-html coverage
После этого в проекте создается папка coverage и мы можем открыть файл index.html и просмотреть подробную информацию о каждом файле тестирования.