Как сделать интеграционное тестирование

Обновлено: 01.07.2024

Интеграционное тестирование (в общем случае) — это вид тестирования, при котором проверяется взаимодействие модулей между собой, а также интеграция подсистем в одну общую систему.

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

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

Основная цель интеграционного тестирования — проверить интерфейсы между модулями.

Важно понимать, что в рамках интеграционного тестирования не проверяются end-to-end бизнес сценарии.

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

Различают два основных уровня интеграционного тестирования:

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

На системном уровне проверяется взаимодействие между разными системами после проведения системного тестирования.

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

Снизу вверх (Bottom Up Integration)

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


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

Сверху вниз (Top Down Integration)

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

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

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

В целом, для проведения хорошего интеграционного тестирования необходимо:

Аннотация: Лекция является второй из трех рассматривающих уровни процесса верификации. Тема данной лекции - процесс интеграционного тестирования, его задачи и цели. Рассматриваются организационные аспекты интеграционного тестирования - структурная и временная классификации методов интеграционного тестирования, планирование интеграционного тестирования. Цель данной лекции: дать представление о процессе интеграционного тестирования, его технической и организационной составляющих

20.1. Задачи и цели интеграционного тестирования

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

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

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

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

20.2. Организация интеграционного тестирования

20.2.1. Структурная классификация методов интеграционного тестирования

Как правило, интеграционное тестирование проводится уже по завершении модульного тестирования для всех интегрируемых модулей. Однако это далеко не всегда так. Существует несколько методов проведения интеграционного тестирования:

  • восходящее тестирование ;
  • монолитное тестирование ;
  • нисходящее тестирование .

Все эти методики основываются на знаниях об архитектуре системы, которая часто изображается в виде структурных диаграмм или диаграмм вызовов функций [10]. Каждый узел на такой диаграмме представляет собой программный модуль, а стрелки между ними представляют собой зависимость по вызовам между модулями. Основное различие методик интеграционного тестирования заключается в направлении движения по этим диаграммам и в широте охвата за одну итерацию.

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

Разработка драйверов и заглушек при восходящем интеграционном тестировании

Однако, у восходящего метода тестирования есть существенный недостаток - необходимость в разработке драйвера и заглушек для модульного тестирования перед проведением интеграционного тестирования и необходимость в разработке драйвера и заглушек при интеграционном тестировании части модулей системы (Рис 20.1)

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

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

Монолитное тестирование имеет ряд серьезных недостатков.

  • Очень трудно выявить источник ошибки (идентифицировать ошибочный фрагмент кода). В большинстве модулей следует предполагать наличие ошибки. Проблема сводится к определению того, какая из ошибок во всех вовлечённых модулях привела к полученному результату. При этом возможно наложение эффектов ошибок. Кроме того, ошибка в одном модуле может блокировать тестирование другого.
  • Трудно организовать исправление ошибок. В результате тестирования тестировщиком фиксируется найденная проблема. Дефект в системе, вызвавший эту проблему, будет устранять разработчик. Поскольку, как правило, тестируемые модули написаны разными людьми, возникает проблема - кто из них является ответственным за поиск устранение дефекта? При такой "коллективной безответственности" скорость устранения дефектов может резко упасть.
  • Процесс тестирования плохо автоматизируется. Преимущество (нет дополнительного программного обеспечения, сопровождающего процесс тестирования) оборачивается недостатком. Каждое внесённое изменение требует повторения всех тестов.

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

Постепенная интеграция модулей при нисходящем методе тестирования

У разных специалистов в области тестирования разные мнения по поводу того, какой из методов более удобен при реальном тестировании программных систем. Йордан доказывает, что нисходящее тестирование наиболее приемлемо в реальных ситуациях [27], а Майерс полагает, что каждый из подходов имеет свои достоинства и недостатки, но в целом восходящий метод лучше [28].

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

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

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

Снимок экрана 2019-06-14 в 15.20.17

Однако важность интеграционного тестирования недооценивать нельзя. Грамотное интеграционное тестирование – один из основных шагов на пути к выпуску надежного продукта.

Что же это за тестирование и как оно проводится?

Первой на ум приходит интеграция продукта с платежными сервисами. Это, безусловно, важный аспект для проверки тестировщиками, но далеко не единственный.

Бизнес сегодня опирается на множество программных решений: вебсайт, системы ERP, CRM, CMS. От интеграции всех систем зависит качество обработки запросов пользователей, скорость предоставления услуг и успешность бизнеса в целом.

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

Интеграционное тестирование: обзор проекта

Заказчик

В a1qa обратился представитель популярного англоязычного журнала. Издание доступно как в печатном виде, так и онлайн. На тестировании онлайн-портала и сфокусировалась команда a1qa.

Задача проекта

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

Для реализации функции подписки и ее управления заказчик использовал следующие программные решения:

Процесс оформления подписки был построен следующим образом:

  1. Подготовка набора данных, создание подписки.
  2. Предоставление пользователю возможности приобретения подписки после внесения персональных и платежных данных.
  3. Обработка заказа третьей стороной, предоставляющей свои услуги клиенту в данной сфере.

Цель клиента

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

Задача тестирования

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

Важно было проверить, как они взаимодействуют между собой, и ответить на вопрос: способна ли вся система решать требуемые задачи?

