Уровни автоматизации (test automation levels) - группы автотестов, которые различаются тем, насколько большую часть системы задействует один тест: модульный тест (unit test) проверяет один компонент в изоляции, интеграционный тест (integration test) проверяет взаимодействие компонентов или систем, сквозной тест (end-to-end test) проверяет всю развёрнутую систему через её внешние интерфейсы. Пирамида тестов (test pyramid) - модель распределения автотестов по этим уровням: много маленьких быстрых тестов внизу и мало больших медленных тестов наверху.
Пирамиду предложил Майк Кон (Mike Cohn). По словам Мартина Фаулера, Кон нарисовал её в разговорах с Лизой Криспин в 2003-2004 годах. Опубликовал он её под названием «пирамида автоматизации тестирования» (test automation pyramid) в книге Succeeding with Agile (Addison-Wesley, 2009). Дальше модель распространили заметка Фаулера TestPyramid в его bliki (2012) и статья Хэма Фокке (Ham Vocke) «The Practical Test Pyramid» (2018). Силлабус ISTQB Foundation Level (версия 4.0.1) описывает пирамиду тестов в разделе 5.1.6, а уровни тестирования - в разделе 2.2.1.
Какую задачу решает пирамида тестов
Одно и то же поведение можно проверить тестами разного размера. Правило, по которому просроченная платёжная карта отклоняется, проверяется вызовом одной функции, запросом к платёжному сервису или оплатой просроченной картой в браузере. Все три теста могут найти один и тот же дефект. Они различаются временем выполнения, стоимостью поддержки и тем, насколько точно падение указывает на причину.
У набора тестов, собранного в основном из тестов через пользовательский интерфейс, известные проблемы. Фаулер перечисляет их: такие тесты хрупкие, дорогие в написании и медленные; одно изменение интерфейса может сломать многие из них сразу; они чаще дают недетерминированный результат.
Пирамида тестов отвечает на два вопроса: какие тесты писать и в какой пропорции. Фокке сводит её к двум практическим правилам: писать тесты разной детализации и чем выше уровень, тем меньше на нём тестов.
История
В таблице - основные вехи модели.
| Дата | Событие |
|---|---|
| 2003-2004 | Майк Кон рисует пирамиду в разговорах с Лизой Криспин (по словам Фаулера) |
| 2006 | Джейсон Хаггинс (Jason Huggins) независимо приходит к той же идее (по словам Фаулера) |
| 20 октября 2009 | Выходит книга Майка Кона Succeeding with Agile с пирамидой из модульных, сервисных и UI-тестов |
| Январь 2012 | Алистер Скотт описывает анти-паттерн «рожок мороженого» в тестировании |
| 1 мая 2012 | Мартин Фаулер публикует заметку TestPyramid |
| 6 февраля 2018 | Кент Доддс (Kent C. Dodds) показывает трофей тестирования |
| 26 февраля 2018 | Хэм Фокке публикует «The Practical Test Pyramid» на martinfowler.com |
| 9 мая 2023 | ISTQB выпускает CTFL v4.0 |
| 2024 | CTFL v4.0.1, пирамида тестов в разделе 5.1.6 |
Уровни тестирования и уровни автоматизации
Силлабус ISTQB определяет пять уровней тестирования. Уровни тестирования - это группы активностей тестирования, которые организуются и управляются вместе. В таблице - пять уровней и уровень автоматизации, который обычно им соответствует.
| Уровень тестирования ISTQB | Фокус | Обычный уровень автоматизации |
|---|---|---|
| Компонентное тестирование (модульное) | Компоненты в изоляции | Модульные тесты |
| Тестирование интеграции компонентов | Интерфейсы и взаимодействие компонентов | Интеграционные тесты |
| Системное тестирование | Поведение и возможности всей системы | Сквозные тесты |
| Тестирование системной интеграции | Интерфейсы с другими системами и внешними сервисами | Интеграционные или сквозные тесты |
| Приёмочное тестирование | Валидация и готовность к развёртыванию | Любой уровень |
Уровень тестирования описывает активность процесса тестирования: кто её выполняет, по какому базису, с какими целями. Уровень автоматизации описывает техническое свойство одного автотеста: что он запускает и что заменяет тестовыми двойниками (test doubles). Компонентное тестирование прямо соответствует модульным тестам. Приёмочный тест можно автоматизировать на любом уровне: Фокке отмечает, что проверка функции с точки зрения пользователя не обязана идти через пользовательский интерфейс.
Модульные тесты
Модульный тест вызывает функцию, класс или модуль напрямую, в процессе тестового фреймворка. Медленные или неуправляемые зависимости - база данных, сеть, часы - заменяются тестовыми двойниками. Силлабус отмечает, что для компонентного тестирования часто нужны тестовые обвязки (test harnesses) или фреймворки модульного тестирования и что его обычно выполняют разработчики в своём окружении разработки.
Модульный тест правила «просроченная карта отклоняется» передаёт функции проверки карту с прошедшим сроком действия и проверяет возвращённую ошибку. Ему не нужны ни сервер, ни база данных, ни браузер. Его падение указывает на одну функцию.
Интеграционные тесты
У термина «интеграционный тест» есть как минимум два значения, как отмечает Фаулер. Узкий интеграционный тест (narrow integration test) задействует только ту часть сервиса, которая общается с другим компонентом: базой данных, брокером сообщений, другим сервисом. По охвату он близок к модульному тесту. Широкий интеграционный тест (broad integration test) требует работающих версий всех сервисов и тестового окружения с доступом к сети.
Уровни ISTQB делятся похожим образом. Тестирование интеграции компонентов сильно зависит от стратегии интеграции: снизу вверх, сверху вниз или «большим взрывом» (big-bang). Тестированию системной интеграции нужно тестовое окружение, по возможности похожее на эксплуатационное.
Интеграционный тест платёжного правила отправляет запрос с просроченной картой платёжному сервису, который работает с настоящей базой данных, и проверяет HTTP-статус и код ошибки в ответе. Падение указывает на сервис, схему его базы данных или отображение между ними.
Сквозные тесты
Сквозной тест работает с развёрнутой системой и идёт через внешний интерфейс: пользовательский интерфейс или публичный API. В примере с оплатой тест открывает магазин в браузере, входит в систему, кладёт товары в корзину, платит просроченной картой и проверяет сообщение на экране.
Такой тест ближе всех к тому, как продуктом пользуются. Он же зависит от наибольшего числа вещей сразу: сборки, конфигурации, тестового окружения, тестовых данных, сети и браузера. Как сквозные тесты находят элементы на странице, описано в статье Локаторы в UI-автотестах.
Стоимость и скорость уровней
Силлабус формулирует общее правило: чем выше слой, тем ниже детализация, изоляция и время выполнения теста. Тесты нижнего слоя маленькие, изолированные и быстрые. Тесты верхнего слоя, как правило, медленнее. Таблица сравнивает уровни по факторам, из которых складывается стоимость теста.
| Фактор | Модульный тест | Интеграционный тест | Сквозной тест |
|---|---|---|---|
| Что работает | Код в процессе теста | Сервис с настоящими зависимостями | Вся развёрнутая система |
| Самая медленная операция | Вызовы функций в памяти | Запросы к базе данных, сетевые вызовы | Загрузка страниц, отрисовка, все сервисы |
| Нужное окружение | Никакого | База данных, брокер, контейнеры сервисов | Полное тестовое окружение |
| Локализация падения | Одна функция или класс | Один сервис или интерфейс | Где угодно в системе |
| Причины падения без дефекта | Немного | Окружение, тестовые данные | Окружение, данные, тайминги, изменения интерфейса |
| Чего не может найти | Дефекты взаимодействия | Дефекты полного пользовательского сценария | Немногое; он ближе всех к реальному использованию |
Точные числа зависят от продукта и инфраструктуры; универсальных значений нет. Разница вытекает из самих операций. Вызов функции в памяти занимает намного меньше времени, чем запрос к базе данных по сети, а запрос - меньше, чем загрузка страницы, которая порождает много запросов.
Некоторые инструменты сборки фиксируют эту разницу явно. В Bazel у тестовой цели есть атрибут size со значениями small, medium, large и enormous. Размер задаёт тайм-аут по умолчанию: 1 минута, 5 минут, 15 минут и 60 минут соответственно, а также ресурсы, которые резервируются для локального запуска.
Время выполнения - только часть стоимости. Падение сквозного теста требует разбора: виноваты могут быть продукт, окружение, данные или сам тест. Тесты, которые падают без дефекта в продукте, описаны в статье Нестабильные (flaky) тесты.
Пирамида тестов
Силлабус описывает пирамиду как модель, которая показывает, что тесты могут иметь разную детализацию. Слои представляют группы тестов. Каждый тест нижнего слоя проверяет небольшой фрагмент функциональности, поэтому для разумного покрытия их нужно много. Каждый тест верхнего слоя проверяет большой фрагмент функциональности, поэтому их достаточно немного.
/\
/ \ End-to-end tests: few, slow, broad
/----\
/ \ Integration tests
/--------\
/ \ Unit tests: many, fast, isolated
+------------+Силлабус отмечает, что число и названия слоёв бывают разными. В исходной пирамиде Кона три слоя: модульные тесты, сервисные тесты и UI-тесты. Другой распространённый вариант - модульные (компонентные) тесты, интеграционные тесты (интеграции компонентов) и сквозные тесты.
Средний слой - наименее очевидный. Кон определяет сервис как то, что приложение делает в ответ на входные данные, и сервисные тесты проверяют эти сервисы без пользовательского интерфейса. Фаулер называет такие тесты подкожными (subcutaneous): они действуют через сервисный слой или API приложения непосредственно под пользовательским интерфейсом. Они дают многие преимущества сквозных тестов без сложности пользовательского интерфейса.
Фокке предостерегает от буквального понимания названий слоёв: пирамида упрощена и может вводить в заблуждение. Точных пропорций форма не задаёт.
Анти-паттерн «рожок мороженого»
Рожок мороженого (ice cream cone) - перевёрнутая пирамида тестов: большая часть проверок - ручные тесты и тесты через пользовательский интерфейс, модульных тестов мало. Алистер Скотт (Alister Scott) описал его в своём блоге WatirMelon в январе 2012 года. Он писал, что организации часто строят перевёрнутые пирамиды тестирования. В его собственной пирамиде наверху облако ручных сессионных тестов, и если её перевернуть, получается рожок мороженого.
Заметка Фаулера TestPyramid описывает, как появляется такой набор. Команда автоматизирует тесты инструментами записи и воспроизведения: тестировщик записывает действия в пользовательском интерфейсе, а инструмент воспроизводит их и сравнивает результаты. Фаулер отмечает, что поначалу это работает хорошо. Затем набор растёт на верхнем уровне и ломается при изменениях интерфейса. Результат Фаулер называет рожком мороженого.
Анти-паттерн описывает пропорцию, а не само наличие сквозных тестов. Набор, где поверх множества тестов нижних уровней есть несколько сквозных тестов основных пользовательских сценариев, остаётся пирамидой.
Другие формы
Трофей тестирования (testing trophy) Кент Доддс показал 6 февраля 2018 года и подробно описал в статье от 3 июня 2021 года. В нём четыре слоя снизу вверх: статические проверки (проверка типов и линтеры), модульные тесты, интеграционные тесты и сквозные тесты. Самый широкий слой - интеграционный. Доддс взял идею из совета Гильермо Рауха (Guillermo Rauch) от 10 декабря 2016 года: «Write tests. Not too many. Mostly integration.»
Таблица сравнивает две формы.
| Пирамида тестов | Трофей тестирования | |
|---|---|---|
| Автор, год | Майк Кон, 2009 | Кент Доддс, 2018 |
| Слои | Модульные, сервисные, UI (исходная) | Статические, модульные, интеграционные, сквозные |
| Самый широкий слой | Модульные тесты | Интеграционные тесты |
| Довод | Нижние тесты быстрее и дешевле | Отдача от вложений: уверенность за потраченное время |
| Заявленная область | Общая | Одна кодовая база; Доддс применял её к монолитам |
Обе формы ставят наверх немного сквозных тестов, и обе связывают число тестов с их стоимостью. Различаются они тем, куда уходит основная часть усилий. Доддс объясняет своё согласие с советом Рауха опытом инженера пользовательских интерфейсов и пишет, что сама классификация важна меньше, чем цель.
В обеих моделях значение слова «интеграционный» нужно согласовать внутри команды. Доддс определяет интеграционные тесты как тесты нескольких модулей, работающих вместе, и это ближе к узким интеграционным тестам Фаулера, чем к широким.
Как выбрать уровень для проверки
Обычное правило следует из пирамиды: проверка относится к самому низкому уровню, на котором она может найти свой дефект. Фокке выводит из него два следствия. Если тест верхнего уровня нашёл ошибку, а ни один тест нижнего уровня не упал, не хватает теста нижнего уровня, и его нужно написать. А тест верхнего уровня, который только повторяет проверки нижних уровней, добавляет стоимость, не добавляя уверенности.
Проверке нужен более высокий уровень, когда её дефект существует только там. Связи компонентов, конфигурацию, схему базы данных и поведение настоящего браузера модульный тест проверить не может: именно эти части он заменяет тестовыми двойниками.
Таблица распределяет по уровням проверки одной функции - промокода при оформлении заказа.
| Проверка | Уровень | Почему этот уровень |
|---|---|---|
| Проверяется формат промокода | Модульный | Чистая функция от входных данных |
| Скидка на границах суммы заказа | Модульный | Много входных значений, каждый тест дешёвый |
| Просроченный промокод отклоняется | Модульный | Часы заменены тестовым двойником |
| Заказ со скидкой сохраняется и читается | Интеграционный | Зависит от настоящей схемы базы данных |
| Запрос скидки соответствует согласованному API | Контрактный тест | Два сервиса, каждый проверяется отдельно |
| Пользователь вводит код и видит новую сумму | Сквозной | Весь сценарий через браузер, один тест |
Значения для второй строки выбирает проектирование тестов, описанное в статье Классы эквивалентности и граничные значения. Пирамида говорит только, что такие многочисленные проверки должны быть дешёвыми.
Контрактные тесты между сервисами
Взаимодействие двух сервисов можно проверить, не разворачивая их вместе. Контрактный тест (contract test) проверяет каждую сторону интерфейса отдельно на соответствие общему описанию запросов и ответов. В контрактах, задаваемых потребителями (consumer-driven contracts) - шаблоне, который Иэн Робинсон (Ian Robinson) описал в 2006 году, - потребители формулируют свои ожидания от провайдера, а обязательства провайдера складываются из этих ожиданий.
Pact реализует этот шаблон. Тесты потребителя отправляют настоящие запросы имитации провайдера (mock provider), которую создаёт Pact. Имитация проверяет каждый запрос и возвращает ожидаемый ответ. Когда тесты потребителя проходят, Pact записывает pact-файл с описанием каждого взаимодействия. При проверке провайдера (provider verification) Pact повторяет каждый записанный запрос к настоящему провайдеру и сравнивает его ответ с минимальным ожидаемым ответом.
Consumer tests Provider verification
| |
+-- request --> Pact mock provider +-- recorded request --> real provider
| | | |
| +-- expected response | +-- actual response
| | |
+--> pact file -----------------------+--> compared with expected <---+Проверяются только те части провайдера, которыми пользуются потребители, поэтому неиспользуемое поведение провайдер может менять свободно. Контрактные тесты берут на себя часть работы широких интеграционных тестов без полного тестового окружения. Развёрнутую систему они не проверяют, поэтому несколько сквозных тестов остаются.
Уровни, покрытие и непрерывная интеграция
Покрытие кода измеряют в основном для модульных и интеграционных тестов: их процесс можно инструментировать. Покрытие требований вычисляется по связям требований с тестами и характерно для системных и приёмочных тестов. Эти две меры сравниваются в статье Трассируемость и покрытие требований.
Уровень теста определяет и то, где он может выполняться в конвейере непрерывной интеграции (CI). Модульным тестам окружение не нужно, их можно запускать при каждом изменении. Интеграционным тестам нужны их зависимости, например запущенные в контейнерах на время задания. Сквозным тестам нужна развёрнутая система. Как тесты запускаются по событиям конвейера, выполняются параллельно и попадают в отчёты, описано в статье Автотесты в CI.
Термины
| Термин | Значение |
|---|---|
| Уровень автоматизации (test automation level) | Группа автотестов, которые задействуют часть системы сходного размера |
| Модульный тест (unit test) | Автотест, который проверяет один компонент в изоляции, в процессе теста |
| Интеграционный тест (integration test) | Автотест, который проверяет взаимодействие компонентов или системы с другими системами |
| Сквозной тест (end-to-end test) | Автотест, который проверяет всю развёрнутую систему через пользовательский интерфейс или публичный API |
| Пирамида тестов (test pyramid) | Модель: много маленьких быстрых тестов внизу и мало больших медленных тестов наверху |
| Рожок мороженого (ice cream cone) | Анти-паттерн перевёрнутой пирамиды: в основном ручные и UI-тесты, мало модульных |
| Трофей тестирования (testing trophy) | Модель Кента Доддса со слоями статических, модульных, интеграционных и сквозных проверок, самый широкий - интеграционный |
| Контрактный тест (contract test) | Тест, который проверяет каждую сторону интерфейса отдельно на соответствие общему описанию запросов и ответов |
| Контракт, задаваемый потребителями (consumer-driven contract) | Контракт провайдера, сформированный из ожиданий его потребителей |
Источники
- ISTQB Certified Tester Foundation Level Syllabus v4.0.1, ISTQB, 2024(откроется в новой вкладке) (на английском)
- ISTQB releases Certified Tester Foundation Level v4.0, ISTQB, 2023(откроется в новой вкладке) (на английском)
- Succeeding with Agile: Software Development Using Scrum, Mike Cohn, Addison-Wesley, 2009(откроется в новой вкладке) (на английском)
- The Forgotten Layer of the Test Automation Pyramid, Mike Cohn(откроется в новой вкладке) (на английском)
- TestPyramid, Martin Fowler, 2012(откроется в новой вкладке) (на английском)
- IntegrationTest, Martin Fowler, 2018(откроется в новой вкладке) (на английском)
- The Practical Test Pyramid, Ham Vocke, 2018(откроется в новой вкладке) (на английском)
- Introducing the software testing ice-cream cone (anti-pattern), Alister Scott, 2012(откроется в новой вкладке) (на английском)
- The Testing Trophy and Testing Classifications, Kent C. Dodds, 2021(откроется в новой вкладке) (на английском)
- Consumer-Driven Contracts: A Service Evolution Pattern, Ian Robinson, 2006(откроется в новой вкладке) (на английском)
- Introduction to Pact, Pact documentation(откроется в новой вкладке) (на английском)
- How Pact works, Pact documentation(откроется в новой вкладке) (на английском)
- Common definitions: attributes common to all test rules, Bazel documentation(откроется в новой вкладке) (на английском)