BDD-тестирование веб-сервиса при помощи JBehave и Docker

в 22:17, , рубрики: bdd, docker, java, тестирование, Тестирование веб-сервисов

Целью данной статьи является демонстрация возможности 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js