Основы BDD: Юнит, Интеграционные и End-to-End тесты. В чем разница между интеграционным тестированием и функциональным тестированием

Всего приложения. Но между этими двумя этапами тестирования происходят и другие. Я, как и многие другие, называю такие тесты интеграционными.

Несколько слов о терминологии

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

Поэтому, если их код использует Ajax или localStorage, или IndexedDB и, следовательно, не может быть протестирован с помощью юнит-тестов, они оборачивают этот функционал в интерфейс и мокают этот интерфейс для юнит-тестов, а тестирование реальной реализации интерфейса называют «интеграционным тестом». С этой точки зрения «интеграционный тест» просто тестирует код, который взаимодействует с «реальным миром» вне тех юнитов, которые работают без учета реального мира.

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

Мое эмпирическое правило о том, следует ли использовать реальные реализации Ajax и других операций I/O (ввода-вывода) в интеграционных тестах, заключается в следующем: если вы можете это сделать и тесты все еще выполняются быстро и не ведут себя странно, то проверяйте I/O. Если операция I/O сложная, медленная или просто странная, то используйте в интеграционных тестах mock-объекты.

В нашем калькуляторе, к счастью, единственным реальным I/O является DOM. Нет вызовов Ajax и других причин писать «моки».

Фейковый DOM

Возникает вопрос: нужно ли писать фейковый DOM в интеграционных тестах? Применим моё правило. Использование реального DOM сделает тесты медленными? К сожалению, ответ - «да»: использование реального DOM означает использование реального браузера, что делает тесты медленными и непредсказуемыми.

Мы отделим большую часть кода от DOM или протестируем всё вместе в E2E-тестах? Оба варианта не оптимальны. К счастью, есть третье решение: jsdom . Этот замечательный и удивительный пакет делает именно то, чего от него ждёшь - реализует DOM в NodeJS.

Он работает, он быстр, он запускается в Node. Если вы используете этот инструмент, то можете перестать рассматривать DOM как «I/O». А это очень важно, ведь отделить DOM от фронтенд-кода сложно, если не невозможно. (Например, я не знаю, как сделать это.) Я предполагаю, что jsdom был написан именно для запуска фронтенд-тестов под Node.

Давайте посмотрим, как он работает. Как обычно, есть инициализирующий код и есть тестовый код, но на этот раз мы начнём с тестового. Но перед этим - отступление.

Отступление

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

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

Вернемся к использованию jsdom.

Использование jsdom

