Меня зовут Тимофей, я руководитель отдела разработки kkube. В этой статье я хочу поделиться с вами своими мыслями об автотестах. Коротко пройдёмся по темам:

  • Тесты — это для «правильных айтишников» или действительно необходимая вещь?

  • Как быть с тестами в условиях дефицита бюджета и времени?

  • Почему разработчики сами саботируют тесты — и даже не замечают этого?

  • Сколько на самом деле стоит «сэкономленное» на тестах время?

  • Способен ли AI писать качественные автотесты?

Чего не будет в статье:

  • Обзора конкретных технологий

  • Кода и паттернов — об этом напишу отдельно, если тема зайдёт

Зачем нам статья без кода и технологий? Отвечаю — Написать expect(result).toMatchSnapshot() — не проблема. Проблема — когда в компании на 500+ человек никто не может объяснить руководству, зачем это нужно. А может, и не хочет — потому что сам не верит. Когда менеджер говорит «не до тестов», а разработчик с облегчением выдыхает — это не экономия времени, это общее заблуждение на двоих.

Когда даже зрелая компания не может объяснить себе, зачем нужны тесты

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

К сожалению, не успеваем уделять время автотестам. Сфокусированы на скорости разработки и релизах. К тому же требования постоянно меняются, приходится быть гибким — на тесты просто нет времени.

Статья звучала бы громче, если бы я мог назвать компанию, но это было бы некрасиво. Поэтому — обезличенные характеристики для контекста:

  • зарубежная продуктовая компания

  • штат 500–1000 человек

  • ведущие разработчики получают в среднем 5 000 €

Меня это удивило. Даже в Европе, где культура разработки зрелая, где документацию пишет профильный специалист, а процессы выстроены по всем канонам, — даже там бизнес не всегда понимает, зачем нужны автотесты.

Причём это не стартапы на коленке. Это компании, где шаблон баг-репорта составлен по ISO/IEC/IEEE 29119. А фраза «все баги пофиксить нельзя» — не отмазка уставшего разработчика, а один из базовых принципов ISTQB.

И вот что не укладывается в голове: если техническая грамотность на таком уровне — почему никто из разработчиков так и не смог объяснить руководству, что автотесты — это не блажь? А может сами разработчики и являются причиной?

Это, к слову, самая распространённая причина отсутствия тестов в кодовой базе. После того как я это услышал, меня охватило непреодолимое желание поделиться мыслями с другими специалистами и узнать их мнение. Пожалуй, Хабр — идеальное место.

Каждый аргумент против — на самом деле аргумент за

Когда я слышал причины, которые, по мнению моего собеседника, были железобетонными основаниями отложить тесты на потом, я буквально каждую из них интерпретировал как аргумент за то, что тесты нужны немедленно:

  1. У нас слишком частые релизы

  2. Постоянно меняются требования

  3. Разработчики и так работают на износ — если ещё тестами нагрузить, выгорят

  4. Хочется дать разработчикам больше свободного времени

Пройдёмся по пунктам.

Частые релизы

А не потому ли релизы такие частые, что на прод проскакивают баги и недоработки? Без unit-тестов каждое незначительное изменение может сломать то, что уже было проверено. Это приводит к бесконечной текучке багфиксов. Классика: выкатили патч старого бага → добавили новый → исправили новый → вернулся старый → срочный хотфикс на прод → повторить.

Именно это стало главной мотивацией для меня как разработчика — писать тесты для своего кода. А как руководитель я многократно фиксировал наблюдение: функционал, покрытый unit-тестами, на порядок реже становится причиной хотфиксов на прод.

Пример из практики. Поддерживали сложную интерактивную логику на сокетах в обучающей платформе: разные типы заданий, множество socket-событий, большое количество интерактивных UI-элементов, огромное число edge-кейсов и способов сделать то, что не предполагалось авторами ТЗ. Legacy-код, отсутствие unit-тестов — в таск-трекере ветка с подзадачами, где постоянно горят 40 багов. Это длилось полтора года.

Покрыли unit-тестами сервисы бэкенда из этой доменной логики (70% code coverage), добавили около 10 e2e-сценариев. Два месяца в релизе — активных багов 5, и все связаны с добавлением нового функционала. Проблема с вечной текучкой багов решена. Это даже не x8 профит — я даже не знаю, как в любимых бизнесом цифрах это оценить.

Тезис: Постоянный поток срочных релизов и hot-fix'ов очень часто следствие отсутствия тестов на проекте.

Постоянно меняются требования

