Пирамида тестирования, также часто говорят уровни тестирования, это группировка тестов по уровню детализации и их назначению. Эту абстракцию придумал Майк Кон и описал в книге «Scrum: гибкая разработка ПО» (Succeeding With Agile. Software Development Using Scrum).

Пирамиду разбивают на 4 уровня (снизу вверх), например, по ISTQB (см. wiki):

  • модульное тестирование (юнит);

  • интеграционное тестирование;

  • системное тестирования;

  • приемочное тестирование.

Источник https://numanhanduran.medium.com
Источник https://numanhanduran.medium.com

Но можно встретить варианты, где 3 уровня (см. в блоге semaphore). В этой модели объединяют интеграционный и системный уровни:

  • модульное тестирование (юнит);

  • интеграционное тестирование (включает в себя системное);

  • приемочное тестирование.

Источник https://semaphoreci.com
Источник https://semaphoreci.com

Можно сказать, что разработка ПО - это движение по пирамиде снизу вверх. Важно отметить:

  1. Тест (ручной, на высоких уровнях, или автотест, на низких уровнях), должен быть на том же уровне, что и тестируемый объект. Например, модульный тест (проверяющий функции, классы, объекты и т.п.) должен быть на компонентном уровне. Это неправильно, если на приемочном уровне запускается тест, который проверят минимальную единицу кода.

  2. Тесты уровнем выше не проверяют логику тестов уровнем/уровнями ниже.

  3. Чем выше тесты уровнем, тем они:

  • сложней в реализации, и соответственно, дороже в реализации;

  • важнее для бизнеса и критичней для пользователей;

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

Влияние уровней на продукт, процессы и пользователей. Источник: https://vladislaveremeev.gitbook.io
Влияние уровней на продукт, процессы и пользователей. Источник: https://vladislaveremeev.gitbook.io
Источник https://semaphoreci.com
Источник https://semaphoreci.com

Компонентный уровень

Чаще всего называют юнит тестированием. Реже называют модульным тестированием. На этом уровне тестируют атомарные части кода. Это могут быть классы, функции или методы классов.

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

Юнит тесты находят ошибки на фундаментальных уровнях, их легче разрабатывать и поддерживать. Важное преимущество модульных тестов в том, что они быстрые и при изменении кода позволяют быстро провести регресс (убедиться, что новый код не сломал старые части кода).

Тест на компонентном уровне:

  1. Всегда автоматизируют.

  2. Модульных тестов всегда больше, чем тестов с других уровней.

  3. Юнит тесты выполняются быстрее всех и требуют меньше ресурсов.

  4. Практически всегда компонентные тесты не зависят от других модулей (на то они и юнит тесты) и UI системы.

В 99% разработкой модульных тестов занимается разработчик, при нахождении ошибки на этом уровне не создается баг-репортов. Разработчик находит баг, правит, запускает и проверяет (абстрактно говоря это разработка через тестирование) и так по новой, пока тест не будет пройден успешно.

На модульном уровне разработчик (или автотестер) использует метод белого ящика. Он знает что принимает и отдает минимальная единица кода, и как она работает.

Интеграционный уровень

Проверят взаимосвязь компоненты, которую проверяли на модульном уровне, с другой или другими компонентами, а также интеграцию компоненты с системой (проверка работы с ОС, сервисами и службами, базами данных, железом и т.д.). Часто в английских статьях называют service test или API test.

В случае с интеграционными тестами редко когда требуется наличие UI, чтобы его проверить. Компоненты ПО или системы взаимодействуют с тестируемым модулем с помощью интерфейсов. Тут начинается участие тестирования. Это проверки API, работы сервисов (проверка логов на сервере, записи в БД) и т.п.

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

Отдельно отмечу, что в интеграционном тестировании, выполняются как функциональные (проверка по ТЗ), так и нефункциональные проверки (нагрузка на связку компонент). На этом уровне используется либо серый, либо черный ящик.

