Тесты: как менять код без страха
Тест — это утверждение о твоём коде, записанное так, что машина может проверить его за секунду. Именно оно превращает «кажется, это всё ещё работает» в «я знаю, что это всё ещё работает».
23:00. Тебе нужно изменить, как корзина считает скидку. Функция на сорок строк, её написал человек, который уже уволился, и от неё зависит цена на каждой странице сайта. Ты меняешь две строки и теперь должен выяснить, не сломал ли ты что-нибудь. Жить можно двумя способами: открыть сайт, вручную прокликать каждую страницу и надеяться, что ничего не пропустил, — или выполнить одну команду и получить ответ за две секунды. Второй способ — не магия. Это тесты.
Тест — это утверждение, записанное в форме, которую может проверить машина. Ты говоришь: для таких входных данных эта функция обязана дать такой ответ. Потом запускаешь. Если ответ совпал, утверждение выдержало. Если нет — ты только что узнал что-то новое. Тест не доказывает, что код правильный; это список конкретных обещаний, которые проверяются автоматически каждый раз.
vat(net, rate) = net * (1 + rate)
assert priceWithVat(100, 0.21) === 121
assert priceWithVat(0, 0.21) === 0
assert priceWithVat(100, 0) === 100
assert x == y
FAIL at line 6Тесты бывают разного размера. Модульный тест берёт один маленький кусок кода — обычно одну функцию — и подаёт ей входные данные, когда больше ничего не участвует: ни сети, ни базы данных, ни браузера. Он выполняется за доли секунды, и когда он падает, ты точно знаешь, какая функция сломана. Интеграционный тест запускает несколько настоящих кусков вместе: твой код плюс настоящая база данных или твой код, который по сети разговаривает с другим сервисом. Он медленнее, может упасть по причинам, никак не связанным с твоим изменением, и ловит совершенно другой вид ошибок — тот, который появляется только когда куски соединены вместе. Нужны и те и другие, и нужно понимать, на какой из них ты смотришь.
Вот что удивляет людей: упавший тест — это хорошая новость. Красный тест значит, что машина нашла расхождение между тем, что ты обещал, и тем, что делает код, — и нашла это сейчас, на твоём ноутбуке, за две секунды, а не в следующий четверг при клиенте. Неприятная версия того же бага — та, для которой никто не написал теста: она доходит до реальных людей, и ты узнаёшь о ней из сообщения, которое начинается со слов «слушай, у тебя сайт тоже сломан?»
Настоящая причина, по которой в командах пишут тесты, вообще не в поиске багов. Она в страхе. В коде без тестов каждое изменение — это ставка, поэтому люди делают как можно меньшие правки и боятся трогать то, что работает, включая уродливые места, которые отчаянно нужно переписать. С тестами можно изменить функцию, прогнать набор и за секунды узнать, не сломал ли ты остальное. Именно это делает уборку и улучшение кода вообще возможными. Тесты — не отчёт для учителя. Это то, что позволяет тебе двигаться.
Практика
Сопоставь каждую проверку с тем, чем она на самом деле является.
Подсказки
- Спроси, что нужно каждой проверке, чтобы запуститься: только эта одна функция или целая работающая система?
- Чем медленнее и реалистичнее проверка, тем дороже её запускать и тем больше она может рассказать о настоящем поведении.
15 XP
Ты написал тест, запустил его, и он упал. Какой следующий шаг самый полезный?
Подсказки
- Спроси, кто неправ, когда проверка падает: код или ожидание, которое ты записал?
- Красный тест — это новая информация. Спроси, что он только что тебе сообщил, а не как сделать его тихим.
14 XP
Расставь порядок, в котором аккуратный разработчик убивает баг насовсем.
Подсказки
- Ты не докажешь, что исправление работает, пока не увидел баг нарочно.
- Старый тест остаётся в наборе навсегда: именно это не даёт одному и тому же багу вернуться.
16 XP