Придумываете новые фичи, затрагиваете старые — отличная почва для багов. Но не подумайте, что я собрался ругать бизнес. Адаптироваться к рынку, тем более в современных реалиях, — сверхсложная задача. И если вы постоянно меняете приоритеты, обкатываете гипотезы, проводите эксперименты или пока ��е знаете, чего хотите, — всё в порядке, так и должно быть. Раз есть что менять и к чему адаптироваться — значит, вы занимаетесь живым делом. Я лишь хочу предложить решение: чтобы не ломалось старое, когда добавляете новое, — пишите тесты.

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

Как найти проблемные участки. Изучите систему трекинга задач и баг-репортов. То, что доставляет больше всего неприятностей, — именно то, что нужно покрывать тестами в первую очередь. Как делаю я: смотрю на качество кодовой базы и количество багов, которые вынуждают меня погружаться в конкретную область кода. Если много багов отсылают к определённому участку и этот код мне не нравится — не обязательно хвататься и всё рефакторить. Пусть работает как работает. Но я зафиксирую хотя бы текущее поведение снапшотами в Jest, и после этого мне уже психологически проще работать.

Что я имею в виду под «качеством кодовой базы». Качество кода — бесконечно спорная тема. Какой код считать качественным? Который красиво выглядит? Вызывает меньше багов? Прошёл N циклов рефакторинга и ревью? С точки зрения тестировщиков ваш код - лажа потому что в нем баги. С точки зрения бизнеса ваш код хороший если компания получила прирост дохода. С точки зрения других разработчиков ваш код плохой, потому что они написали бы лучше. Каждый будет измерять по-своему. К счастью существуют объективные метрики — например, цикломатическая сложность (в eslint правило complexity), которая считает количество независимых путей выполнения в коде. Чем больше ветвлений, вложенных условий и нетривиальных переходов — тем выше сложность, тем труднее код понимать и тем вероятнее в нём баги. Но на практике вам не обязательно вооружаться какими-то передовыми инструментами анализа, чтобы понять с каким качеством кода вы имеете дело. Если код сложен для понимания, если есть ощущение, что разработчики нагадили (неважно — торопились, не хватало компетенций или просто код вам не нравится), если морально тяжело взять себя в руки, чтобы в нём разобраться, — ваша интуиция почти наверняка указывает на те же участки, которые подсветил бы статический анализ. Это и есть то, что я называю некачественным кодом. Столкнувшись с таким, я сначала напишу «дешёвые» тесты на Jest-снапшотах, а уже потом буду разбираться с бизнес-логикой.

Тезис: Изменения требований — это норма и чем больше они меняются, тем больше повод задуматься о том как обезопасить свой код.

Разработчики и так работают на износ — если ещё тестами нагрузить, выгорят

Конечно выгорят. Конечно работают на износ. И, думаю, вы уже догадались почему — потому что на проекте нет тестов.

Если ты взялся допиливать чью-то фичу или исправлять баг, а быстрого способа убедиться, что ничего не сломал, нет — значит, ты обречён на переработку. Дальше тебя ждёт футбол с тестировщиком или пользователями, которые непременно сообщат, как всё плохо. Добавят тебе пару очков к шкале выгорания, и ты пойдёшь обратно полировать свою фичу.

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

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

Хочется дать разработчикам больше свободного времени

Да уж, фантастика какая-то, бизнес заботится о свободном времени своих сотрудников? Ёще и хочет его им дать? По-моему, ты лукавишь - скажет читатель - вряд ли между руководителями компаний ведутся такие диалоги. Все в порядке, не волнуйтесь, это я просто так переформулировал желание бизнеса получить от вас больше закрытых задачек в неделю.

Очень часто тесты воспринимают исключительно как инструмент предотвращения багов или попытку сделать копию вашего ручного тестировщика только на языке программирования. Это очень большое и вредное заблуждение. Каждый разработчик должен уметь владеть тестами в первую очередь для решения сразу множества проблем:

  • Дебаг кода — запуск конкретного участка кода в изолированной среде вместо прокликивания всего приложения

  • Автоматизация репродюса — превращение шагов воспроизведения бага в запускаемый скрипт, который можно гонять сколько угодно раз

  • Работа с mock-данными — быстрая генерация нужных состояний и данных без зависимости от внешних сервисов и БД

  • Безопасный рефакторинг — уверенность, что изменения внутренней структуры не ломают внешнее поведение

  • Исследование legacy-кода — способ понять, что делает незнакомый код, через эксперименты с вводом и выводом

  • Проверка граничных случаев — систематическая проверка null, пустых коллекций, переполнений и прочих крайних сценариев

  • Изоляция компонентов — возможность запустить один модуль отдельно от всего приложения и проверить именно его логику

  • Изучение сторонних библиотек — написание маленьких тестов для проверки гипотез о поведении чужого кода

  • Ускорение онбординга — новый разработчик читает тесты и быстро понимает сценарии использования системы

  • Уверенность при деплое — зелёный CI даёт объективное основание катить релиз, а не полагаться на интуицию

  • Фиксация требований — бизнес-правила, выраженные в тестах, становятся исполняемой спецификацией, а не абзацем в Confluence

  • Живая документация — тесты описывают реальное поведение системы и, в отличие от комментариев, не устаревают незаметно

  • Проектирование API — написание теста до реализации заставляет продумать интерфейс с точки зрения потребителя

  • Валидация архитектурных решений — если код тяжело тестировать, это сигнал о проблемах в архитектуре

