Статьи

Модульное тестирование, часть 3 — интеграционное тестирование

Завершая цикл статей о модульном тестировании, я сосредоточусь на интеграционном тестировании. Напомним, что первая статья была посвящена базовому модульному тестированию в классе POJO . Вторая статья взяла вещи шага futher и внедренное тестирование с использованием фиктивных и заглушками объектов. С этими двумя пунктами разработчик должен иметь возможность писать тесты, которые полностью охватывают написанный код, проверяя ожидаемую функциональность.

Однако при использовании этого подхода не было написано ни одного теста для проверки возможности подключения к реальному источнику данных. Вот где интеграция вступает в игру.

Что такое интеграционное тестирование?

В целях этой статьи интеграционное тестирование относится к случаям, когда тестируемая система / класс требует взаимодействия с внешним элементом. Общие примеры включают в себя:

  • Соединение с базой данных / данные

  • Подключение к сети

  • Внешняя система (например, очередь или почтовый сервер)

  • Чтение / запись файла (ов) или выполнение других операций ввода / вывода

Пример базы данных категории конфет

Продолжая использовать Candy-тему с моими примерами, давайте рассмотрим класс CandyRepository, у которого есть метод для возврата списка категорий Candy. Метод getAllCandyCategories () может выглядеть примерно так, как указано ниже:

public List<CandyCategory> getAllCandyCategories() {
return template.queryForList("SELECT id, name FROM category ORDER BY id ASC");
}

Давайте предположим, что запрос возвращает данные, перечисленные как показано ниже:

Я БЫ имя
0 Камедь
1 Candy Stick
2 Шоколад
3 смолистый
4 труднопроизносимое слово
5 Ретро

Предполагая, что мы хотим убедиться, что список категорий Candy всегда содержит эти элементы, только эти элементы, используя указанные идентификаторы, можно написать тест с использованием JUnit, как показано ниже:

@Test
public void testValidateCandyCategories() {
List<CandyCategory> categoryList = candyRepository.getAllCandyCategories();

assertEquals(categoryList.size(), 6);
assertEquals(categoryList.get(0).getName(), "Gum");
assertEquals(categoryList.get(1).getName(), "Candy Stick");
assertEquals(categoryList.get(2).getName(), "Chocolate");
assertEquals(categoryList.get(3).getName(), "Gummy");
assertEquals(categoryList.get(4).getName(), "Jawbreaker");
assertEquals(categoryList.get(5).getName(), "Retro");
}

Приведенный выше тест подтвердит, что размер списка составляет ровно шесть элементов и что идентификатор соответствует тому, что мы ожидаем. Опять же, это простой пример, иллюстрирующий, как мы можем выполнить тест.

Когда должен срабатывать интеграционный тест?

Обычной практикой является запуск модульных тестов при регистрации, чтобы разработчик имел представление о любых ошибках или проблемах с модульными тестами. Поскольку интеграционные тесты предназначены для проверки возможности подключения / конфигурации на другом уровне, они обычно не запускаются одновременно с модульными тестами. Вместо этого они выполняются как часть процесса слияния или развертывания.

В результате этой позиции в жизненном цикле развертывания можно использовать Maven для выполнения интеграционных тестов с помощью плагина Surefire. Используя наш пример выше, плагин может быть настроен, как показано ниже:

<build>
<plugins>
    <plugin>
        <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.19</version>
            <dependencies>
            <dependency>
               <groupId>org.apache.maven.surefire</groupId>
                    <artifactId>surefire-junit47</artifactId>
                    <version>2.19</version>
                </dependency>
            </dependencies>
            <configuration>
            <groups>sample.CandyRepositoryTest</groups>
            </configuration>
            <executions>
            <execution>
                <goals>
                    <goal>integration-test</goal>
                    </goals>
                    <configuration>
                    <includes>
                        <include>**/*.class</include>
                        </includes>
                    </configuration>
               </execution>
            </executions>
        </plugin>
    </plugins>
</build>

В результате при достижении цели интеграционного теста выходные данные будут выглядеть примерно так, как показано ниже:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running sample.CandyRespositoryTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.085 sec

Results:

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.967 s
[INFO] ------------------------------------------------------------------------

Инструменты непрерывной интеграции, такие как Bamboo или Jenkins, могут дополнительно автоматизировать этот процесс.

Вывод

Эта серия предназначена для общего обзора модульного тестирования, включая введение в интеграционное тестирование. Ключевой момент, который я хотел бы подчеркнуть, заключается в том, чтобы убедиться, что модульные тесты написаны так, чтобы сосредоточиться на тестируемой системе / классе.

Когда для тестов требуются внешние данные, заманчиво (и быстро) вызвать основные классы репозитория, чтобы получить реальные данные из базы данных, но я всегда рекомендую вместо этого использовать фиктивные / тупые данные. Это не значит, что подключение к базе данных (в качестве примера) следует игнорировать, поскольку именно здесь вступают в действие интеграционные тесты.

Хорошего дня!