Архитектура среды тестирования на основе моделей


Виды компонентов и их интеграция


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

Верификационная система строится из компонентов различных типов.

  • Собственно, проверяемые компоненты.

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

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

  • Модели поведения (обобщенные контракты).

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

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

  • Модели взаимодействия.

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

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

  • Модели ситуаций.

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

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



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

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




    • Для решения первой задачи можно использовать два подхода.


      • На практике более часто применяется связка из автоматной модели теста и генератора путей по графу переходов автомата. Эта техника положена в основу ModelJUnit и NModel.

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


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


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


    • Адаптеры.

      Адаптеры устраняют возможные расхождения между интерфейсами моделей и моделируемых ими компонентов.

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

      Можно отметить, что во многих случаях небольшие расхождения между модельным и проверяемым интерфейсами не требует написания адаптера, поскольку могут быть устранены указанием библиотечной процедуры преобразования. Это относится к случаям, в которых различия сводятся к отсутствию ряда параметров, перестановке параметров местами или простым преобразованиям типов, например, чисел в строки и обратно. Во всех этих случаях адаптер строится не вручную, а автоматически, лишь по указанию соответствующего преобразования в конфигурационном файле тестовой системы.
    • Заглушки (stubs, test doubles).



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


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


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

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


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


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

      Рисунок 1. Схема построения тестовой системы.

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


      Содержание раздела