Что такое интеграционное тестирование

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

С точки зрения выполнения кода тестирование делится на статическое и динамическое. При статическом код не запускается на выполнение; применяются средства статического анализа — инспекция, рецензирование, разбор кода. Динамическое тестирование предполагает выполнение кода и проверку его поведения. Динамическое тестирование может выполняться на всех четырех уровнях тестовой пирамиды. Интеграционное тестирование относится к динамическому тестированию, поскольку предполагает анализ взаимодействий в ПО.

Юнит-тестирование

На самом низком уровне пирамиды, во время юнит-тестирования, проверяются отдельные модули приложения. Это дает преимущество в том, что в небольшом изолированном модуле (юните) легче найти неполадки, и надежно протестировать весь модуль. Баги, найденные на этом уровне, устраняются быстрее всего и легче всего.

Интеграционное тестирование

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

Интеграционное тестирование
Интеграционное тестирование

Системное тестирование

На третьем уровне выполняется полное (или сквозное) тестирование приложения по разным параметрам, называемое системным. Оно дает возможность валидировать требования перед следующим, финальным уровнем, выполняемым чаще у клиента — приемочным.

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

Четвертый и последний уровень пирамиды. Стейкхолдеры, а главное пользователи принимают продукт, оценивая его на соответствие своим требованиям.

Что такое интеграционное тестирование

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

Почему не обойтись без

  • Модуль может прекрасно функционировать в изолированном виде, но в «местах контакта» с другими модулями возникают проблемы
  • Быстрый пример: возникают несоответствия типов данных при передаче информации между модулями
  • В больших компаниях, где модули пишутся разными разработчиками/командами/отделами, и этих отделов много, интеграционное — важнейший этап.

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

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

  • Некорректная логика. Юниты пишутся разными разработчиками, подходы которых (и даже видение продукта в целом) могут достаточно сильно отличаться. Поэтому, будучи интегрированными, юниты работают неправильно с точки зрения функциональности и юзабельности. Тестирование интеграции «выравнивает» модули по единому образцу.
  • Изменились требования. Клиенты могут обновлять требования достаточно часто. Модификация кода даже одного модуля, чтобы приспособить его к новым требованиям, может повлиять на весь компонент и приложение в целом. Интеграционное тестирование помогает определить и быстро «закрыть» такой дефект (и позже вернуться к проблемному модулю).
  • Ошибки данных, чаще всего их формата, при передаче между модулями. Данные некорректно отдаются/принимаются.
  • Сторонние (3rd-party) сервисы и API могут получать некорректные внешние данные и соответственно выдавать некорректные ответы.
  • Неправильная обработка ошибок, отсутствие полных исчерпывающих сценариев обработки ошибок.
  • Внешние hardware-интерфейсы, то есть их аппаратная и программная несовместимость, тоже может определяться интеграционным тестированием.

Еще полезности интеграционного тестирования

  • Главная польза, как уже говорилось — устранение возможных «конфликтов между модулями»
  • Обеспечение правильной работы интегрированных модулей для перехода на этап выше, к системному тестированию
  • Баги, найденные интеграционным тестированием, легче устранить, чем на поздних этапах
  • Улучшает тестовое покрытие и общую надежность

Сложности

  • Возникают по самой специфике интеграционного тестирования, из-за множества модулей и связей между ними
  • Довольно сложное по сравнению с системным, при котором приложение может, в принципе, рассматриваться как черный ящик в целом
  • Требует много времени и ресурсов
  • Нужны так называемые стабы и драйверы, которые при некорректном использовании усугубляют проблемы

Этапы интеграционного тестирования

Этапы интеграционного тестирования
  • Подготовить план интеграционного тестирования
  • Создать тест-кейсы, сценарии, use-кейсы
  • Выполнить тесты
  • Собрать баги и передать чтобы устранили
  • Повторно проверить продукт

Пример

Например есть приложение для просмотра стримов. Оно, по требованиям, должно делать такое:

  • Регистрация/Вход
  • Показ окна с подписками на месяц/год
  • А также индивидуальные варианты подписки
  • Собственно, просмотр стримов

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

Понятно, что любая ошибка в этом user flow приведет к проблемам. Поэтому, после завершения тестирования всех отдельных модулей регистрации/подписки/оформления, нужно провести интеграционное тестирование этих модулей — таким образом «связав» их вместе, проверив интерфейсы между ними. 

Типы интеграционного тестирования

Подходы к интеграционному тестированию
Интеграционное тестирование — Типы

Тестирование Большого Взрыва

В этом подходе готовые модули интегрируются все вместе, одномоментно, почему и называется «Большим Взрывом». После этого проводится тестирование интегрированного «сверх-модуля». Этот тип отличается от системного тестирования тем, что в ТБВ делается упор на проверке интерфейсов/коммуникации между модулями.

