Хорошее программное обеспечение всегда включает в себя автоматизированные тесты для обнаружения ошибок во время создания и модификации кода. В этом руководстве интеграционные тесты будут добавлены в приложение Spring Boot , подключающееся к “реальной” базе данных с помощью библиотеки Testcontainers.
Фоны
Интеграционный тест предназначен для проверки взаимодействия нескольких частей приложения. Итак, для приложения Spring Boot было бы здорово проверить запущенное приложение, включая базу данных (например, MySQL, MariaDB или PostgreSQL). Встроенная база данных не была бы оптимальной для этого, так как могут быть существенные различия с реальной базой данных, и поэтому некоторые функции могут не работать или могут быть пропущены ошибки.
Для этой цели существует библиотека Test containers для загрузки образа Docker именно той базы данных , которая используется разработчиком и позже в производстве. Чтобы написать интеграционный тест для нашего приложения, мы сначала добавляем зависимости.
testImplementation('org.testcontainers:junit-jupiter:1.15.0') testImplementation('org.testcontainers:mysql:1.15.0') testImplementation('org.springframework.boot:spring-boot-starter-test')
Зависимости build.gradle
Помимо spring-boot-starter-test
нам нужны org.testcontainers:junit-jupiter
, а также org.test containers
ссылка на используемую нами базу данных. В нашем случае мы выбираем MySQL.
Запуск контекста приложения
Чтобы централизовать повторяющуюся логику наших тестовых классов, мы сначала создаем абстрактный базовый класс , от которого затем наследуются наши ИТ-классы. Первая версия этого класса выглядит следующим образом:
@SpringBootTest(classes = MyAppApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("it") public abstract class BaseIT { @Autowired public TestRestTemplate restTemplate; }
Первая версия BaseIT.java
Spring Boot обеспечивает очень хорошую поддержку интеграционного тестирования с помощью аннотации @SpringBootTest
для загрузки полного контекста приложения . С помощью @ActiveProfiles
мы определяем, что приложение запускается с профилем “это” . При необходимости мы можем использовать этот профиль для активации специальных настроек в нашем коде.
Установив переменную веб-среда
в RANDOM_PORT
приложение будет запущено через случайный (и доступный) порт. Это будет автоматически подхвачено TestRestTemplate при использовании его для отправки вызовов REST API нашего приложения – подробнее об этом позже.
Издевательство над базой данных
Теперь мы хотим настроить базу данных, доступную исключительно для наших тестов. Для этого мы расширяем ЕГО с помощью следующего фрагмента кода:
private static final MySQLContainer mySQLContainer; static { mySQLContainer = (MySQLContainer)(new MySQLContainer("mysql:8.0") .withUsername("testcontainers") .withPassword("Testcontain3rs!") .withReuse(true)); mySQLContainer.start(); } @DynamicPropertySource public static void setDatasourceProperties(final DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", mySQLContainer::getJdbcUrl); registry.add("spring.datasource.password", mySQLContainer::getPassword); registry.add("spring.datasource.username", mySQLContainer::getUsername); }
Расширение нашего базового ИТ-класса с помощью настройки Testcontainers
В статическом разделе настраивается и запускается контейнер MySQL. Строка "mysql:8.0"
определяет используемый образ Docker. В идеале это должно точно соответствовать версия, которая также используется в производстве. С параметром с повторным использованием(true)
контейнер Docker не завершается автоматически в конце тестов, но доступен для повторного использования без более длительного периода ожидания. Иногда контейнер необходимо завершить вручную, чтобы полностью сбросить базу данных.
Чтобы разрешить повторное использование, файл //.test containers.properties
должно быть расширено записью
testcontainers.reuse.enable=true
В разделе после @DynamicPropertySource
мы устанавливаем свойства на связываем контекст нашего приложения с базой данных контейнеров Docker . В зависимости от того, как мы инициализируем нашу схему базы данных – например, с помощью Flyway или Liquibase – это будет автоматически применено во время запуска контекста.
Запуск тестов
Сделав это, мы можем написать наш первый тестовый класс. Давайте предположим, что следующий @RestController
уже существует. Служба, на которую ссылается ссылка, возвращает все записи, существующие в таблице “Test” .
@RestController @RequestMapping(value = "/api/tests", produces = MediaType.APPLICATION_JSON_VALUE) public class TestController { private final TestService testService; @Autowired public TestController(final TestService testService) { this.testService = testService; } @GetMapping public ListgetAllTests() { return testService.findAll(); } }
Тестовый контроллер нашего приложения Spring Boot
Теперь мы можем создать класс Testcontroller
это расширяет наш абстрактный базовый класс. Метод тестирования отправляет запрос GET на существующую конечную точку, используя наш TestRestTemplate
.
public class TestControllerIT extends BaseIT { @Test @Sql({"/data/clearAll.sql", "/data/testData.sql"}) public void getAllTests_success() { final HttpEntityrequest = new HttpEntity<>(null, new HttpHeaders()); final ResponseEntity > response = restTemplate.exchange( "/api/tests", HttpMethod.GET, request, new ParameterizedTypeReference<>() {}); assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(1, response.getBody().size()); assertEquals((long)1000, response.getBody().get(0).getId()); } }
TestControllerIT нашего приложения Spring Boot
Несмотря на то, что наша схема базы данных уже инициализирована, нам все еще не хватает явных тестовых данных, необходимых для нашего теста. Для этого мы используем аннотацию Spring @Sql
, который выполняет два скрипта и , таким образом , переводит нашу базу данных в известное состояние . В нашем случае теперь в тестовой таблице есть ровно одна запись, которую мы ожидаем в результате.
DELETE FROM test;
Удаление всех данных с помощью src/test/resources/data/clear All.sql
INSERT INTO test ( id, test ) VALUES ( 1000, 'Aenean pulvinar...' );
Создайте единую запись таблицы с тестовыми данными.sql
С помощью RestTemplate.exchange(...)
мы отправляем запрос GET в /api/tests
. Функциональность в основном такая же, как мы уже знаем из класса Spring RestTemplate
. Наш определенный запрос не отправляет никаких данных ( null
). Ответ десериализуется до типа List
и в утверждениях мы проверяем, что Http Status
и список соответствуют нашим ожиданиям.
Когда мы запускаем тест в первый раз, нам приходится немного подождать запуска контейнера Docker, в зависимости от нашей среды. Также может быть полезно предварительно кэшировать изображение с помощью docker pull mysql:8.0
, чтобы избежать проблем с загрузкой. Теперь наш тест должен пройти без ошибок.
Вывод
С помощью описанной настройки мы создали способ проверки поведения нашего приложения, включая базу данных с высокого уровня. Благодаря этому у нас есть очень хорошее дополнение к нашим модульным тестам. Test containers также предлагает поддержку других сервисов, таких как RabbitMQ, поэтому мы можем гибко расширять ЕГО по мере необходимости.
В профессиональном плане Boot предлагает возможность активировать тестовые контейнеры. Это инициализирует приложение Spring Boot, включающее описанную настройку, в зависимости от выбранной базы данных. Он также генерирует ИТ-классы и скрипты в соответствии с созданными таблицами и контроллерами.
» Смотрите Характеристики и цены
Дальнейшие чтения
Домашняя страница тестовых контейнеров Официальное руководство по тестированию приложений Spring Boot/| Концентратор Docker для поиска изображений Объяснение @Sql и @SqlMergeMode Где искать .test containers.properties
Оригинал: “https://dev.to/tleipzig/adding-integration-tests-in-spring-boot-with-testcontainers-ck7”