Перейти к содержимому

Тесты для своего кода - что покрывать и как запускать

Заводим тесты там, где они окупаются: на расчётах, разборе форматов и правилах, которые сложнее одной строки.

Решение

Начинаем с кода без ядра:

// /local/lib/Price/Calculator.php - чистый расчёт, платформа ему не нужна
final class Calculator
{
public function withDiscount(float $price, float $percent): float
{
return round($price * (100 - $percent) / 100, 2);
}
}
// такой класс проверяется тестом за минуту и без единого запроса к базе

Первыми тестируют функции, которым ядро не нужно. Расчёт скидки, разбор строки обмена, склейка адреса - именно здесь ошибки дороже всего и ловятся дешевле всего.

Поднимаем ядро там, где без него никак:

// bootstrap.php тестов
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
$_SERVER['DOCUMENT_ROOT'] = '/home/stend/public_html';
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';

Тесту с базой ядро подключают одним файлом. Дальше в тесте доступны те же классы, что и в коде сайта, но и цена растёт: такой тест идёт секунды, а не миллисекунды.

Готовим и убираем данные:

protected function setUp(): void
{
$this->iblockId = self::createTestIblock(); // свои данные, не боевые
}
protected function tearDown(): void
{
CIBlock::Delete($this->iblockId); // и убираем за собой
}

Тест создаёт свои данные и убирает их за собой. Тест, опирающийся на «товар с номером 42», ломается в тот день, когда этот товар удалят, и вины кода в этом нет.

Запускаем на стенде:

Окно терминала
cd /home/stend && ./vendor/bin/phpunit --testsuite unit # быстрые, без ядра
./vendor/bin/phpunit --testsuite integration # медленные, с базой
# на боевом сайте тесты не запускают: они пишут и удаляют данные

Тесты живут на стенде, а не на живом сайте. Они создают и удаляют записи, а случайно запущенный на боевом сайте набор способен натворить дел за одну минуту.

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

Вёрстку и вывод компонентов тестами не покрывают. Такой тест ломается от любой правки разметки и не ловит ничего полезного, а поддерживать его приходится вечно.

Тест, найденный сломанным после чужой правки, стоит починить сразу. Отключённый «на время» тест не включают уже никогда, а через полгода его никто и не помнит.

Первые тесты стоит написать на том куске кода, который уже ломался. Место, где ошибка стоила вечера разбора, - самый честный кандидат: там тест сразу показывает пользу, и спорить о нужности тестов больше не приходится.

Типичные проблемы

Тесты падают через раз.

Они опираются на данные боевого сайта, которые меняются. Тест создаёт свои данные и удаляет их за собой.

Набор тестов идёт по десять минут.

Все тесты поднимают ядро, даже те, которым оно не нужно. Быстрые и медленные тесты разводят по наборам.

На боевом сайте пропали записи.

Набор тестов запущен на живом сайте, и уборка данных сработала на его записях. Тесты запускают только на стенде.

Тест сломался от правки вёрстки.

Он проверяет разметку компонента, а не поведение кода. Вывод тестами не покрывают.

Половина тестов отключена.

Их отключали по одному, когда они мешали выкладке. Сломанный тест чинят сразу, а не откладывают.

Частые вопросы

С чего начать, если тестов нет вовсе?

С расчётов и разбора форматов: там ядро не нужно. Первые тесты появляются за час.

Нужно ли поднимать ядро в каждом тесте?

Нет, и лучше не поднимать: это главный источник медленных наборов. Ядро нужно только тестам с базой.

Где брать данные для тестов?

Создавать их в самом тесте и удалять после. Боевые данные для этого не годятся.

Что не стоит покрывать тестами?

Вывод, вёрстку и настройки в интерфейсе. Они меняются чаще, чем ломаются.

Смежное

Первоисточник