Многие из этих пунктов это в первую очередь про экономию времени, причем речь о гигантских временных ресурсах. Конечно все это возможно только если вы хорошо владеете инструментами и паттернами тестирования. Все это придет только с опытом, но откуда этому опыту взяться если у вас нет на это времени.

Я не буду следовать методичкам в стиле «покажите цифры, бизнес любит цифры, сколько вы сэкономили человеко-часов». Здесь их крайне сложно привести, и вряд ли кто-то покажет вам действительно стоящую аналитику по этому вопросу. Но всё же приведу пример в виде небольшой истории.

История одного линолеума

Вася делает многостраничную форму расчёта стоимости линолеума. Всего 5 шагов.

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

  2. Надоедает — пишет заглушки для четырёх страниц, делает себе песочницу для отладки функции подсчёта.

  3. Доделывает → создаёт MR → ой, забыл убрать заглушки.

  4. Возвращается, рефакторит, перепроверяет все страницы → обновляет MR.

  5. MR на тестовом стенде. Тестировщица Алиса тратит день, находит 5 багов.

  6. Вася вооружается заглушками, фиксит → MR → Алиса снова тратит день на полный прогон → фича уходит в релиз.

  7. Техподдержка рапортует о баге.

  8. Вася достаёт заглушки → фиксит → MR → забывает убрать заглушки → тимлид замечает → рефакторинг → обновляет MR.

  9. Алиса в третий раз проходит все тесткейсы и чеклисты → репортует новый баг.

  10. Few thousand years later...

  11. Фича в релизе.

Итого: Вася — ~неделя. Алиса — ~3 дня ручного прогона. Тимлид — нервы.

С тестами: та же фича, где-то в параллельной вселенной

Вася пишет ту же форму расчёта линолеума.

  1. Сразу покрывает функцию подсчёта юнит-тестами — проверяет граничные случаи, скидки, округления.

  2. Навигацию между шагами проверяет интеграционным тестом — не нужно каждый раз заполнять 4 страницы руками.

  3. Заглушки не нужны — тесты и есть его песочница.

  4. Доделывает → прогоняет тесты → зелёные → MR. Никакого мусора в коде.

  5. Алиса проверяет визуал, UX и пару сценариев, которые автоматизация не покрывает. Находит 1 мелкий баг.

  6. Вася фиксит → тест на этот кейс → MR → Алиса точечно перепроверяет → релиз.

  7. Техподдержка рапортует о баге. Вася пишет падающий тест, воспроизводящий проблему → фиксит → тесты зелёные → MR → быстрый прогон Алисы → релиз.

Итого: Вася — 1 день. Алиса — пара часов вместо трёх дней. Баг из прода больше не вернётся — на него есть авто-тест.

Вывод: бизнес может теперь уволить Алису Сэкономлены целые дни, в стратегической перспективе человеко-месяца.

История полностью реальная — честное слово.

Способен ли AI писать качественные автотесты

Ну как же без AI. Обещал высказаться и об этом. Поэтому вот — нет, не может.

На этом хотелось бы закончить, но раз вы всё ещё читаете, продолжу — разумеется, может, но только в руках опытного разработчика. И качество этих тестов будет прямо пропорционально опыту этого разработчика.

И сейчас тоже хочется закончить, но раз вы все еще читаете — вообще AI идеально пишет тесты, если ваша цель кол-во зеленых галочек, то у вас их будет много. Будут ли они соответствовать отсутствию багов и способствовать улучшению кодовой базы — это уже совсем другой вопрос, для отдельной статьи. Кстати Хабр тут отлично справляется и без меня.

Ну всё, всё. Теперь точно пора закругляться.

С вами был Тимофей. В первую очередь я просто хотел высказаться и услышать ваше мнение. У меня есть группа в ТГ, если интересно присоединяйтесь. Буду признателен, если поучаствуете в голосовании, которое я там оставил на эту тему.