В интеграционном тестировании есть 3 основных способа тестирования (представь, что каждый модуль может состоять еще из более мелких частей):

  • Снизу вверх (Bottom Up Integration): все мелкие части модуля собираются в один модуль и тестируются. Далее собираются следующие мелкие модули в один большой и тестируется с предыдущим и т.д. Например, функция публикации фото в соц. профиле состоит из 2 модулей: загрузчик и публикатор. Загрузчик, в свою очередь, состоит из модуля компрессии и отправки на сервер. Публикатор состоит из верификатора (проверяет подлинность) и управления доступом к фотографии. В интеграционном тестировании соберем модули загрузчика и проверим, потом соберем модули публикатора, проверим и протестируем взаимодействие загрузчика и публикатор.

  • Сверху вниз (Top Down Integration): сначала проверяем работу крупных модулей, спускаясь ниже добавляем модули уровнем ниже. На этапе проверки уровней выше данные, необходимые от уровней ниже, симулируются.Например, проверяем работу загрузчика и публикатора. Руками (создаем функцию-заглушку) передаем от загрузчика публикатору фото, которое якобы было обработано компрессором.

  • Большой взрыв ("Big Bang" Integration): собираем все реализованные модули всех уровней, интегрируем в систему и тестируем. Если что-то не работает или недоработали, то фиксим или дорабатываем.

Системный уровень

О системном уровне говорили в интеграционном. Тут отметить только то, что:

  1. Системный уровень проверят взаимодействие тестируемого ПО с системой по функциональным и нефункциональным требованиям.

  2. Важно тестировать на максимально приближенном окружении, которое будет у конечного пользователя.

Тест-кейсы на этом уровне подготавливаются:

  1. По требованиям.

  2. По возможным способам использования ПО.

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

На этом уровне используют черный ящик. Интеграционный уровень позволяет верифицировать требования (проверить соответствие ПО прописанным требованиям).

Приемочное тестирование

Также часто называют E2E тестами (End-2-End) или сквозными. На этом уровне происходит валидация требований (проверка работы ПО в целом, не только по прописанным требованиям, что проверили на системном уровне).

Проверка требований производится на наборе приемочных тестов. Они разрабатываются на основе требований и возможных способах использования ПО.

Отмечу, что приемочные тесты проводят, когда (1) продукт достиг необходимо уровня качества и (2) заказчик ПО ознакомлен с планом приемки (в нем описан набор сценариев и тестов, дата проведения и т.п.).

Приемку проводит либо внутреннее тестирование (необязательно тестировщики) или внешнее тестирование (сам заказчик и необязательно тестировщик).

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

В заключении

Хочу отметить, что переходят от уровня к уровню может приходить понимание то ли мы делаем. Возникают вопросы к требованиям, появляются доработки - это нормально.

Приглашаю читать мой блог в телеграмме "Тестировщик" :)

Дополнительные материалы

  1. The Testing Pyramid: How to Structure Your Test Suite

  2. Автоматизация и пирамида тестов

  3. The Practical Test Pyramid

Комментарии (2)


  1. OlegZH
    21.06.2022 12:06
    +1

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

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

    При таком подходе к делу, система, сначала, создаётся на языке программирования, в котором реализованы средства контроля исполнения (точки наблюдения и логи), а, затем, когда необходимо выпустить релиз системы, созданная имитационная модель системы транслируется в совершенно другой язык программирования. Но и тут может потребоваться возможность перевода реальной функционирующий системы в режим тестирования для выявления неполадок.

    Ну и, конечно, всё это тестирование, сколь бы крутым и комплексным оно ни было, всё-равно упрётся в нетестируемую часть — в операционную систему. И тут нас поджидает неожиданный вопрос: как должна быть устроена операционная система, чтобы обеспечить должное функционирование нашей системы?


  1. alekslynx
    22.06.2022 23:48

    Все зависит от сложности системы и то как на неё посмотреть.

    Компонентой может быть как функция в коде, так и модуль большой монолитной программы. А интеграция может проверять как взаимодействие модулей одной системы, так и взаимодействие двух функций в рамках одного модуля. Поэтому однозначно относить unit тестирование к компонентному не всегда корректно. Также всегда почему то не упоминается про интеграционный уровень после системного, так как на этом этапе возможно тестирование интеграции нескольких систем в рамках одного e2e процесса, и ещё после приёмка в виде Альфа или Бэта тестирования.

    мой вывод: эту модель можно повторять по спирали, но не стоит привязывать к конкретным методам тестирования.