Начало работы с Nette Tester
****************************
<div class=perex>
Ошибаются и хорошие программисты. Разница между хорошим и плохим программистом в том, что хороший ошибётся один раз, а в следующий раз обнаружит ошибку автоматическими тестами.
- "Кто не тестирует, обречён повторять свои ошибки." (поговорка)
- "Как только мы избавимся от одной ошибки, появится другая." (закон Мёрфи)
- "Всякий раз, когда вам хочется написать print, напишите вместо этого тест." (Мартин Фаулер)
</div>
Вы когда-нибудь писали в PHP такой код?
```php
$obj = new MyClass;
$result = $obj->process($input);
var_dump($result);
```
То есть выводили результат вызова функции только для того, чтобы глазами проверить, возвращает ли она то, что должна? Наверняка вы делаете так по многу раз в день. Положа руку на сердце: если всё работает правильно, вы этот код удаляете? Ожидаете, что класс в будущем не сломается? Законы Мёрфи гарантируют обратное :-)
По сути, вы написали тест. Его достаточно немного изменить, чтобы он не требовал проверки глазами, а проверял себя сам. И если вы тест не удалите, то в будущем сможете в любой момент запустить его и убедиться, что всё по-прежнему работает как надо. Со временем таких тестов у вас накопится много, так что было бы полезно запускать их автоматически.
И со всем этим вам поможет Nette Tester.
Чем Tester уникален?
====================
Написание тестов для Nette Tester уникально тем, что **каждый тест - обычный PHP-скрипт**, который можно запустить самостоятельно.
То есть, когда вы напишете тест, вы можете просто запустить его и выяснить, нет ли в нём, например, программистской ошибки. Работает ли он правильно. Если нет, вы легко пройдёте по нему шагами в своей IDE и найдёте ошибку. Вы можете даже открыть его в браузере.
И самое главное: запуская его, вы выполняете тест. Вы сразу узнаёте, прошёл он или провалился. Как? Покажем. Напишем тривиальный тест работы с массивом PHP и сохраним его в файл `ArrayTest.php`:
```php .{file:ArrayTest.php}
<?php
use Tester\Assert;
require __DIR__ . '/vendor/autoload.php'; # загружаем автозагрузчик Composer
Tester\Environment::setup(); # инициализируем Nette Tester
$stack = [];
Assert::same(0, count($stack)); # ожидаем, что count() вернёт ноль
$stack[] = 'foo';
Assert::same(1, count($stack)); # ожидаем, что count() вернёт единицу
Assert::contains('foo', $stack); # проверяем, что $stack содержит элемент 'foo'
```
Как видите, для подтверждения того, что фактическое значение соответствует ожидаемому, используются так называемые [методы утверждений|assertions] вроде `Assert::same()`.
Тест написан, и мы можем запустить его из командной строки. Первый запуск выявит возможные синтаксические ошибки, и если вы нигде не сделали опечатку, он выведет:
/--pre .[terminal]
$ php ArrayTest.php
<span style="color:#FFF; background-color:#090">OK</span>
\--
Попробуйте изменить утверждение в тесте на ложное, например `Assert::contains('XXX', $stack);`, и посмотрите, что произойдёт при запуске:
/--pre .[terminal]
$ php ArrayTest.php
<span style="color: #FFF">Failed: </span><span style="color: #FF0">['foo']</span><span style="color: #FFF"> should contain </span><span style="color: #FF0">'XXX'</span>
<span style="color: #CCC">in </span><span style="color: #FFF">ArrayTest.php(17)</span><span style="color: #808080"> Assert::contains('XXX', $stack);</span>
<span style="color: #FFF; background-color: #900">FAILURE</span>
\--
О написании тестов мы продолжаем в главе [Написание тестов |writing-tests].
Установка и требования
======================
Минимальная версия PHP, которую требует Tester, - 8.0 (подробности в таблице [#Поддерживаемые версии PHP]). Предпочтительный способ установки - через [Composer |best-practices:composer]:
/--pre .[terminal]
composer require --dev nette/tester
\--
Попробуйте запустить Nette Tester из командной строки (без аргументов он выведет только справку):
/--pre .[terminal]
vendor/bin/tester
\--
Запуск тестов
=============
По мере роста приложения растёт и количество тестов. Запускать тесты по одному было бы непрактично. Поэтому у Tester есть массовый запускатель тестов, который мы вызываем из командной строки. Параметром мы указываем каталог, в котором находятся тесты. Точка означает текущий каталог.
/--pre .[terminal]
vendor/bin/tester .
\--
Запускатель тестов обходит указанный каталог и все подкаталоги и ищет тесты, то есть файлы `*.phpt` и `*Test.php`. Он найдёт и наш тест `ArrayTest.php`, потому что тот подходит под маску.
Затем он начинает тестирование. Каждый тест запускается как новый процесс PHP, поэтому выполняется полностью изолированно от остальных. Запускает он их параллельно в нескольких потоках, благодаря чему работает чрезвычайно быстро. И сначала запускает тесты, которые провалились при предыдущем запуске, так что вы сразу узнаёте, удалось ли вам исправить ошибку.
Во время выполнения тестов Tester непрерывно выводит результаты в терминал символами:
- <code style="color: #CCC; background-color: #000">.</code> - тест прошёл
- <code style="color: #CCC; background-color: #000">s</code> - тест был пропущен
- <code style="color: #FFF; background-color: #900">F</code> - тест провалился
Вывод может выглядеть так:
/--pre .[terminal]
_____ ___ ___ _____ ___ ___
|_ _/ __)( __/_ _/ __)| _ )
|_| \___ /___) |_| \___ |_|_\ v2.6.0
PHP 8.5.2 (cli) | php | 8 threads
........s................<span style="color: #FFF; background-color: #900">F</span>.........
<span style="color: #F00">-- FAILED: greeting.phpt</span>
<span style="color: #CCC"> </span><span style="color: #FFF">Failed: </span><span style="color: #FF0">'Hello John'</span><span style="color: #FFF"> should be</span>
<span style="color: #FFF"> ... </span><span style="color: #FF0">'Hello Peter'</span>
<span style="color: #CCC"> in </span><span style="color: #FFF">greeting.phpt(19)</span><span style="color: #808080"> Assert::same('Hello Peter', $o->say('John'));</span>
<span style="color: #FFF; background-color: #900">FAILURES! (35 tests, 1 failures, 1 skipped, 1.7 seconds)</span>
\--
Было запущено 35 тестов, один провалился, один был пропущен.
Мы продолжаем в главе [Запуск тестов |running-tests].
Режим наблюдения
================
Занимаетесь рефакторингом кода? Или, может, даже разрабатываете по методике TDD (Test Driven Development)? Тогда вам понравится режим наблюдения. В этом режиме Tester следит за исходными кодами и при их изменении запускается автоматически.
При разработке у вас в углу монитора терминал, в котором на вас светит зелёная полоса состояния, и когда она вдруг сменится красной, вы знаете, что только что сделали что-то не совсем так. По сути, это отличная игра, в которой вы программируете и стараетесь удержать цвет.
Режим наблюдения запускается параметром [`--watch` |running-tests#-w --watch path].
Отчёты о покрытии кода
======================
Tester умеет порождать отчёты с обзором того, какую часть исходного кода покрывают тесты. Отчёт может быть либо в понятном человеку формате HTML, либо в Clover XML для дальнейшей машинной обработки.
Посмотрите "пример HTML-отчёта":attachment:coverage.html с покрытием кода.
Поддерживаемые версии PHP
=========================
| Версия | Совместима с PHP
|------------------|-------------------
| Tester 2.6 | PHP 8.0 - 8.5
| Tester 2.5 | PHP 8.0 - 8.5
| Tester 2.4 | PHP 7.2 - 8.2
| Tester 2.3 | PHP 7.1 - 8.0
| Tester 2.1 - 2.2 | PHP 7.1 - 7.3
| Tester 2.0 | PHP 5.6 - 7.3
| Tester 1.7 | PHP 5.3 - 7.3 + HHVM 3.3+
| Tester 1.6 | PHP 5.3 - 7.0 + HHVM
| Tester 1.3 - 1.5 | PHP 5.3 - 5.6 + HHVM
| Tester 0.9 - 1.2 | PHP 5.3 - 5.6
Относится к последней патч-версии.
Tester вплоть до версии 1.7 поддерживал и [HHVM |https://hhvm.com] 3.3.0 или новее (через `tester -p hhvm`). Начиная с версии Tester 2.0 поддержка прекращена.
Начало работы с Nette Tester
Ошибаются и хорошие программисты. Разница между хорошим и плохим
программистом в том, что хороший ошибётся один раз, а в следующий раз
обнаружит ошибку автоматическими тестами.
- “Кто не тестирует, обречён повторять свои ошибки.” (поговорка)
- “Как только мы избавимся от одной ошибки, появится другая.”
(закон Мёрфи)
- “Всякий раз, когда вам хочется написать print, напишите вместо этого
тест.” (Мартин Фаулер)
Вы когда-нибудь писали в PHP такой код?
$obj = new MyClass;
$result = $obj->process($input);
var_dump($result);
То есть выводили результат вызова функции только для того, чтобы
глазами проверить, возвращает ли она то, что должна? Наверняка вы
делаете так по многу раз в день. Положа руку на сердце: если всё
работает правильно, вы этот код удаляете? Ожидаете, что класс в будущем
не сломается? Законы Мёрфи гарантируют обратное :-)
По сути, вы написали тест. Его достаточно немного изменить, чтобы он
не требовал проверки глазами, а проверял себя сам. И если вы тест не
удалите, то в будущем сможете в любой момент запустить его и убедиться,
что всё по-прежнему работает как надо. Со временем таких тестов у вас
накопится много, так что было бы полезно запускать их автоматически.
И со всем этим вам поможет Nette Tester.
Чем Tester уникален?
Написание тестов для Nette Tester уникально тем, что каждый тест –
обычный PHP-скрипт, который можно запустить самостоятельно.
То есть, когда вы напишете тест, вы можете просто запустить его и
выяснить, нет ли в нём, например, программистской ошибки. Работает ли он
правильно. Если нет, вы легко пройдёте по нему шагами в своей IDE и
найдёте ошибку. Вы можете даже открыть его в браузере.
И самое главное: запуская его, вы выполняете тест. Вы сразу узнаёте,
прошёл он или провалился. Как? Покажем. Напишем тривиальный тест работы
с массивом PHP и сохраним его в файл ArrayTest.php:
<?php
use Tester\Assert;
require __DIR__ . '/vendor/autoload.php'; # загружаем автозагрузчик Composer
Tester\Environment::setup(); # инициализируем Nette Tester
$stack = [];
Assert::same(0, count($stack)); # ожидаем, что count() вернёт ноль
$stack[] = 'foo';
Assert::same(1, count($stack)); # ожидаем, что count() вернёт единицу
Assert::contains('foo', $stack); # проверяем, что $stack содержит элемент 'foo'
Как видите, для подтверждения того, что фактическое значение
соответствует ожидаемому, используются так называемые методы утверждений вроде Assert::same().
Тест написан, и мы можем запустить его из командной строки. Первый
запуск выявит возможные синтаксические ошибки, и если вы нигде не
сделали опечатку, он выведет:
$ php ArrayTest.php
OK
Попробуйте изменить утверждение в тесте на ложное, например
Assert::contains('XXX', $stack);, и посмотрите, что произойдёт при запуске:
$ php ArrayTest.php
Failed: ['foo'] should contain 'XXX'
in ArrayTest.php(17) Assert::contains('XXX', $stack);
FAILURE
О написании тестов мы продолжаем в главе Написание тестов.
Установка и требования
Минимальная версия PHP, которую требует Tester, – 8.0 (подробности в
таблице Поддерживаемые версии PHP).
Предпочтительный способ установки – через Composer:
composer require --dev nette/tester
Попробуйте запустить Nette Tester из командной строки (без аргументов он
выведет только справку):
vendor/bin/tester
Запуск тестов
По мере роста приложения растёт и количество тестов. Запускать тесты
по одному было бы непрактично. Поэтому у Tester есть массовый запускатель
тестов, который мы вызываем из командной строки. Параметром мы
указываем каталог, в котором находятся тесты. Точка означает текущий
каталог.
vendor/bin/tester .
Запускатель тестов обходит указанный каталог и все подкаталоги и
ищет тесты, то есть файлы *.phpt и *Test.php. Он найдёт и наш тест
ArrayTest.php, потому что тот подходит под маску.
Затем он начинает тестирование. Каждый тест запускается как новый
процесс PHP, поэтому выполняется полностью изолированно от остальных.
Запускает он их параллельно в нескольких потоках, благодаря чему
работает чрезвычайно быстро. И сначала запускает тесты, которые
провалились при предыдущем запуске, так что вы сразу узнаёте, удалось
ли вам исправить ошибку.
Во время выполнения тестов Tester непрерывно выводит результаты в
терминал символами:
. – тест прошёл
s – тест был пропущен
F – тест провалился
Вывод может выглядеть так:
_____ ___ ___ _____ ___ ___
|_ _/ __)( __/_ _/ __)| _ )
|_| \___ /___) |_| \___ |_|_\ v2.6.0
PHP 8.5.2 (cli) | php | 8 threads
........s................F.........
-- FAILED: greeting.phpt
Failed: 'Hello John' should be
... 'Hello Peter'
in greeting.phpt(19) Assert::same('Hello Peter', $o->say('John'));
FAILURES! (35 tests, 1 failures, 1 skipped, 1.7 seconds)
Было запущено 35 тестов, один провалился, один был пропущен.
Мы продолжаем в главе Запуск тестов.
Режим наблюдения
Занимаетесь рефакторингом кода? Или, может, даже разрабатываете по
методике TDD (Test Driven Development)? Тогда вам понравится режим наблюдения. В
этом режиме Tester следит за исходными кодами и при их изменении
запускается автоматически.
При разработке у вас в углу монитора терминал, в котором на вас светит
зелёная полоса состояния, и когда она вдруг сменится красной, вы
знаете, что только что сделали что-то не совсем так. По сути, это
отличная игра, в которой вы программируете и стараетесь
удержать цвет.
Режим наблюдения запускается параметром --watch.
Отчёты о покрытии кода
Tester умеет порождать отчёты с обзором того, какую часть исходного кода
покрывают тесты. Отчёт может быть либо в понятном человеку формате HTML,
либо в Clover XML для дальнейшей машинной обработки.
Посмотрите пример HTML-отчёта с
покрытием кода.
Поддерживаемые версии PHP
| Версия |
Совместима с PHP |
| Tester 2.6 |
PHP 8.0 – 8.5 |
| Tester 2.5 |
PHP 8.0 – 8.5 |
| Tester 2.4 |
PHP 7.2 – 8.2 |
| Tester 2.3 |
PHP 7.1 – 8.0 |
| Tester 2.1 – 2.2 |
PHP 7.1 – 7.3 |
| Tester 2.0 |
PHP 5.6 – 7.3 |
| Tester 1.7 |
PHP 5.3 – 7.3 + HHVM 3.3+ |
| Tester 1.6 |
PHP 5.3 – 7.0 + HHVM |
| Tester 1.3 – 1.5 |
PHP 5.3 – 5.6 + HHVM |
| Tester 0.9 – 1.2 |
PHP 5.3 – 5.6 |
Относится к последней патч-версии.
Tester вплоть до версии 1.7 поддерживал и HHVM 3.3.0 или
новее (через tester -p hhvm). Начиная с версии Tester 2.0 поддержка
прекращена.