Объектно-ориентированное программирование выступает за модульность для создания небольших и многократно используемых компонентов. Однако для этого есть и другие причины. В случае среды Spring модульность обеспечивает интеграционное тестирование, возможность тестирования системы или ее частей, включая конфигурацию сборки.
Почему так важно протестировать собранную систему с окончательной конфигурацией? Давайте рассмотрим простой пример — изготовление автомобиля. Модульное тестирование автомобиля было бы сродни тестированию всех болтов и болтов в отдельности, в то время как интеграционное тестирование автомобиля было бы похоже на вождение по кругу. При тестировании только компонентов автомобиля по отдельности продажа собранного автомобиля представляет собой огромный риск, поскольку ничто не гарантирует, что он будет вести себя правильно в реальных условиях.
Теперь, когда мы заявили, что интеграционное тестирование необходимо, чтобы гарантировать адекватный уровень внутреннего качества, пришло время включить интеграционное тестирование со средой Spring. Интеграционное тестирование основано на понятии SUT . Определение SUT — это определение границ между тем, что тестируется, и его зависимостями. Практически во всех случаях для настройки теста потребуется предоставить какой-то двойной тест для каждой требуемой зависимости. Конфигурирование этих тестовых двойников может быть достигнуто только путем модульной конфигурации Spring, чтобы они могли заменять зависимые компоненты, расположенные вне SUT.
Рис. 1 — Пример диаграммы зависимостей бина
Конфигурация Spring DI поставляется в 3 различных вариантах: XML — устаревший способ, автоматическое подключение и новейший JavaConfig. Мы посмотрим, как можно добиться модульности для каждого аромата. Смешанная модуляция DI может быть выведена из каждой отдельной записи.
автоматическое связывание
Автопроводка — это простой способ сборки приложений Spring. Это достигается за счет использования либо @Autowiring
или @Inject
. Давайте быстро рассмотрим автоматическую разводку: поскольку внедрение неявно, нет простого способа модульной конфигурации. Приложения, использующие автоматическую разводку, просто должны перейти на другой вариант DI, чтобы разрешить интеграционное тестирование.
XML
XML является устаревшим способом внедрения зависимостей, но все еще используется. Рассмотрим следующий монолитный XML-файл конфигурации:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource" /> <bean id="productRepository" class="ProductRepository"> <constructor-arg ref="dataSource" /> </bean> <bean id="customerRepository" class="CustomerRepository"> <constructor-arg ref="dataSource" /> </bean> <bean id="orderRepository" class="OrderRepository"> <constructor-arg ref="dataSource" /> </bean> <bean id="orderService" class="OrderService"> <constructor-arg ref="productRepository" index="0" /> <constructor-arg ref="customerRepository" index="1" /> <constructor-arg ref="orderRepository" index="2" /> </bean> </beans>
На этом этапе интеграционное тестирование orderService
не так просто, как должно быть. В частности, нам необходимо:
- Скачать сервер приложений
- Настройте сервер для
jdbc/MyDataSource
источника данных - Развернуть все классы на сервере
- Запустить сервер
- Остановить сервер После теста (ов)
Конечно, все предыдущие задачи должны быть автоматизированы! Хотя это не невозможно благодаря таким инструментам, как Arquillian, это противоречит принципу KISS. Чтобы преодолеть эту проблему и сделать нашу жизнь (а также тестовое обслуживание) проще в процессе, требуются инструменты и дизайн. В части инструментов мы будем использовать локальную базу данных. Обычно такая база данных имеет вид в памяти, например, H2. В части проектирования он требует разделения наших компонентов путем создания двух разных фрагментов конфигурации, один из которых предназначен исключительно для подделки источника данных, а другой — для компонентов, составляющих SUT.
Затем мы будем использовать трюк пути к классу Maven: Maven помещает тестовый путь к классу перед основным путем при выполнении тестов. Таким образом, файлы, найденные в тестовом пути к классам, «переопределят» файлы с одинаковыми именами в основном пути к классам. Давайте создадим два фрагмента конфигурационных файлов:
«Реальный» источник данных JNDI как в монолитной конфигурации
<beans ...> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyDataSource" /> </beans>
Поддельный источник данных
<beans...> <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource"> <property name="driverClassName" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:~/test" /> <property name="username" value="sa" /> <property name="maxActive" value="1" /> </bean> </beans>
-
Обратите внимание, что мы используем объект источника данных Tomcat, для этого требуется
org.apache.tomcat:tomcat-jdbc:jar
библиотека в тестовом пути к классам. Также обратите внимание наmaxActive
собственность. Это отражает максимальное количество подключений к базе данных. Рекомендуется всегда устанавливать его в 1 для тестовых сценариев, чтобы ошибки исчерпания пула соединений можно было проверять как можно раньше.
Окончательный макет выглядит следующим образом:
Рис. 2 — Структура проекта для конфигурации Spring XML Интеграционное тестирование
- JNDI источник данных
- Другие бобы
- Поддельный источник данных
Конечный main-config.xml
файл выглядит так:
<?xml version="1.0" encoding="UTF-8"?> <beans...> <import resource="classpath:datasource-config.xml" /> <!-- other beans go here --> </beans>
Такая структура является основой для включения интеграционного тестирования.
JavaConfig
JavaConfig — это самый последний способ настройки приложений Spring, обеспечивающий безопасность как во время компиляции (как автоматическое подключение), так и явное конфигурирование (как XML).
Вышеуказанные фрагменты источников данных могут быть «переведены» на Java следующим образом:
- «Реальный» источник данных JNDI как в монолитной конфигурации
@Configuration public class DataSourceConfig { @Bean public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("jdbc/MyDataSource"); } }
Поддельный источник данных
public class FakeDataSourceConfig { public DataSource dataSource() { org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource(); dataSource.setDriverClassName("org.h2.Driver"); dataSource.setUrl("jdbc:h2:~/test"); dataSource.setUsername("sa"); dataSource.setMaxActive(1); return dataSource; } }
Однако при использовании JavaConfig возникают две проблемы.
- Невозможно использовать тот же трюк пути к классам при импорте, что и ранее с XML, так как Java запрещает иметь 2 (или более) классов с одинаковым квалифицированным именем, загружаемых одним и тем же загрузчиком классов (как в случае с Maven). Следовательно, фрагменты конфигурации JavaConfig не должны явно импортировать другие фрагменты, но должны оставлять ответственность за сборку фрагмента своим пользователям (приложению или тестам), чтобы имена могли отличаться, например :
@ContextConfiguration(classes = {MainConfig.class, FakeDataSource.class}) public class SimpleDataSourceIntegrationTest extends AbstractTestNGSpringContextTests { @Test public void should_check_something_useful() { // Test goes there } }
@Autowired
аннотации (один из немногих соответствующих его использования).@Configuration public class MainConfig { @Autowired private DataSource dataSource; // Other beans go there. They can use dataSource! }
Резюме
В этой статье я показал, как можно выполнить интеграционное тестирование с поддельным источником данных, модулируя монолитную конфигурацию Spring в различные фрагменты конфигурации, будь то XML или JavaConfig.
Однако сфера тестирования интеграции — с Spring или без, огромна. Если вы хотите пойти дальше, я буду держать разговор по интеграции тестирования в Agile Тур Лондон на октябрь 24 — го и на Java Дни Киева на 17 октября й -18 — й .
Эта статья является сокращенной версией части весенней главы « Интеграционное тестирование из окопов» . Взгляните на это, есть даже бесплатный образец главы!