const React = require("react") const e = React.createElement const ReactDom = require("react-dom") const CalculatorApp = require("../../lib/calculator-app") ... describe("calculator app component", function () { ... it("should work", function () { ReactDom.render(e(CalculatorApp), document.getElementById("container")) const displayElement = document.querySelector(".display") expect(displayElement.textContent).to.equal("0")

Интересными являются строки с 10 по 14. В строке 10 мы визуализируем компонент CalculatorApp , который (если вы следите за кодом в репозитории) также отображает компоненты Display и Keypad .

Затем мы проверяем, что в строках 12 и 14 элемент в DOM показывает на дисплее калькулятора начальное значение, равное 0.

И этот код, который работает под Node, использует document ! Глобальная переменная document является переменной браузера, но вот она здесь, в NodeJS. Чтобы эти строки работали, требуется очень большой объем кода. Этот очень большой объем кода, который находится в jsdom, является, по сути, полной реализацией всего, что есть в браузере, за вычетом самого рендеринга!

Строка 10, которая вызывает ReactDom для визуализации компонента, также использует document (и window), так как ReactDom часто использует их в своем коде.

Итак, кто создает эти глобальные переменные? Тест - давайте посмотрим на код:

Before(function () { global.document = jsdom(`

`) global.window = document.defaultView }) after(function () { delete global.window delete global.document })

В строке 3 мы создаём простой document , который содержит лишь div .

В строке 4 мы создаём глобальное window для объекта. Это нужно React.

Функция cleanup удалит эти глобальные переменные, и они не будут занимать память.

В идеале переменные document и window должны быть не глобальными. Иначе мы не сможем запустить тесты в параллельном режиме с другими интеграционными тестами, потому что все они будут переписывать глобальные переменные.

К сожалению, они должны быть глобальными - React и ReactDom нуждаются в том, чтобы document и window были именно такими, поскольку вы не можете им их передать.

Обработка событий

А как насчет остальной части теста? Давайте посмотрим:

ReactDom.render(e(CalculatorApp), document.getElementById("container")) const displayElement = document.querySelector(".display") expect(displayElement.textContent).to.equal("0") const digit4Element = document.querySelector(".digit-4") const digit2Element = document.querySelector(".digit-2") const operatorMultiply = document.querySelector(".operator-multiply") const operatorEquals = document.querySelector(".operator-equals") digit4Element.click() digit2Element.click() operatorMultiply.click() digit2Element.click() operatorEquals.click() expect(displayElement.textContent).to.equal("84")

Остальная часть теста проверяет сценарий, в котором пользователь нажимает «42 * 2 =» и должен получить «84».

И он делает это красивым способом - получает элементы, используя известную функцию querySelector , а затем использует click , чтобы щелкнуть по ним. Вы даже можете создать событие и иницировать его вручную, используя что-то вроде:

Var ev = new Event("keyup", ...); document.dispatchEvent(ev);

Но встроенный метод click работает, поэтому мы используем его.

Так просто!

Проницательный заметит, что этот тест проверяет точно то же самое, что и E2E-тест. Это правда, но обратите внимание, что этот тест примерно в 10 раз быстрее и является синхронным по своей природе. Его гораздо проще писать и гораздо легче читать.

А почему, если тесты одинаковы, нужен интеграционный? Ну, просто потому, что это учебный проект, а не настоящий. Два компонента составляют всё приложение, поэтому интеграционные и E2E-тесты делают одно и то же. Но в реальном приложении E2E-тест состоит из сотен модулей, тогда как интеграционные тесты включают в себя несколько, быть может, 10 модулей. Таким образом, в реальном приложении будет около 10 E2E-тестов, но сотни интеграционных тестов.

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

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

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

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

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

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

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

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

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

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

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

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

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


Рис. 20.1.

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

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

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

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

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

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

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

Классы и виды тестов.

Существуют два основных класса тестов: традиционные и нетрадиционные .

Тест обладает составом , целостностью и структурой . Он состоит из:

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

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

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

Традиционный тест представляет собой единство, по меньшей мере, трех систем:

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

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

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

Гомогенные тесты

К традиционным тестам относятся тесты гомогенные и гетерогенные .

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

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



Гетерогенные тесты

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

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

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

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

Интегративные тесты

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

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

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

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

Адаптивные тесты

Целесообразность адаптивного контроля вытекает из необходимости рационализации традиционного тестирования.

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

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

Тестирование обладает следующими преимуществами перед другими методами педагогического контроля:

· повышение скорости проверки качества усвоения знаний и умений учащимися;

· осуществление хотя и поверхностного, но полного охвата всего учебного материала;

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

· высокая объективность и, как следствие, большее позитивное стимулирующее воздействие на познавательную деятельность учащегося;

· ориентированность на современные технические средства, на использование в среде компьютерных обучающих и контролирующих систем;

· возможность математико-статистической обработки результатов контроля, и как следствие, повышение объективности педагогического контроля;

· осуществление принципа индивидуализации и дифференциации обучения благодаря использованию адаптивных тестов;

· возможность увеличить частоту и регулярность контроля за счет уменьшения времени выполнения заданий и автоматизации проверки;

· облегчение процесса интеграции системы образования страны в европейскую.

Тесты можно классифицировать по следующим основаниям:

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

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

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

4.Ориентация на определенный этап контроля : тесты предварительного контроля, тесты текущего контроля, тесты итогового контроля.

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

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

7. Степень гомогенности тестовых заданий : тесты с однородными или разнородными формами построения заданий.

8. Скоростной фактор : скоростные (с обязательным фиксированием времени выполнения) и нескоростные.

9. Форма организации тестирования : массовые, индивидуальные, групповые .

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

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

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

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

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

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

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

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

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

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

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

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

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

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


Рис. 20.1.

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

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

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

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

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

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

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

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

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

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

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

На Рис. 15 приведена структура комплекса программ K, состоящего из оттестированных на этапе модульного тестирования модулей M1, M2, M11, M12, M21, M22. Задача, решаемая методом интеграционного тестирования, - тестирование межмодульных связей, реализующихся при исполнении программного обеспечения комплекса K.

Рис. 15. Пример структуры комплекса программ

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

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

Монолитный, характеризующийся одновременным объединением всех модулей в тестируемый комплекс;

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

В инкрементальном методе выделяют две стратегии добавления модулей:

1) «Сверху вниз» и соответствующее ему восходящее тестирование.

2) «Снизу вверх» и соответственно нисходящее тестирование.

Особенности монолитного тестирования заключаются в следующем: для замены неразработанных к моменту тестирования модулей, кроме самого верхнего (модуль К на Рис. 15), необходимо дополнительно разрабатывать драйверы (test driver) и/или заглушки (stub), замещающие отсутствующие на момент сеанса тестирования модули нижних уровней.

Сравнение монолитного и интегрального подхода дает следующее.

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

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

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

Например, порядок тестирования комплекса K (Рис. 15) при нисходящем тестировании может быть таким, как показано ниже, где тестовый набор, разработанный для модуля Mi, обозначен как XYi = (X, Y)i.

K->XYk
M1->XY1
M11->XY11
M2->XY2
M22->XY22
M21->XY21
M12->XY12

К недостаткам нисходящего тестирования следует отнести:

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

Сложность организации и разработки среды для реализации исполнения модулей в нужной последовательности;

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

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

Например, порядок тестирования комплекса K (Рис. 15) при нисходящем тестировании может быть следующим:

M11->XY11
M12->XY12
M1->XY1
M21->XY21
M2(M21, Stub(M22))->XY2
K(M1, M2(M21, Stub(M22)) ->XYk
M22->XY22
M2->XY2
K->XYk

А к недостаткам восходящего тестирования можно отнести:

Запаздывание проверки концептуальных особенностей тестируемого комплекса;

Необходимость в разработке и использовании драйверов.