Стратегия проведения интеграционного тестирования A1QA

  1. Определены ключевые бизнес-процессы, которые должна выполнять система: создание, отмена, приостановка и возобновление подписки, изменение платежной информации для подписки и т.д.
  2. Разработана тестовая документация с учетом всех возможных вариаций. Вариации – различные альтернативные выполнения операций (например, отмена подписки может произойти по желанию заказчика, а может быть произведена автоматически, если платежные данные были отклонены банком), а также различные параметры (например, тип продукта). В документации требовалось учесть проверку того, например, что создание подписки пройдет успешно для всех продуктов в рамках каждого бизнес-процесса.
  3. Проведение тестирования, которое заключалось в пошаговом прохождении каждого бизнес-процесса со стартового компонента (где он был инициирован) через все промежуточные и до финального (или финальных) с проверкой того, что все данные передаются правильно, а ожидаемые события на самом деле случаются.

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

На проведение тестирования интеграции было потрачено порядка 40% всех трудозатрат QA-команды.

Трудности

Большинство трудностей при проведении тестирования интеграции было вызвано недостаточной проработкой требований на начальном этапе проекта. Именно некачественные требования стали причиной множества дефектов и нестабильности системы в целом.

Поясним. Изначально требования выглядели как набор пользовательских историй (User Story) в JIRA и содержали только заголовки без какого-либо пояснения. Создавали их чаще всего разработчики.

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

Автоматизация интеграционного тестирования

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

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

Результаты

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

Подводя итоги

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


В предыдущих статьях мы узнали, как использовать xUnit для написания модульных тестов для нашего класса Validation и как тестировать наш класс Controller с его действиями с помощью библиотеки Moq для изоляции зависимостей.

Подготовка нового проекта для интеграционного тестирования

Сначала необходимо новый проект xUnit с именем EmployeesApp.IntegrationTests для целей интеграционного тестирования.

После создания проекта необходимо переименовать класс UnitTest1.cs в EmployeesControllerIntegrationTests :


Кроме того, необходимо добавить ссылку на основной проект и установить один пакет NuGet , необходимый для целей тестирования:

  • AspNetCore.Mvc.Testing - этот пакет предоставляет TestServer и важный класс WebApplicationFactory , чтобы помочь нам загрузить наше приложение в памяти.
  • Microsoft.EntityFrameworkCore.InMemory - поставщик базы данных в памяти.

Теперь мы можем продолжить.

Создание конфигурации фабрики In-Memory

Давайте создадим новый класс TestingWebAppFactory и изменим его соответствующим образом:

Здесь стоит упомянуть пару вещей. Наш класс реализует класс WebApplicationFactory и переопределяет метод ConfigureWebHost . В этом методе мы удаляем регистрацию EmployeeContext из класса Startup.cs . Затем мы добавляем поддержку базы данных в памяти Entity Framework в контейнер DI через класс ServiceCollection .

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

Далее проверяем, что мы заполняем данные из класса EmployeeContext (те же данные, которые вы вставили в реальную базу данных SQL Server в начале этой серии).

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

Интеграционное тестирование действия Index

В нашем тестовом классе мы можем найти единственный тестовый метод с именем по умолчанию. Но давайте удалим его и начнем с нуля.

Первое, что нам нужно сделать, это реализовать ранее созданный класс TestingWebAppFactory :

Теперь давайте напишем наш первый интеграционный тест:

Мы используем метод GetAsync для вызова действия на маршруте /Employees , которое является действием Index , и возвращаем результат в виде переменной response . С помощью метода EnsureSuccessStatusCode мы проверяем, что свойство IsSuccessStatusCode имеет значение true:


Если значение равно false, это будет означать, что запрос не был успешным, поэтому тест не пройден.


Теперь мы можем продолжить интеграционное тестирование обоих действий Create .

Тестирование действия Create (GET)

Прежде чем продолжить тестирование, давайте откроем файл Create.cshtml из папки Views\Employees и изменим его, изменив тэг h4 :

Теперь мы готовы написать наш тестовый код.

Мы хотим проверить, что когда выполняется действие Create (GET), оно возвращает форму Create:


И это действительно так.

Тестирование действия Create (POST)

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

Даллее, мы сериализуем наш ответ и выполняем проверку утверждения.

Теперь мы можем запустить обозреватель тестов:


Итак, если мы откроем наш контроллер и посмотрим на действие Create (POST), мы увидим атрибут ValidateAntiForgeryToken. Итак, наше действие ожидает, что токен защиты от подделльного запроа будет предоставлен, но мы этого не делаем, поэтому тест не проходит. А пока (как временное решение) мы закомментируем этот атрибут и снова запустим тест:



Теперь тест пройден. Как мы уже говорили, это временное решение. Чтобы настроить токен Anti-Forgery в нашем тестовом коде, нужно выполнить несколько шагов, и в следующей статье мы покажем вам, как это сделать шаг за шагом. Пока оставим ValidateAntiForgeryToken закомментированым.

Тестирование успешного запроса POST

Давайте напишем последний тест в этой статье, в котором мы проверим, что действие Create возвращает представление Index , если запрос POST выполнен успешно:

Далее запустим обозреватель тестов:


Заключение

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

Читайте также: