сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Теория тестирования

Уровни тестирования

Уровни и пирамида тестирования

Я проверил один и тот же расчёт корзины тремя способами и замерил каждый. Вызвать функцию напрямую - 1,2 микросекунды. Прогнать через настоящую базу с записью и чтением - 13,9 микросекунды. Дёрнуть работающий сервер по сети - 507 микросекунд. Одна и та же проверка, разница в 422 раза.

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

// Формулировки: «какие уровни тестирования?», «что такое пирамида?», «чем smoke отличается от sanity?».

Замер: сколько стоит каждый уровень

Разберём те три способа. Первый - вызвать функцию расчёта с готовыми числами и сверить ответ: 1,2 микросекунды. Это юнит-тест: проверка одной функции или класса в отрыве от всего остального. Второй - положить корзину в реальную базу, сохранить, прочитать обратно и посчитать: 13,9 микросекунды, в 12 раз дороже. Это интеграционный: проверяется стык кода с базой. Третий - поднять сервер и сходить к нему по сети, как ходит браузер: 507 микросекунд, в 422 раза дороже юнита.

И честная оговорка про третий замер: там был голый сетевой вызов, без браузера и без прогрева базы. Настоящий сценарий «открыл страницу, заполнил форму, нажал кнопку» через браузер стоит уже секунды. То есть в жизни разрыв между низом и верхом больше моего замера, а не меньше.

// Всего уровней четыре, по масштабу проверяемого куска. Юнит - одна функция. Интеграционный - как модули договариваются между собой. Системный - собранный продукт целиком, через интерфейс и сетевые вызовы. Приёмочный - готов ли продукт с точки зрения заказчика и пользователя; сюда же входит UAT (user acceptance testing), когда продукт щупают не тестировщики, а реальные пользователи или заказчик.

юнит-тест
проверка одной функции в отрыве от базы, сети и соседей
E2E
end-to-end, сквозной тест: сценарий целиком - интерфейс, сервер, база

Пирамида и что ловит каждый уровень

Пирамида тестирования - это про пропорции: в основании много быстрых юнит-тестов, посередине меньше интеграционных, наверху единицы сквозных. Основание держит скорость, вершина - уверенность, что всё вместе работает.

Уровни ловят разные дефекты, и это не вопрос вкуса. Ошибку в формуле скидки юнит найдёт за микросекунды и покажет пальцем на строку. А вот то, что два сервиса разошлись в формате даты, юнит не найдёт никогда: он по определению изолирован, соседа ему подменяет заглушка - это подставной объект, который отвечает заранее заготовленным ответом вместо настоящего модуля. Заглушка отвечает так, как её научили, а не так, как ответит живой сервис.

// Обратная беда - перевёрнутая пирамида, когда всё гонят через интерфейс. Набор становится в сотни раз медленнее, дороже в поддержке, и главное - бесполезен для диагностики: сквозной тест упал, а где именно из десяти шагов, ищи сам. Правило простое: лови дефект на самом низком уровне, где его вообще можно поймать.

пирамида тестирования
много быстрых юнитов внизу, единицы сквозных наверху
заглушка (стаб)
подставной объект с заранее заготовленным ответом вместо настоящего модуля

Почему вершина шатается: замер флаки

Я написал тест, который ждёт ответа не дольше 20 миллисекунд, а сервис отвечает то за 5, то за 25 - как повезёт с нагрузкой. Прогнал 300 раз: 68 падений, 23%. Поднял таймаут - то самое предельное ожидание - до 26 миллисекунд, и падений стало 0 из 300. Код не изменился ни на строку, изменилось только ожидание.

Это флаки-тест: тест, который на одном и том же коде то падает, то проходит. Вторая классическая порода - гонка: два потока прибавляют к общему счётчику, каждый читает старое значение и пишет своё. Мой замер: тест «в счётчике должно быть 2» упал 271 раз из 300. Чем выше уровень, тем больше таких источников случайности - сеть, время, общие данные, чужие прогоны рядом.

// И вот почему флаки опаснее обычного падения. Красный тест чинят. Мигающий - перезапускают, а через месяц набор перестают читать вообще: «а, это опять оно». Дальше настоящая поломка уезжает в прод под прикрытием привычного мигания. Лечится не поднятием таймаута, а убиранием случайности: ожидание события вместо сна на удачу, свои данные на прогон, изоляция.

флаки-тест
на неизменном коде то падает, то проходит
гонка
результат зависит от того, кто из потоков успел первым

Регресс, смоук, санити

Эти три слова - не уровни, а назначения проверки, и путают их постоянно. Регрессионное тестирование отвечает на вопрос «то, что работало вчера, работает сегодня?». Оно живёт на всех уровнях и первым уходит в автоматизацию: при релизе раз в неделю руками его ещё можно успеть, при релизе каждый день - уже нет.

Смоук - быстрая проверка «сборка вообще пригодна?»: приложение поднялось, главные экраны открываются, вход в аккаунт проходит, оплата не падает на первом шаге. Широко и мелко, минуты. Санити - точечная проверка «эту конкретную правку сделали?»: узко и глубоко, ровно вокруг изменения.

// Смоук из 400 шагов - это уже не смоук. Смысл смоука в том, чтобы за минуты решить, стоит ли вообще запускать основное тестирование; если он идёт полдня, решение принимается слишком поздно и функцию свою он не выполняет.

регресс
проверка, что новое не сломало работавшее старое
смоук / санити
пригодна ли сборка вообще / сделана ли конкретная правка

Как отвечать: «Что такое пирамида тестирования и зачем она?»

Пирамида - это принцип распределения тестов по уровням: много быстрых юнит-тестов в основании, меньше интеграционных, единицы сквозных наверху. Причина в цене, и её легко померить: я как-то проверял один и тот же расчёт корзины тремя способами - прямой вызов функции занял чуть больше микросекунды, прогон через реальную базу в 12 раз дороже, а поход к серверу по сети в 400 с лишним раз дороже. И это без браузера, с ним разрыв ещё больше. Вторая причина - стабильность: чем выше уровень, тем больше случайности, сеть и тайминги дают мигающие тесты, которые то падают, то нет. Третья - диагностика: юнит показывает пальцем на сломанную функцию, а красный сквозной тест говорит только, что где-то в десяти шагах беда. Поэтому логику проверяют внизу, а наверх поднимают несколько критичных сценариев целиком - оформление заказа, оплата. При этом уровни не взаимозаменяемы: расхождение форматов между двумя сервисами юнит не найдёт в принципе, он изолирован заглушками. Смысл пирамиды - ловить каждый дефект на самом низком уровне, где его можно поймать.

Почему это сильный ответ: пропорции объяснены через измеренную цену уровней, названы три причины (цена, стабильность, диагностика) и добавлено, что уровни ловят разные дефекты, а не дублируют друг друга.

На чём валят

  • Гнать всё через интерфейс: тот же тест дороже юнита в сотни раз и не показывает, где сломалось.
  • Пропустить интеграционный уровень: юниты зелёные, а сервисы разошлись в формате данных.
  • Лечить мигающий тест поднятием таймаута - я так убрал 68 падений из 300, не тронув причину.
  • Смоук на 400 шагов: решение «пригоден ли билд» приходит через полдня, когда оно уже не нужно.
  • Держать регресс только руками при частых релизах - либо не успеваешь, либо режешь глубину.

