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

  • Правильное подключение контекстов IoC-контейнера Spring.

  • Доступ к данным с помощью JDBC или ORM-инструмента. Это может включать, среди прочего, корректность SQL-инструкций, запросов Hibernate, отображений сущностей JPA и так далее.

Spring Framework предоставляет первоклассную поддержку интеграционного тестирования в модуле spring-test. Имя фактического JAR-файла может включать версию выпуска, а также может быть представлено в длинной форме org.springframework.test, в зависимости от того, откуда вы его получаете (обратитесь к разделу об управлении зависимостями за пояснениями). Данная библиотека включает пакет org.springframework.test, который содержит полезные классы для интеграционного тестирования с участием контейнера Spring. Такое тестирование не зависит от сервера приложений или другой среды развертывания. Такие тесты выполняются медленнее, чем модульные, но гораздо быстрее, чем эквивалентные тесты Selenium или удаленные тесты, которые полагаются на развертывание на сервере приложений.

Поддержка модульного и интеграционного тестирования представлена в виде аннотационно-ориентированного фреймворка Spring TestContext Framework. Фреймворк TestContext не зависит от реально используемой инфраструктуры тестирования, что позволяет инструментировать тесты в различных средах, включая JUnit, TestNG и другие.

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

Поддержка интеграционного тестирования в Spring преследует следующие основные цели:

  • Управление кэшированием IoC-контейнера Spring между тестами.

  • Обеспечение внедрения зависимостей для экземпляров тестового стенда (среды).

  • Обеспечение, соответствующего интеграционному тестированию.

  • Предоставление специфичных для Spring основных классов, которые помогают разработчикам в написании интеграционных тестов.

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

Управление контекстом и кэширование

Spring TestContext Framework обеспечивает последовательную загрузку экземпляров ApplicationContext и WebApplicationContext из Spring, а также кэширование этих контекстов. Поддержка кэширования загруженных контекстов важна, поскольку время начального запуска может стать проблемой – не из-за задержки в самом Spring, а из-за того, что объектам, экземпляры которых создаются контейнером Spring, требуется время на создание. Например, в проекте с 50-100 файлами отображения Hibernate может потребоваться от 10 до 20 секунд для загрузки файлов отображения, и эти затраты перед запуском каждого теста в каждом тестовом стенде приводят к замедлению общего процесса тестирования, что снижает производительность разработчиков.

Тестовые классы обычно объявляют либо массив расположения ресурсов для метаданных конфигурации XML или Groovy – часто в пути классов – либо массив компонентных классов, который используется для конфигурирования приложения. Эти расположения или классы являются такими же или похожими на те, которые указаны в web.xml или других конфигурационных файлах для развёртывания в производственной среде .

По умолчанию после загрузки сконфигурированный ApplicationContext используется повторно для каждого теста. Таким образом, ресурсы на настройку тратятся только единожды для каждого тестового комплекта, а последующее выполнение тестов происходит гораздо быстрее. В данном контексте термин "тестовый комплект" означает все тесты, выполняемые в одной JVM – например, все тесты, выполняемые из сборки Ant, Maven или Gradle для данного проекта или модуля. В маловероятном случае, если тест повреждает контекст приложения и требует перезагрузки (например, при изменении определения бина или состояния объекта приложения), фреймворк TestContext можно настроить на перезагрузку конфигурации и восстановление контекста приложения перед выполнением следующего теста.

Внедрение зависимостей для тестовых стендов

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

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

  • Конфигурация Spring: Главным образом, корректно ли представлено и имеется ли все необходимое, что связано с конфигурацией бина HibernateTitleRepository?

  • Конфигурация файла отображения Hibernate: Все ли правильно отображено и установлены ли правильные настройки отложенной загрузки?

  • Логика репозитория HibernateTitleRepository: Выполняет ли сконфигурированный экземпляр этого класса ожидаемые функции?

Управление транзакциями

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

Фреймворк TestContext решает эту проблему. По умолчанию фреймворк создает и откатывает транзакцию для каждого теста. Можно написать код, способный предполагать существование транзакции. Если вы вызываете проксированные объекты в своих тестах транзакционно, они ведут себя правильно, в соответствии с настроенной транзакционной семантикой. Кроме того, если тестовый метод удаляет содержимое выбранных таблиц во время выполнения управляемой для теста транзакции, то эта транзакция откатывается по умолчанию, а база данных возвращается в состояние, предшествовавшее выполнению теста. Поддержка транзакций предоставляется тесту с помощью бина PlatformTransactionManager, определенного в контексте приложения теста.

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

См. управление транзакциями с помощью фреймворка TestContext (см. ссылку в конце лекции).

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

Spring TestContext Framework предоставляет несколько абстрактных вспомогательных классов, которые упрощают написание интеграционных тестов. Эти основные тестовые классы предоставляют четко определенные перехватчики, подключаемые к инфраструктуре тестирования, а также вспомогательные переменные экземпляров и методы, которые позволяют получить доступ к:

  • ApplicationContext – для выполнения явного поиска бинов или тестирования состояния контекста в целом.

  • JdbcTemplate – для выполнения SQL-инструкций для запросов к базе данных. Можно использовать такие запросы для подтверждения состояния базы данных как до, так и после выполнения кода приложения, связанного с базой данных, и Spring обеспечит, что такие запросы будут выполняться в рамках той же транзакции, что и код приложения. При использовании в сочетании с ORM-инструментом следует избегать ложных срабатываний (см. ссылку в конце лекции).

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

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