Экспресс-курс · No. 21
Тест — это просто код, который автоматически проверяет твой код. Люди думают, что его работа — доказать, что сегодняшний код работает. Но настоящая отдача приходит позже: тесты дают менять, рефакторить и добавлять фичи завтра, не ломая того, что уже работало. Выучи виды и слова — и тестирование перестанет быть рутиной и станет тем, что даёт двигаться быстро.
19 понятий · схема у каждого · шпаргалка в конце
Почти все неверно понимают, для чего тесты. Они выглядят как способ доказать, что код работает. Их настоящая ценность глубже и практичнее — и когда ты это видишь, ты пишешь тесты по-другому.
Карта: 19 понятий в семи главах
Карта метро: сначала видишь все линии целиком, потом едешь по станциям.
Все понятия курса разложены по семи главам. Сначала — зачем вообще тесты. Потом — виды тестов и их баланс. Потом — подделки, без которых не изолировать кусок кода. Дальше — ремесло хорошего теста, доверие к набору тестов и то, как всё это применять в работе.
Как читать курс
Паспорт: одинаковые поля у всех, поэтому их легко сравнивать.
У каждого понятия дальше одно и то же: английское название (запомни его — это термин, который ты встретишь в документации и на собеседованиях), образ из жизни, схема и карточка с полями: проблема, идея, пример, цена, когда брать и связи с другими понятиями. Главная мысль всего курса одна: тесты — это страховка для будущих изменений, а не справка о том, что код работал однажды.
Automated Test — тест как код, который проверяет код
Второй человек, который пересчитывает твои суммы на калькуляторе и отмечает каждую, что не сходится, — автоматически, без устали и одинаково каждый раз.
Тест (test) — это маленький кусок кода, который запускает твой код с известным вводом и проверяет, что результат тот, что ты ждёшь. «Если дать add числа 2 и 3, вернёт ли она 5?» Если да, тест проходит (pass); если нет — падает (fail) и сообщает тебе. Вот и вся механика. Сила в том, что это автоматически и повторяемо: тысячи таких проверок бегут за секунды при каждом изменении, и никому не надо перепроверять руками.
- Проблема
Проверять руками долго и скучно — поэтому после каждой правки никто не перепроверяет всё.
- Идея
Записать проверку кодом: известный ввод → вызов → сравнение с ожиданием. Итог — pass или fail.
- Пример
- «
add(2, 3)должна вернуть 5» — одна строка, бежит за миллисекунды. - Цена
- Тесты — тоже код: их надо писать, читать и поддерживать.
- Когда брать
- Для любого кода, который ты собираешься менять дальше.
- Связи
Ловит Regression; бывает Unit, Integration и E2E; бежит в CI Gate.
Safety Net — настоящий смысл: менять без страха
Страховка у скалолаза: она не лезет за тебя, но даёт рискнуть смелым ходом, потому что срыв не будет смертельным. Ты лезешь смелее, ведь тебя поймают.
Вот главное недопонимание. Тесты в основном не про то, чтобы доказать, что сегодняшний код верен, — они про завтра. Когда ты меняешь код, рефакторишь его или добавляешь фичу, хороший набор тестов (test suite) сразу говорит, не сломал ли ты то, что раньше работало. Эта страховочная сеть (safety net) и даёт уверенно менять большую кодовую базу, а не бояться её трогать. Тесты — это способ двигаться быстро, чтобы за спиной ничего тихо не ломалось.
Regression — регрессия
Ряд растяжек по всему дому: как только что-то тревожит комнату, на которую ты даже не смотрел, срабатывает сигнал. Тебе не надо обходить каждую комнату самому.
Регрессия (regression) — это когда изменение ломает то, что раньше работало, часто где-то далеко от того места, которое ты трогал. Без тестов регрессии прячутся, пока их не найдёт пользователь. С тестами в тот же миг, как твоё изменение ломает старое поведение, тест краснеет и показывает, где именно. Это будничная отдача: внёс правку, прогнал тесты — и сразу знаешь, не сломал ли что-то ещё. Набор тестов стережёт весь дом, пока ты работаешь в одной комнате.
- Что это
- Изменение ломает поведение, которое раньше работало.
- Подвох
- Ломается часто не там, где ты правил, а в далёкой части системы.
- Пример
- Поменял расчёт корзины — и счета начали выходить с неверной суммой.
- Как ловить
Набор тестов на старое поведение, который гоняют на каждом изменении.
- Совет
- Нашёл баг — добавь тест на него, чтобы он не вернулся.
- Связи
- Ловится Automated Test; страхует автоматически через CI Gate.
Тест проверяет код автоматически. Его настоящая ценность — не одноразовое доказательство, а свобода менять завтра, не ломая тихо то, что работает сегодня.
Фундамент тестирования — юнит-тест: быстрая, точная проверка одного маленького куска кода самого по себе. Большинство твоих тестов будут такими, и не зря.
Unit Test — один маленький кусок в изоляции
Проверить, что один кубик Lego правильной формы и размера, прежде чем строить из него, — а не собирать весь замок, чтобы потом узнать, что одна деталь была кривой.
Юнит-тест (unit test) проверяет один маленький юнит (unit) — обычно одну функцию — в изоляции (isolation) от остальной системы. Дал ввод, проверил вывод, больше никто не участвует. Кусок маленький и сам по себе, поэтому, когда тест падает, ты точно знаешь, что сломалось и где. Юнит-тесты — микроскоп тестирования: узкий, точный, наведённый на одну вещь за раз.
- Проблема
Когда падает большая проверка, непонятно, какая из сотни частей виновата.
- Идея
- Проверять по одной функции, отрезав её от базы, сети и всего остального.
- Пример
calculateTax(100)возвращает20— без базы, без HTTP, за миллисекунды.- Цена
- Не видит, как куски работают вместе, — для этого нужны другие тесты.
- Когда брать
Для большей части тестов: бизнес-логика, расчёты, правила, краевые случаи.
- Связи
Основание Test Pyramid; изоляцию дают Test Doubles; форма — Arrange–Act–Assert.
Fast and many — быстро и много, вот вся идея
Проверка орфографии, которая проходит весь документ за мгновение, — так быстро, что её гоняешь постоянно, а не раз в месяц, как медленное ревью.
Каждый юнит-тест крошечный и не трогает ничего внешнего, поэтому он бежит за миллисекунды. Значит, их можно иметь тысячи и гонять все на каждом изменении, постоянно. В этой скорости и суть: набор, который бежит за секунды, запускают всё время — и поломку ловят в момент, когда она случилась. Медленный набор пропускают, а пропущенный набор не защищает ничего. Быстро и много — вот что делает юнит-тесты рабочей лошадкой.
Arrange–Act–Assert — подготовь, сделай, проверь
Простой опыт: подготовь условия, сделай одно действие, проверь результат. Ясная подготовка, один ход, одна проверка.
У хорошего юнит-теста три простых шага — паттерн Arrange–Act–Assert (AAA): подготовь вводы, один раз вызови то, что тестируешь, потом проверь, что результат тот, что ты ждал. Ассерт (assertion) — это сама проверка: «ответ должен быть равен 5». Тесты в этой чистой форме легко читать, а их падение сразу понятно. Тест, который трудно прочесть, — это тест, которому никто не будет доверять и который никто не станет поддерживать.
- Проблема
Тест-каша: подготовка, вызовы и проверки вперемешку — не понять, что он проверяет.
- Идея
- Три блока по порядку: подготовка → одно действие → проверка (assertion).
- Пример
Arrange: корзина с двумя товарами. Act:
cart.total(). Assert: итог равен 20.- Цена
- Почти никакой — нужна только дисциплина «одно действие на тест».
- Когда брать
- Всегда, в любом тесте — юнит, интеграционном или сквозном.
- Связи
Делает тест читаемым — а значит, Living Documentation; основа Unit Test.
Юнит-тест проверяет один маленький кусок в изоляции за миллисекунды — поэтому таких тестов тысячи, и они бегут постоянно. Подготовь ввод, сделай одно действие, проверь результат.
Юнит-тесты проверяют куски поодиночке, но софт — это куски, которые работают вместе. Это покрывают два более широких вида тестов, а у правильного баланса всех трёх есть форма, которую стоит знать.
Integration Test — куски, работающие вместе
Проверив каждый кубик Lego, ты скрепляешь несколько вместе — убедиться, что они правда соединяются. По отдельности кубики были в порядке, но подходят ли они друг к другу?
Интеграционный тест (integration test) проверяет, что несколько частей верно работают вместе: твой код и база данных, два сервиса, которые обмениваются данными. Каждый юнит может быть идеален сам по себе и всё равно не состыковаться: не тот формат, неверное допущение о другой стороне. Интеграционные тесты ловят именно эти швы (seams). Они медленнее юнит-тестов, потому что в них больше настоящих движущихся частей, но проверяют стыки, которые юнит-тесты по замыслу пропускают.
- Проблема
- Каждая часть проходит свои юнит-тесты, а вместе они не работают.
- Идея
- Запустить несколько настоящих частей вместе и проверить стык между ними.
- Пример
Репозиторий пишет пользователя в настоящую тестовую базу и читает его обратно.
- Цена
- Медленнее юнит-тестов, нужна настройка окружения (база, сервисы).
- Когда брать
- Для швов: код ↔ база, сервис ↔ сервис, твой код ↔ чужой API.
- Связи
- Средний слой Test Pyramid; ловит то, что прячет Over-mocking.
End-to-End Test — вся система глазами пользователя
Генеральная репетиция всей пьесы от начала до конца на настоящей сцене — не проверка реплик или реквизита по отдельности, а весь спектакль, как его увидит зритель.
Сквозной тест (end-to-end test, E2E) гоняет всю систему так, как это делал бы настоящий пользователь: нажать кнопку, заполнить форму, проверить, что нужное случилось через весь стек. Это самый реалистичный тест и самое ценное подтверждение, что продукт правда работает. Но он же самый медленный и хрупкий (fragile): должно работать много частей сразу, а мелкое изменение интерфейса может его сломать. Поэтому e2e берут для немногих критичных путей, а не для всего.
- Проблема
- Все части проверены, но работает ли продукт целиком — неизвестно.
- Идея
- Пройти путь пользователя через весь настоящий стек и проверить итог.
- Пример
Браузер-робот: войти, положить товар в корзину, оплатить, увидеть «заказ оформлен».
- Цена
- Самый медленный и хрупкий; ломается от мелких правок интерфейса.
- Когда брать
- Для нескольких критичных путей: вход, оплата, главный сценарий.
- Связи
Вершина Test Pyramid; если таких тестов слишком много — «рожок мороженого».
Test Pyramid — пирамида тестов
Пирамида стоит, потому что основание у неё широкое, а вершина узкая. Переверни её на острие — и она упадёт. Сама форма и даёт устойчивость.
Пирамида тестов (test pyramid) — правило большого пальца для баланса: много быстрых юнит-тестов в основании, меньше интеграционных в середине и горстка медленных сквозных на вершине. Такая форма дёшево даёт широкую и быструю проверку, а сверху — ровно столько реалистичных проверок, сколько нужно. Антипаттерн — куча медленных e2e и мало юнит-тестов — называют «рожком мороженого» (ice-cream cone): он медленный, флакающий и мучительный. Целься в пирамиду, а не в рожок.
- Проблема
Тестов много, но набор медленный и нестабильный — его перестают гонять.
- Идея
- Много дешёвых быстрых тестов внизу, мало дорогих медленных наверху.
- Пример
Тысячи юнит-тестов, сотни интеграционных, десяток e2e на главные сценарии.
- Цена
- Нужно осознанно решать, на каком уровне проверять каждую вещь.
- Антипаттерн
Ice-cream cone — пирамида вверх ногами: много e2e, мало юнитов.
- Связи
Слои — Unit, Integration, E2E; лишние e2e плодят Flaky Tests.
Юнит-тесты проверяют куски поодиночке, интеграционные — вместе, сквозные — всю систему глазами пользователя. Держи пирамиду: много быстрых, мало медленных.
Чтобы проверить кусок в изоляции, часто нужно подменить настоящие вещи, от которых он зависит. Это и есть моки и стабы — и ими так же легко навредить, как помочь.
Test Double — подделка вместо настоящей зависимости
Авиатренажёр вместо настоящего самолёта: поддельные приборы и вид из окна дают отработать один навык безопасно, без цены и риска настоящего полёта.
Чтобы проверить кусок в изоляции, его настоящие зависимости (dependencies) — базу, платёжный сервис, отправку писем — подменяют подделками. Общее название для любой такой подделки — тестовый двойник (test double), как дублёр в кино. Так ты проверяешь свой код без медленной, дорогой или непредсказуемой настоящей вещи. Юнит-тест не должен на самом деле списывать деньги с карты или слать письмо — ты подставляешь подделку, которая делает вид, и тест остаётся быстрым, надёжным и самодостаточным.
- Проблема
Настоящие зависимости медленные, платные или непредсказуемые — тест с ними хрупкий.
- Идея
Подставить вместо зависимости объект, который ведёт себя как она, но понарошку.
- Пример
- Вместо Stripe — подделка, которая всегда отвечает «оплата прошла».
- Цена
- Подделка может врать: настоящая вещь ведёт себя иначе.
- Когда брать
Для внешнего, медленного и случайного: сеть, платежи, почта, время.
- Связи
Два главных вида — Stub и Mock; даёт изоляцию Unit Test; в избытке — Over-mocking.
Stub vs Mock — заготовленный ответ против проверки вызова
Стаб — картонная фигура, которая по сигналу говорит одну реплику. Мок — актёр, который ещё и докладывает, что именно ты ему сказал и как.
Два вида подделок часто путают. Стаб (stub) просто возвращает фиксированный, заготовленный ответ — «пусть база вернёт вот этого пользователя», — чтобы проверить, как твой код обработает такой ввод. Мок (mock) ещё и проверяет взаимодействие — «убедись, что мой код вызвал sendEmail ровно один раз и с этим адресом». Стаб кормит твой код данными; мок следит, как твой код с ним обращается. Нужны данные — бери стаб; нужно убедиться, что случился побочный эффект, — бери мок.
- Что это
- Подделка, которая возвращает заготовленный ответ.
- Пример
getUser()всегда возвращает{ name: 'Ann' }.- Что проверяем
- Вывод твоего кода при таких данных.
- Когда брать
- Когда коду нужно что-то получить от зависимости.
- Что это
- Подделка, которая запоминает вызовы и даёт их проверить.
- Пример
- Проверить, что
sendEmailвызван ровно один раз с нужным адресом. - Что проверяем
- Сам вызов: был ли, сколько раз, с какими аргументами.
- Когда брать
- Когда важен побочный эффект: письмо ушло, платёж отправлен.
Over-mocking — моки, которые врут
Репетировать пьесу целиком с картонными фигурами вместо всех остальных актёров: свои реплики ты отточишь, но так и не узнаешь, сыграет ли настоящая труппа вместе.
Подделки мощные, но в избытке опасны. Если ты мокаешь всё, тест проверяет твои допущения о том, как ведут себя другие части, — а не то, как они ведут себя на деле. Тест зеленеет, пока настоящая интеграция сломана. Это антипаттерн чрезмерного моканья (over-mocking). Поэтому мокай медленные, внешние и непредсказуемые зависимости, но не подменяй подделкой ту самую вещь, которую пытаешься проверить. Тест, построенный целиком на моках, может пройти, пока настоящая система падает, — самый опасный вид зелёного.
- Симптом
- Тест зелёный, а в продакшене та же функция падает.
- Причина
Подделано всё вокруг — тест проверяет только твои допущения о других частях.
- Пример
Мок базы всегда отвечает «ок», а настоящая база отвергает запрос из-за неверного поля.
- Лекарство
Мокай только внешнее и медленное; стыки проверяй Integration Test.
- Связи
Злоупотребление Test Double; ломает доверие так же, как Flaky Test.
Моки и стабы подменяют настоящие зависимости, чтобы тестировать в изоляции. Стаб даёт данные, мок проверяет вызов. Замокаешь слишком много — тест пройдёт, пока реальность падает.
Кроме видов тестов есть ремесло — писать хорошие. И есть одна дисциплина, разработка через тесты, которая переворачивает привычный порядок и меняет то, как ты проектируешь код.
TDD — сначала тест, потом код
Нарисовать мишень до выстрела, а не после, — чтобы целиться в ясную цель, а не обводить яблочко вокруг того места, куда стрела случайно попала.
Разработка через тесты (test-driven development, TDD) переворачивает обычный порядок: ты пишешь тест до кода. Цикл называется red, green, refactor (красный, зелёный, рефакторинг): напиши падающий тест на то, что хочешь (красный), напиши ровно столько кода, чтобы он прошёл (зелёный), потом приведи код в порядок под защитой теста (рефакторинг). Тест первым заставляет точно определить, что значит успех, ещё до постройки, и гарантирует, что код можно тестировать — ведь он родился из теста.
- Проблема
- Тесты пишут «потом» — и код выходит неудобным для тестирования.
- Идея
- Сначала падающий тест, потом минимум кода, потом уборка. И так по кругу.
- Пример
Тест «пустая корзина → итог 0» падает; пишешь
total(); тест зелёный; чистишь код.- Цена
- Непривычно и поначалу медленнее; сложно, когда задача ещё не ясна.
- Когда брать
Для логики с понятными правилами: расчёты, валидация, исправление багов.
- Связи
Даёт тестируемый код и Living Documentation; рефакторинг держится на Test Behaviour.
Test Behaviour, Not Implementation — проверяй что, а не как
Судить повара по тому, вкусное ли блюдо, а не по тому, в какой руке он держал нож. Важен итог, а не личные детали того, как он к нему пришёл.
Важнейшая привычка для долгоживущих тестов: проверяй, что код делает, а не как он это делает — поведение (behaviour), а не реализацию (implementation). Проверяй, что getDiscount возвращает верную цену, а не что он вызвал три конкретных внутренних помощника по порядку. Тесты, привязанные к итогу, переживают рефакторинг: можно переписать всё нутро, а они всё ещё проходят. Тесты, привязанные к реализации, ломаются при каждой уборке кода — и люди привыкают им не доверять и удалять их. Проверяй поведение, и тест остаётся полезным.
- Проблема
- Тесты ломаются от каждого рефакторинга, хотя поведение не менялось.
- Идея
- Проверять только наблюдаемый итог через публичный интерфейс.
- Пример
Проверить, что
getDiscountвернул 90, — а не какие помощники он вызвал внутри.- Цена
- Иногда сложнее добраться до краевого случая только через вход и выход.
- Когда брать
- Всегда по умолчанию.
- Связи
Делает возможным рефакторинг в TDD; лишние Mock часто привязывают к реализации.
Living Documentation — тесты как документация
Хорошо написанный набор тестов читается как список обещаний: «при этом оно делает то». Это яснее любого комментария о том, как код должен себя вести.
Ясный набор тестов работает как живая документация (living documentation). Каждый тест записывает обещание — «при пустой корзине итог равен нулю». И раз тесты бегут, эта документация не устаревает, как комментарии: если поведение меняется, тест ломается. Поэтому пиши тесты так, чтобы их читали: ясные имена, одно поведение на тест, очевидный arrange-act-assert. Новичок должен понять, что делает код, просто читая его тесты. Эта читаемость стоит столько же, сколько сама проверка.
- Проблема
- Комментарии и вики врут: код поменялся, а текст остался старым.
- Идея
- Тесты — обещания, записанные кодом. Сломалось обещание — тест покраснел.
- Пример
Имя теста: «пустая корзина — итог равен нулю». Читается как спецификация.
- Цена
- Нужно время на понятные имена и одно поведение на тест.
- Когда брать
- Всегда — особенно в коде, который читают новички.
- Связи
- Держится на Arrange–Act–Assert и Test Behaviour.
Пиши тест первым (red-green-refactor), проверяй поведение, а не реализацию, и делай тест читаемым — тогда тесты переживают рефакторинг и работают документацией, которая не устаревает.
Одно число и одна напасть решают, можно ли на деле доверять набору тестов. Неверно пойми покрытие или терпи флакающие тесты — и зелёный прогон перестанет что-либо значить.
Code Coverage — что протестировано, а не насколько хорошо
Карта улиц, по которым проехал патруль: удобно заметить районы, куда никто не заезжал, но проехать мимо дома — не значит проверить замки.
Покрытие кода (code coverage) — это процент кода, который выполняется во время тестов. Это полезный ориентир, чтобы найти то, что не тестировано совсем, — улицы, где патруль не был. Но высокое покрытие не значит хорошие тесты: код может выполниться в тесте, который ничего толком не проверяет, — 100% покрытия при почти нулевой проверке. Используй покрытие, чтобы находить слепые пятна, а не как доказательство качества. Тронутая строка — не то же самое, что проверенная строка.
- Что это
- Доля строк (или веток) кода, которые выполнились во время тестов.
- Чем полезно
- Показывает слепые пятна — код, который не запускал ни один тест.
- Подвох
Не меряет качество: строка может выполниться, но ни один assertion её не проверит.
- Пример
80% покрытия, но вся логика скидок в непокрытых 20% — вот где искать риск.
- Когда брать
- Как фонарик для поиска пробелов, а не как цель.
- Связи
Превращается в ловушку по закону Гудхарта; не заменяет Risk-based Testing.
Goodhart's Law — не дай покрытию стать целью
Платить рабочим за километр покрашенной разметки и смотреть, как они рисуют длинные бесполезные линии на пустырях, чтобы добить план. Метрика выросла, дороги безопаснее не стали.
Когда число становится целью, люди оптимизируют число, а не то, что оно мерило, — это закон Гудхарта (Goodhart's law). Потребуй «100% покрытия» — и получишь тесты, написанные, чтобы тронуть строки, а не поймать баги: пустые тесты, которые ничего не проверяют и просто обыгрывают метрику. Покрытие — фонарик для поиска пробелов, а не кубок, который надо набить до максимума. Гонись за настоящей уверенностью — ловят ли тесты поломку на деле? — и пусть покрытие будет одной подсказкой из нескольких, а не самой целью.
- Суть
- Когда мера становится целью, она перестаёт быть хорошей мерой.
- В тестах
Правило «без 100% покрытия не мёржим» рождает тесты без assertion — ради числа.
- Пример
Тест вызывает функцию и ничего не проверяет — покрытие растёт, защиты ноль.
- Лекарство
Спрашивать «поймает ли тест поломку?», а покрытие держать подсказкой.
- Связи
- Главная ловушка Code Coverage.
Flaky Test — флакающий тест хуже, чем никакого
Пожарная сигнализация, которая срабатывает случайно и без причины: через неделю её все игнорируют, и когда начинается настоящий пожар, никто не двигается.
Флакающий тест (flaky test) — тот, что проходит и падает случайно, хотя код не менялся. Обычно виноваты тайминг, порядок запуска или скрытая зависимость. Флакающие тесты — яд: когда красный результат может быть просто шумом, люди перестают верить любому красному, и настоящий сбой пропускают со словами «наверное, опять флакает». Тест должен быть детерминированным (deterministic): тот же код — тот же результат, каждый раз. Чини флакающие тесты или удаляй их; набор, которому не доверяешь, не защищает ничего.
- Что это
- Тест, который то проходит, то падает без изменения кода.
- Причины
- Тайминг (sleep, гонки), порядок тестов, скрытая общая зависимость.
- Пример
Тест ждёт 100 мс ответа сервиса; на загруженной CI-машине иногда не дожидается.
- Вред
- Учит команду игнорировать красный — и настоящий сбой проходит мимо.
- Лекарство
Сделать тест deterministic — или удалить. Терпеть флакающий тест нельзя.
- Связи
- Чаще всего живёт в E2E; ломает доверие к CI Gate.
Покрытие показывает, что протестировано, а не насколько хорошо, — не делай его целью. А флакающий тест хуже, чем никакого: раз красный может быть шумом, игнорируют любой красный.
Тестирование — это суждение, а не ритуал. Навык в том, чтобы тестировать важное, в правильных пропорциях, и встроить это в то, как ты выпускаешь код, — чтобы набор заслуживал доверия, а не превращался в отписку.
Risk-based Testing — тестируй рискованное, а не всё
Парашют и тормоза ты проверяешь трижды; подстаканник на прочность не испытываешь. Усилия идут туда, где сбой правда больно бьёт.
Не весь код заслуживает одинакового внимания. Тестирование по рискам (risk-based testing) — это сосредоточить усилия там, где баг был бы дорогим или вероятным: ключевая бизнес-логика, деньги, безопасность, хитрые краевые случаи, части, которые часто меняются. Простому коду с низким риском можно уделить меньше. Гнаться за тестами для всего — значит тратить силы и получить набор такой большой и медленный, что его никто не запускает. Целься в тесты, которые ловят сбои, о которых ты правда пожалел бы, а не в число: покрытие важного лучше покрытия всего.
- Проблема
Сил на тесты мало, а пытаться покрыть всё — значит размазать их тонко.
- Идея
Больше тестов там, где сбой дорогой или вероятный; меньше — на тривиальном.
- Пример
Расчёт платежей и права доступа — плотно; геттеры и простая вёрстка — по минимуму.
- Цена
- Нужно честно оценивать риск, а не тестировать то, что легко.
- Когда брать
- Всегда, когда решаешь, на что тратить время на тесты.
- Связи
Заменяет погоню за Code Coverage; плотнее всего — Unit Test на логике.
CI Gate — тесты на каждом изменении, автоматически
Турникет, который не откроется, пока билет не действителен: проверка встроена в ворота, а не зависит от того, вспомнит ли кто-то посмотреть.
Тесты защищают, только если они правда запускаются. Встрой их в пайплайн (pipeline), чтобы они бежали автоматически на каждом изменении, и блокируй мёрж, если они падают, — это CI-гейт (CI gate) из курса о деплое. Именно это превращает тесты из того, что ты, может быть, запустишь, в гарантию, что ничего сломанного не попадёт в основную ветку. «Без тестов не выпускаем» — стандарт, который стоит держать: непротестированное изменение в продакшене — это ставка, которую ты не обязан был делать.
- Проблема
Тесты есть, но их забывают запускать — и сломанный код уходит в main.
- Идея
CI сам гоняет все тесты на каждом изменении и не пускает мёрж, пока они не зелёные.
- Пример
- Pull request: CI запускает тесты, один упал — кнопка мёржа неактивна.
- Цена
- Время ожидания CI; флакающие тесты здесь бьют больнее всего.
- Когда брать
- В любом проекте, где больше одного изменения в неделю.
- Связи
Запускает весь Test Pyramid; не терпит Flaky Test; ловит Regression до пользователя.
Как всё связано
Оркестр: каждый инструмент важен, но музыка получается только вместе.
Понятия курса работают как одна система. Пирамида задаёт форму набора, TDD его пишет, подделки дают юнит-тестам изоляцию, а CI-гейт гоняет всё на каждом изменении — и это даёт главное: менять код без страха. Против работают флакающие тесты и чрезмерное моканье — они подрывают доверие к зелёному. А покрытие лишь подсказывает, где пробелы.
- Даёт ли он менять уверенно — поймал бы он регрессию завтра?
- Есть ли у него форма пирамиды — много юнит, немного интеграционных, пара e2e?
- Проверяют ли тесты поведение, а не внутреннюю реализацию, которая ломается на рефакторинге?
- Мокаются ли только настоящие внешние зависимости, а не то, что под тестом?
- Быстрый ли он и детерминированный — нет флакающих тестов, которые подтачивают доверие?
- Бежит ли он в CI на каждом изменении, блокируя сломанные мёржи?
- Ты меняешь код уверенно и доверяешь набору поймать то, что сломал.
- Твои тесты образуют пирамиду: быстрые в основании, пара реалистичных сверху.
- Тесты проверяют поведение и переживают рефакторинг, а не ломаются на каждой уборке.
- Набор быстрый и детерминированный, а флакающие тесты ты чинишь или удаляешь.
- Тесты бегут в CI на каждом изменении, а покрытие для тебя — подсказка, а не цель.
Шпаргалка: все 19 понятий
Зачем мы тестируем
- Automated Testавтотест
Код, который проверяет код: ввод → вызов → assertion → pass или fail.
- Regressionрегрессия
Изменение ломает то, что раньше работало, часто далеко от места правки.
Юнит-тесты
- Unit Testюнит-тест
Одна функция в изоляции, за миллисекунды. Их тысячи.
- Arrange–Act–AssertAAA
Подготовь, сделай одно действие, проверь результат.
Интеграция, e2e и пирамида
- Integration Testинтеграционный тест
Несколько настоящих частей вместе — проверка швов.
- End-to-End TestE2E, сквозной
Вся система глазами пользователя. Реалистично, но медленно и хрупко.
- Test Pyramidпирамида тестов
Много юнит, меньше интеграционных, пара e2e. Не рожок мороженого.
Подделки
- Test Doubleтестовый двойник
Подделка вместо медленной, платной или случайной зависимости.
- Stubстаб
Возвращает заготовленный ответ — кормит код данными.
- Mockмок
Запоминает вызовы — проверяет, что побочный эффект случился.
- Over-mockingчрезмерное моканье
Замокано всё — тест зелёный, а настоящая система падает.
TDD и хорошие тесты
- TDDred-green-refactor
Сначала падающий тест, потом минимум кода, потом уборка.
- Test Behaviour, Not Implementationповедение, а не реализация
Проверяй итог через публичный интерфейс — тест переживёт рефакторинг.
- Living Documentationживая документация
Тесты — обещания кодом, которые не устаревают.
Доверие к набору
- Code Coverageпокрытие
Сколько кода выполнилось в тестах. Фонарик для пробелов, не мера качества.
- Goodhart's Lawзакон Гудхарта
Мера, ставшая целью, перестаёт мерить. 100% покрытия → пустые тесты.
- Flaky Testфлакающий тест
Падает случайно. Лекарство — deterministic-тест или удаление.
Применять хорошо
- Risk-based Testingтестирование по рискам
Больше тестов там, где сбой дорогой или вероятный.
- CI GateCI-гейт
Тесты на каждом изменении; красный — мёрж заблокирован.
Хорошее тестирование — это суждение: тестируй рискованное, держи пирамиду, проверяй поведение, а не реализацию, и гоняй всё автоматически — чтобы зелёный набор правда значил «можно менять».