Целью данной статьи является демонстрация возможности BDD-тестирования веб-сервиса с использованием Docker и JBehave.
Весь исходный код приведен здесь.
Подготовка
Ставим Docker, но до этого — разрешаем использование виртуализации в BIOS, в Windows — доустанавливаем дополнительно Hyper-V, и апдейтим ее до версии, которую просит Docker.
Структура приложения
Приложение представляет собой мультимодульное maven-приложение, со следующими модулями:
- spring-boot-empty-project — собственно spring-boot приложение
- dockerization — модуль, оборачивающий spring-boot приложение в Docker-образ
- jbehave-testing — модуль BDD-тестирования
Немного подробнее:
Сервис
Обычный spring-boot сервис, с контроллером, возвращающим «Hello World!» для домашней страницы:
@RestController
public class Controller {
@RequestMapping("/")
@ResponseBody
String home() {
return "Hello World!";
}
}
Оборачивание сервиса в Docker-образ
Тут используем docker-maven-plugin от Spotify и java:8u111-jre-alpine образ в качестве базового образа. Чтобы приложение стартовало после старта образа — используем секцию entryPoint конфигурации плагина:
<!-- *** Build images *** -->
<execution>
<id>build-spring-boot-simple-app-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
<configuration>
<imageName>jbehave-spring-boot-simple-app-image:${project.version}</imageName>
<baseImage>java:8u111-jre-alpine</baseImage>
<!-- run application when container start -->
<entryPoint>["java", "-jar", "/spring-boot-empty-project-${project.version}.jar"]</entryPoint>
<!-- copy the service's jar file from target into the root directory of the image -->
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}/lib</directory>
<include>spring-boot-empty-project-${project.version}.jar</include>
</resource>
</resources>
</configuration>
</execution>
В той же фазе package тегаем созданный образ:
<!-- *** Tag images *** -->
<execution>
<id>tag-spring-boot-simple-app-image</id>
<phase>package</phase>
<goals>
<goal>tag</goal>
</goals>
<configuration>
<image>jbehave-spring-boot-simple-app-image:${project.version}</image>
<newName>${dockerRepository}/jbehave-spring-boot-simple-app-image:${project.version}</newName>
</configuration>
</execution>
В фазе push пушаем образ в репозиторий:
<!-- *** Push images *** -->
<execution>
<id>push-spring-boot-simple-app-image</id>
<phase>deploy</phase>
<goals>
<goal>push</goal>
</goals>
<configuration>
<imageName>${dockerRepository}/jbehave-spring-boot-simple-app-image:${project.version}</imageName>
</configuration>
</execution>
Пуш образа — операция, занимающая некоторое время, поэтому в ходе обычного билда образ лишь создается и помещается в локальный репозиторий. Но в процессе релиза — он будет также пушнут в ремоутный репозиторий
Собственно BDD-тестирование
Описываем историю в story-файле:
Narrative:
As a developer
I want to get access to spring-boot application from outside of docker container
Scenario: After when Docker container with spring-boot app in it started, application home page is accessable
Then spring-boot application home page accessable
— просто хотим, чтобы домашняя страница была доступна, чего же болей
В соответствии с именем story-файла создаем java-класс с именем GetAccessToSpringBootAppFromOutsideOfDockerContainer:
public class GetAccessToSpringBootAppFromOutsideOfDockerContainer extends JUnitStory {
// Here we specify the configuration, starting from default MostUsefulConfiguration, and changing only what is needed
@Override
public Configuration configuration() {
return new MostUsefulConfiguration()
// where to find the stories
.useStoryLoader(new LoadFromClasspath(this.getClass()))
// CONSOLE and TXT reporting
.useStoryReporterBuilder(new StoryReporterBuilder().withDefaultFormats().withFormats(Format.CONSOLE, Format.TXT));
}
// Here we specify the steps classes
@Override
public InjectableStepsFactory stepsFactory() {
// varargs, can have more that one steps classes
return new InstanceStepsFactory(configuration(), new CheckSpringBootAppHomePageAvailabilitySteps());
}
}
В это классе используется другой класс, специфицирующий шаги тестирования:
public class CheckSpringBootAppHomePageAvailabilitySteps {
@Then("spring-boot application home page accessable")
public void checkExistenceOfNewRecordsInDB() throws IOException {
String baseUrl = System.getProperty("docker.url");
RestTemplate restTemplate = new RestTemplate();
String result =
restTemplate.getForObject(
baseUrl,
String.class
);
assertThat("Default 'Hello World!' string expected", result, is("Hello World!"));
}
}
Чтобы получить docker.url из java-кода, добавляем это в конфигурацию maven-failsafe плагина:
<configuration>
<systemPropertyVariables>
<docker.url>http://${docker.host.address}:${spring-boot-app.port}</docker.url>
</systemPropertyVariables>
</configuration>
Чтобы протестировать приготовленный Docker-образ с сервисом внутри — стартуем образ в фазе pre-integration-test и стопаем в фазе post-integration-test при помощи docker-maven-plugin от io.fabric8:
<!-- Hooking into the lifecycle -->
<executions>
<execution>
<id>start</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
Также задаем таймаут для старта сервиса и префикс TC для отслеживания сообщений от Docker-образа в логах (fabric8 плагин как раз позволяет сделать это):
<!-- ............................................................... -->
<!-- Runtime configuration for starting/stopping/linking containers -->
<!-- ............................................................... -->
<run>
<!-- Assign dynamically mapped ports to maven variables (which can be reused in integration tests) -->
<ports>
<port>spring-boot-app.port:8090</port>
</ports>
<wait>
<!-- Check for this URL to return a 200 return code .... -->
<url>
http://${docker.host.address}:${spring-boot-app.port}
</url>
<!-- ... but at max 10 seconds -->
<time>10000</time>
</wait>
<log>
<prefix>TC</prefix>
<color>cyan</color>
</log>
</run>
При запуске билда видим, что история тестируется успешно:
Running story stories/get_access_to_spring_boot_app_from_outside_of_docker_container.story
(stories/get_access_to_spring_boot_app_from_outside_of_docker_container.story)
Scenario: After when Docker container with spring-boot app in it started, application home page is accessable
...
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
— собственно чего и хотелось бы.
Напоследок вопрос к комьюнити:
Автор: andd3dfx