Статьи

Тестирование Spring Boot Microservice в Black Box – это так просто

Когда мне нужно было создавать прототипы, проверять концепцию или играть с какой-то новой технологией в свободное время, запуск нового проекта всегда был немного раздражающим барьером для Maven. Должен сказать, что настройка проекта Maven не сложна, и вы можете использовать Maven Archetypes. Но архетипы часто устарели. Кто хочет поиграть со старыми технологиями? Так что я всегда получаю связи, с которыми хотел поиграть. Не очень продуктивно проведенное время.

Но потом пришёл Spring Boot. Я влюбился. За последние несколько месяцев я создал не менее 50 небольших игровых проектов, прототипов с Spring Boot. Также включены это на работе. Это просто идеально подходит для прототипирования, обучения, микросервисов, веб, пакетных, корпоративных, потоков сообщений или приложений командной строки. Вы должны быть динозавром или быть слепым, чтобы не оценить Spring Boot для вашего следующего проекта Spring. И когда вы закончите оценивать это, вы пойдете на это. Я обещаю.

Я чувствую необходимость подчеркнуть, насколько просто тестирование микросервиса Spring Boot в Black Box. Тестирование черного ящика  относится к тестированию без использования артефактов приложения. Такое тестирование можно назвать также интеграционным тестированием. Вы также можете выполнить тестирование производительности или стресс-тестирования, как я собираюсь продемонстрировать.

Spring Boot Microservice — это обычно веб-приложение со встроенным Tomcat. Так что он выполняется как JAR из командной строки. Существует возможность преобразовать проект Spring Boot в артефакт WAR, который можно разместить в общем контейнере сервлетов . Но мы не хотим этого сейчас. Лучше, когда в микросервисе есть свой маленький встроенный контейнер.

Я использовал существующее  руководство Spring REST по обслуживанию в  качестве цели тестирования. Основное внимание уделяется тестированию проекта, поэтому удобно использовать это REST-приложение «Hello World» в качестве примера. Я ожидаю, что эти два общих инструмента установлены и установлены на вашем компьютере:

Поэтому нам нужно скачать исходный код и установить JAR-артефакт в наш локальный репозиторий. Я собираюсь использовать командную строку для загрузки и установки микросервиса. Давайте перейдем в какой-то каталог, куда мы загружаем исходный код. Используйте эти команды:

git clone git@github.com:spring-guides/gs-rest-service.git
cd gs-rest-service/complete
mvn clean install

Если все прошло нормально, артефакт JAR микросервиса Spring Boot теперь установлен в нашем локальном репозитории Maven. В серьезной разработке на Java, скорее всего, он будет установлен в общий репозиторий (например, Artifactory, Nexus,…). Когда наш микросервис установлен, мы можем сосредоточиться на тестировании проекта. Он также основан на Maven и Spring Boot.

Тестирование черного ящика будет осуществляться путем загрузки артефакта из репозитория Maven (независимо от того, локальный он или удаленный). Maven-зависимый плагин может помочь нам в этом:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>compile</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <includeArtifactIds>gs-rest-service</includeArtifactIds>
                <stripVersion>true</stripVersion>
            </configuration>
        </execution>
    </executions>
</plugin>

По target/dependency умолчанию он загружает артефакт микросервиса в  каталог. Как видите, он подключен к  compile фазе жизненного цикла Maven, поэтому загруженный артефакт доступен на  test этапе. Версия артефакта удаляется из информации о версии. Мы используем последнюю версию. Это облегчает использование JAR-артефакта во время тестирования.

Читатели, знакомые с Maven, могут заметить отсутствие версии плагина. Проект, управляемый Spring Boot, унаследован от родительского проекта Maven  spring-boot-starter-parent. Содержит версии основных плагинов Maven. Это один из убежденных аспектов Spring Boot. Мне это нравится, потому что он обеспечивает стабильную матрицу зависимостей. Вы можете изменить версию, если вам нужно.

Когда в нашей файловой системе есть артефакт, мы можем начать тестирование. Нам нужно иметь возможность выполнять JAR-файл из командной строки. Я использовал стандартный Java ProcessBuilder  следующим образом:

public class ProcessExecutor {
    public Process execute(String jarName) throws IOException {
        Process p = null;
        ProcessBuilder pb = new ProcessBuilder("java", "-jar", jarName);
        pb.directory(new File("target/dependency"));
        File log = new File("log");
        pb.redirectErrorStream(true);
        pb.redirectOutput(Redirect.appendTo(log));
        p = pb.start();
        return p;
    }
}

Этот класс выполняет заданный JAR-процесс на основе заданного имени файла. Местоположение жестко запрограммировано в  target/dependency каталог, где  maven-dependency-plugin находится наш артефакт. Стандартный и ошибочный выходы перенаправляются в файл. Следующий класс, необходимый для тестирования, — DTO (Объект передачи данных). Это простой POJO, который будет использоваться для десериализации из JSON. Я использую   проект Lombok для уменьшения стандартного кода, необходимого для геттеров, сеттеров, hashCode и равных.

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Greeting {
    private long id;
    private String content;
}

Сам тест выглядит так:

public class BlackBoxTest {
    private static final String RESOURCE_URL = "http://localhost:8080/greeting";
 
    @Test
    public void contextLoads() throws InterruptedException, IOException {
        Process process = null;
        Greeting actualGreeting = null;
        try {
            process = new ProcessExecutor().execute("gs-rest-service.jar");
 
            RestTemplate restTemplate = new RestTemplate();
            waitForStart(restTemplate);
 
            actualGreeting = restTemplate.getForObject(RESOURCE_URL, Greeting.class);
        } finally {
            process.destroyForcibly();
        }
        Assert.assertEquals(new Greeting(2L, "Hello, World!"), actualGreeting);
    }
 
    private void waitForStart(RestTemplate restTemplate) {
        while (true) {
            try {
                Thread.sleep(500);
                restTemplate.getForObject(RESOURCE_URL, String.class);
                return;
            } catch (Throwable throwable) {
                // ignoring errors
            }
        }
    }
}

Сначала он запускает процесс микросервиса Spring Boot и ждет его запуска. Чтобы проверить, запущен ли микросервис, он отправляет HTTP-запрос на URL-адрес, где он ожидается. Сервис готов к тестированию после первого успешного ответа. Микросервис должен отправить простой приветственный JSON-ответ на запрос HTTP GET. Десериализация из JSON в наш  Greeting DTO проверяется в конце теста.

Исходный код доступен на Github .