Проверьте себя

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

  1. #levels1 / 5
    Что проверяет модульное (unit) тестирование и кто его обычно пишет?
    A)Взаимодействие нескольких модулей между собой через их общие интерфейсы и контракты
    B)Отдельный компонент в изоляции; пишут обычно разработчики, с моками зависимостей
    C)Всю систему целиком глазами конечного пользователя по сквозным бизнес-сценариям
    D)Внешний вид интерфейса приложения, который вручную проверяет тестировщик по макету
    показать ответ и разбор
    +B)Отдельный компонент в изоляции; пишут обычно разработчики, с моками зависимостей

    // разбор: Unit-тест проверяет наименьшую тестируемую единицу — функцию, метод, класс — изолированно от остального: внешние зависимости (БД, сеть, другие модули) подменяют моками/стабами, чтобы проверять только логику самого компонента. Пишут их, как правило, разработчики, потому что нужно знание внутренней структуры (белый ящик), и гоняют часто (на каждый коммит в CI) — они быстрые и дешёвые. Unit ловит дефекты логики компонента рано, но не видит проблем на стыках модулей — это уже уровень интеграционного тестирования.

  2. #levels2 / 5
    Что именно проверяет интеграционное тестирование?
    A)Логику одной изолированной функции без обращения к каким-либо внешним модулям и сервисам
    B)Готовность собранного продукта к приёмке со стороны бизнеса и конечного заказчика
    C)Корректность взаимодействия модулей через интерфейсы: передачу данных, вызовы API
    D)Скорость ответа системы под высокой нагрузкой при множестве одновременных пользователей
    показать ответ и разбор
    +C)Корректность взаимодействия модулей через интерфейсы: передачу данных, вызовы API

    // разбор: Интеграционное тестирование проверяет стыки: отдельные модули могут быть исправны по unit-тестам, но при соединении всплывают дефекты интерфейсов — несовпадение форматов данных, неверный контракт API, потеря или искажение данных при передаче, ошибки в порядке вызовов. Проверяют как внутренние интеграции (модуль A ↔ модуль B), так и внешние (сервис ↔ БД, сервис ↔ сторонний API). Именно здесь ловятся баги, которые unit пропускает по определению (он изолирует компонент), а системное увидит слишком поздно и труднее локализует. Стратегии сборки — сверху вниз, снизу вверх, big bang.

  3. #levels3 / 5
    Что отличает системное тестирование от интеграционного?
    A)Системное проверяет один модуль в изоляции, а интеграционное — всю систему целиком под нагрузкой
    B)Между ними нет разницы: оба термина описывают проверку взаимодействия двух соседних модулей
    C)Системное — это проверка производительности, а интеграционное — функций
    D)Всю собранную систему как единое целое против требований, а не отдельные стыки
    показать ответ и разбор
    +D)Всю собранную систему как единое целое против требований, а не отдельные стыки

    // разбор: Системное тестирование берёт продукт целиком, в окружении, близком к боевому, и проверяет его поведение против функциональных и нефункциональных требований — сквозными пользовательскими сценариями от начала до конца. В отличие от интеграционного (которое смотрит на конкретные стыки пары компонентов), системное оценивает продукт как единое целое: проходит ли пользователь весь путь, выполняются ли бизнес-требования, держит ли система нагрузку и безопасность. Обычно это чёрный ящик по требованиям, выполняемый командой QA. Это последний уровень перед приёмкой заказчиком.

  4. #levels4 / 5
    В чём суть приёмочного тестирования (UAT)?
    A)Готовность продукта к передаче глазами заказчика: решает ли он реальную бизнес-задачу
    B)Финальная проверка кода разработчиками на соответствие внутренним стандартам оформления
    C)Проверка взаимодействия модулей системы между собой перед сборкой финального билда продукта
    D)Автоматический прогон всех регрессионных тестов после каждого изменения в исходном коде
    показать ответ и разбор
    +A)Готовность продукта к передаче глазами заказчика: решает ли он реальную бизнес-задачу

    // разбор: Приёмочное тестирование (acceptance, часто UAT — user acceptance testing) отвечает на вопрос «принимаем ли продукт». Его ведут со стороны заказчика или конечных пользователей (а не только QA-команды разработчика), на реальных или приближенных к реальным данных и сценариях, против бизнес-требований и критериев приёмки. Цель — не столько найти технические баги (их ловят на предыдущих уровнях), сколько подтвердить, что продукт делает то, что нужно бизнесу, и готов к выпуску. Это по сути валидация. Провал UAT означает, что даже технически исправный продукт не решает задачу пользователя.

  5. #levels5 / 5
    Чем альфа-тестирование отличается от бета-тестирования?
    A)Альфа проводят внешние пользователи, а бета — внутренние сотрудники компании-разработчика
    B)Альфа — внутри компании-разработчика; бета — реальными внешними пользователями «в поле»
    C)Альфа — это автоматизированные тесты, а бета — ручные проверки перед выпуском продукта
    D)Альфа выполняется после релиза продукта, а бета — на самой ранней стадии проектирования
    показать ответ и разбор
    +B)Альфа — внутри компании-разработчика; бета — реальными внешними пользователями «в поле»

    // разбор: Оба относятся к приёмочному тестированию перед релизом, но отличаются исполнителем и средой. Альфа-тестирование идёт внутри организации-разработчика: продукт гоняют свои сотрудники (не из команды разработки — например, внутренние пользователи, поддержка) в контролируемой среде, имитируя реальное использование. Бета-тестирование выносится наружу: продукт дают ограниченному кругу реальных внешних пользователей, которые применяют его в своих настоящих условиях и на своих данных, присылая обратную связь и баги. Бета ловит проблемы реального окружения и разнообразия конфигураций, которые внутри воспроизвести трудно.

дальше

Теорию прочитали. Навык ставится повторением

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