Big Bang Approach
Подход «Большой Взрыв»

Преимущество: вполне подходит для небольших проектов, в которых все модули (их сравнительно мало) уже готовы к ТБВ.

Недостаток: сложно провести в крупных проектах, поскольку невозможно (или непрактично) завершить все модули к моменту «большого взрыва».

Сверху вниз (нисходящее)

Top Bottom Approach
Нисходящий подход

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

Стабы (также заглушки) — макеты модулей, симулирующие их функциональность; такой макет принимает нужные параметры и отдает нужные результаты. Стабы имеют hard-coded (фиксированный) ввод и вывод, что помогает тестировать другие модули, интегрированные с тем который заменен стабом.

Плюсы: не нужно ждать пока все модули будут готовы. Можно приоритизировать сначала критически важные модули.

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

Снизу вверх (восходящее)

Bottom Up Approach
Восходящий подход

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

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

Преимущества: экономия времени

Недостаток: высокоуровневые модули, тестируемые на поздних этапах, могут содержать баги, пропущенные из-за спешки.

Гибридное тестирование («сэндвич»)

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

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

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

Инструменты

Существуют как платные, так и неплохие бесплатные с открытым кодом.

Rational Integration Tester от IBM

Дающий codeless-окружение для интеграционных тестов

Сайт

TESSY

Автоматизация юнит- и интеграционного тестирования embedded-софта на С и С++

Сайт

LDRA

Юнит-, интеграционные и системные тесты. Серьезный инструмент для серьезного тестирования (аэрокосмическая промышленность, медицинские приборы, автомобили, в общем корпоративный)

Сайт

Protractor

Бесплатный открытый фреймворк для Angular и AngularJS

Сайт

Citrus Integration Testing 

Открытый фреймворк для автоматизированных интеграционных тестов, поддерживает HTTP REST, TCP/IP, SOAP, FTP, XML, JSON

Сайт

Steam

Открытый фреймворк с возможностями headless-интеграционных тестов

Сайт

Jasmine

Открытый, под BDD-тестирование на JavaScript

Сайт

Системное и интеграционное тестирование — чем отличается

Системное тестированиеИнтеграционное тестирование
Система проверяется вся полностью. Тестировщик рассматривает ее как единое целоеПроверяется взаимодействие между компонентами системы, интерфейсы между ними
Выполняется после интеграционного и перед приемочнымВыполняется после модульного и перед системным и приемочным
Может быть направлено как на функциональные, так и нефункциональные характеристики системы (включая юзабельность, производительность и пр.)Как правило ограничено сугубо функциональными характеристиками (компонентов и интерфейсов) системы, будучи сравнительно низкоуровневым типом тестирования
В основном подход черного ящика — большинство ошибок, которые ищут по методу белого ящика, к тому времени уже найдены и устраненыДля тестирования интерфейсов/коммуникаций нужно хотя бы частичное знание кода, поэтому применяется подход белого и серого ящика, с разбором кода компонентов и связующих интерфейсов
Существует множество типов тестирования, при котором приложение (сайт) рассматривается как единая система (до сорока): тестирование производительности, юзабельности, нагрузочное, стабильности, масштабируемости, безопасности, инсталляции и деинсталляции и так далееСуществует не так много подтипов интеграционного тестирования: всего четыре (перечислены выше)

Типичные ошибки при интеграционном тестировании

  1. Злоупотребление негативными сценариями

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

С интеграционным тестированием это так не работает. Многие считают большой ошибкой включение негативных сценариев. Из-за того что оно обычно предполагает сложную настройку, создание и отработку фикстур, поддержку конфигураций, что значительно удлиняет цикл. Для негативных сценариев требуется создание сложных кастомных стабов/моков/драйверов, которые нужно будет обновлять после изменений кода. Все это приводит к нестабильности, ненадежности интеграционных тестов. Желательно только позитивные сценарии, с четкими условиями ввода пользователя. Плюс обработка ошибок должна быть на уровне юнит-тестов.

  1. Попытки быстро покрыть все сценарии в одном (нескольких) интеграционных тестах

Покрыть все сценарии буквально несколькими интеграционными тестами — часто непрактично, потому что: 

  • Интеграционные тесты сложнее и выполняются дольше чем модульные — предполагается больше уровней доступа, в том числе к базам данных, файловой системе, сторонним API
  • Сложные фикстуры и конфигурации, как уже говорилось выше, и на их выполнение больше времени
  • Обработка мелких ошибок должна по возможности уже быть проведена при юнит-тестировании
  1. Неподготовленные данные

Данные в БД приложения бывают не всегда готовы к тестированию. Они могли измениться, особенно если к БД и тестовому окружению есть доступ у многих разработчиков/QA. Это делает результаты тестирования менее надежными. Лучше потратить чуть больше времени на подготовку тестовых данных до начала интеграционного. А потом после его завершения, их нужно проверить/почистить/удалить, чтобы не влияли на будущие тесты.

  1. Интеграционные тесты не включены в CI/CD

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

  1. Не автоматизируются сложные сценарии

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

Лучшие практики

  1. Тесты независимы друг от друга

Полезной практикой является независимость интеграционных тестов; то есть результат условного теста 1 не должен влиять на результат теста 2; все нужные данные и ресурсы (конфигурационные файлы, БД, переменные окружения) должны содержаться в самом тесте. Тогда интеграция будет надежной. В противном случае зависимость от любых внешних ресурсов может вызвать неожиданное/ошибочное поведение, при любом изменении.

  1. Определить стратегию и подходы до начала

Особенно важно для инкрементального интеграционного тестирования — нужно тщательно изучить систему.

  • Понять дизайн и архитектуру приложения и определить критически важные модули
  • И в зависимости от подхода начать с приоритетных модулей
  • Теснее сотрудничать с разработчиками и стейкхолдерами, определяя требования (какие функции должны быть протестированы, какие модули и компоненты приоритетны, какие конфигурации и тестовые данные понадобятся и т.п.)
  1. Определить и подготовить «заглушки»

Непрерывная интеграция (CI) подразумевает, что QA-команды тестируют функции немедленно, обеспечивая быстрый фидбек; но иногда некоторые модули еще недоступны, или их тестировать в данный момент нецелесообразно. В таких случаях QA-инженеры создают «заглушки» различных типов, заменяющие функции отсутствующих модулей. Делая это заблаговременно, можно обеспечить быстрый проход интеграции. 

  1. Валидировать целостность данных при передаче между компонентами

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

Чтобы обеспечить целостность, необходимо задать некую «базовую линию» по данным в каждом компоненте/системе, сохраняя их изначальные значения. После завершения интеграции всей системы новые значения сравниваются с «базовой линией», таким образом определяя причины проблем с данными; этот процесс можно автоматизировать.

Моки и стабы в интеграционном тестировании

Интеграционные тесты, как мы уже знаем, находятся на среднем уровне пирамиды уровней тестирования, с точки зрения времени и ресурсов:

Пирамида тестирования и место интеграционных тестов в ней
Пирамида тестирования и место интеграционных тестов в ней

Что такое мок

Мок (mock) — это «рабочая копия» сервиса, применяемая вместо него, чтобы выполнять интеграционные тесты быстрее и надежнее. Моки применяются, когда тестируемый компонент взаимодействует скорее со свойствами (внешнего) объекта, чем с его функцией/поведением.

Что такое стаб

Стабы (stub), как и моки, создаются как замена реального объекта, но стаб имитирует объект ограниченно, только его поведение, а не весь объект (отсюда англоязычное название термина, «пень»). Стабы применяются, когда тестируемый компонент взаимодействует только с определенным поведением (внешнего) объекта.

На примере

Интеграционное тестирование проверяет взаимодействие между компонентами (сервисами внутри приложения). Один из подходов состоит в вынесении вовне всех зависимых сервисов и запуске в тестовом окружении. Что не очень правильно, ибо создает потенциальные точки отказов со стороны неконтролируемых сервисов, вообще усложняет и замедляет процесс. Гораздо быстрее (хотя не всегда проще) применять в интеграционном тестировании моки и/или стабы.

Следует отметить, что применение моков/стабов в интеграционном тестировании отличается от применения в юнит-тестировании. В интеграционном проверяется только имплементация и функциональность которую можно проконтролировать. Сначала находят первоочередные интеграции, которые надо проверить, далее смотрят, где можно применить моки/стабы.

К примеру, нам нужно проверить, как код приложения взаимодействует с GitHub API. поскольку обычный разработчик/тестировщик не может повлиять на то как GitHub API отвечает на запросы, можно это не тестировать. То есть вставить мок вместо ответа GitHub API, что позволит выделить больше времени на тестирование внутренних интеграций в своем коде.

@unittest.mock.patch('Github')
def test_parsed_content_from_git(self, mocked_git):
   expected_decoded_content = "b'# Sample Hello World\n\n> How to run this app\n\n- installation\n\n dependencies\n"
   mocked_git.get_repo.return_value = expected_decoded_content

   parsed_content = read_parse_from content(repo='my/repo',
                                            file_to_read='README.md')

   self.assertEqual(parsed_content['titles'], ['Sample Hello World'])

В этом коде метод read_parse_from_content интегрирован с классом, обрабатывающим JSON-объект из вызова GitHub API. В этом тесте мы тестируем интеграцию между двумя классами.

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

Интеграционное без мока
Интеграционное без мока
Интеграционное - мок
Интеграционное — мок

Видео

Интеграционное тестирование на фронтенде в Яндексе:

Быстрый туториал от Edureka на английском:


Сообразительный QA-джуниор — это будущий системный аналитик

Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас