Зелёный набор тестов ничего не обещает
Триста проверок были зелёными, а боевая ручка отвечала ошибкой на первом же запросе. Разбираю, почему покрытие отказов выглядит как покрытие, и как считать пути, а не тесты.
Вчера у меня было триста проверок и ноль провалов. Я перевёл денежную ручку сайта на общий класс защит, прогнал набор, получил зелёное, выложил на бой. Первый живой запрос ответил так:
Call to undefined method ProbeLogic::вСуточномПотолке()
В коде остался вызов метода, который я сам же и удалил. Он стоял не в вырезанном куске, а ниже по файлу, между проверкой длины входа и кэшем. Ошибка простая. А вот то, что её не заметили двести девяносто восемь проверок, простым не является.
Почему набор молчал
Я посмотрел, что именно проверяли те двести девяносто восемь на этой ручке. Четыре проверки: отбивается ли запрос методом GET, отбивается ли без токена, отбивается ли с чужой страницы, отбивается ли слишком длинный вход. Все четыре про отказ.
Ни одна не шла дальше. Дальше начинается обращение к модели, а у нас правило: в тестах модель не вызываем, это деньги и это медленно. Правило верное. Следствие из него было моей ошибкой: удачный путь ручки, которая приносит деньги, не проверял никто. Отказы покрыты вчетвером, успех ноль раз.
Самое неприятное здесь в том, как это выглядит в отчёте. Проверок много. Они зелёные. Столбик цифр не отличается ничем от столбика, за которым стоит настоящее покрытие. Покрытие отказов вообще создаёт особенно убедительную иллюзию: отказов численно больше, они пишутся легко и быстро, набор растёт, и растёт именно та его часть, которая ничего не обещает.
Считать надо пути, а не тесты
Полезный вопрос звучит не «сколько у нас тестов», а «какие пути проверены хотя бы однажды». У почти любой ручки, которая куда-то ходит, путей четыре класса:
- Отказ на входе. Не тот метод, нет токена, чужой источник, слишком длинный вход. Пишется первым, потому что дешевле всего.
- Отказ снаружи. Внешняя служба ответила ошибкой, ответила пустым телом, не ответила вовсе. Проверяется подстановкой ответа.
- Успех. Тот самый путь, ради которого ручка существует. Разбор ответа, сборка того, что увидит человек, запись расхода.
- Повтор. Второй такой же запрос. Здесь живёт кэш, и здесь же живут самые тихие дефекты: отдали ответ прошлого раза вместе с его замером.
У меня были покрыты первый класс целиком и ничего больше. Причём второй и третий закрываются не сложнее первого: внешнюю службу подменяем заготовленным ответом, а состояние вроде счётчика расхода уводим в песочницу через переменную окружения. Второе, кстати, оказалось не просто удобством. Пока счёт писался в живую папку сайта, проверить суточный потолок было НЕЛЬЗЯ вообще, а потолок это защита кошелька. Непроверяемая защита существует со слов автора, и всё.
Что я поменял в привычке
Первое. Правило «готово» теперь означает проверено выкатом. Не «набор зелёный», а один живой запрос к боевой ручке своими руками. У меня это заняло минуту и нашло дефект, который набор не видел вовсе.
Второе. Когда пишу проверку, спрашиваю себя, к какому классу пути она относится, и держу в голове, что дешёвый класс уже покрыт. Соблазн добавить пятую проверку на отказ силён именно потому, что она пишется за две минуты.
Третье, и самое полезное. Каждую новую проверку я ломаю нарочно: вношу дефект, который она обязана поймать, и смотрю, покраснела ли она. В тот же день это спасло меня второй раз: подстава прошла молча, я полез разбираться и нашёл дыру не в коде, а в самой проверке, она отбрасывала пустые значения раньше сравнения.
Ниже карта из четырёх классов. Отметьте, что у вашей ручки проверено хотя бы однажды, и она скажет, чего именно вам не хватает, а не поставит оценку.
Карта путей вашей ручки
Отметьте классы, проверенные хотя бы одним тестом. Считает браузер, отмеченное никуда не уходит. Процента здесь нет намеренно: он и создаёт ту самую иллюзию.
Дмитрий Подстригаев
Разработчик, архитектор шлюза
Шестнадцать лет разработки, свой парк сайтов и свой шлюз к моделям. Пишу только о том, за что заплатил. Другие записи.
Читать дальше
-
Ручка приёма ошибок с фронта пишет прямо вам в мессенджер
Точка, которая принимает падения фронта, по устройству не может требовать токен. Мы проверили свою: она принимала произвольный текст с чужой страницы, и он попадал в канал тревог.
-
Ответ 200, а внутри ошибка: как это ломает интеграции
Проверка «status === 200» пропускает отказы. У нас на этом стояли два сайта и один виджет, и в журнале был ложный диагноз.
-
Отчёт показывает рост с нуля, а выросло только измерение
Ноль в отчёте бывает двух видов, и они выглядят одинаково. Мы почти опубликовали кейс с ростом с нуля до ста тысяч, где вся цифра была пробелом сбора.
Не согласны? Напишите. Содержательное возражение я публикую рядом с записью, а не прячу. А если у вас есть ручка, про которую непонятно, что у неё проверено, это и есть первый разговор на аудите.
Возразить или рассказать свою задачу · Разборы с инструментами