Статьи

Отделение интеграционных тестов от модульных тестов с использованием Maven Failsafe & JUnit @Category

Почему юнит-тесты должны выполняться отдельно от интеграционных тестов

TDD на уровне модульного тестирования довольно прост, так как классы в модульном тестировании либо не имеют сложных зависимостей, либо вы макетируете эти зависимости с помощью среды моделирования (например, Mockito). Тем не менее, TDD быстро становится трудным, когда мы начинаем интеграционное тестирование . Интеграционное тестирование — это, в основном, тестирование компонента с некоторыми или всеми его зависимостями вместо того, чтобы проверять их все. Примерами являются тесты, проходящие через несколько уровней, тесты, которые читают или записывают в базу данных или файловую систему, тесты, которые требуют наличия контейнера сервлета или EJB-контейнера, тесты, которые включают сетевое взаимодействие, веб-службы и т. Д.

Интеграционные тесты обычно бывают хрупкими и / или медленными . Примеры:

  • Тест, который обращается к БД, может потерпеть неудачу не потому, что логика в коде была неправильной, а потому, что БД не работает, URL / имя пользователя / пароль к БД были изменены, или в БД были неправильные данные.
  • Тесты, которые читают или записывают на диск, медленные, и каждый раз, когда вы запускаете тест, файл или база данных должны быть сброшены с правильными данными или содержимым.
  • Упаковка и развертывание в контейнере происходит медленно.
  • Тесты, выполняющие сетевые вызовы, могут завершиться неудачей не потому, что логика в коде неверна, а потому, что сетевой ресурс недоступен или существуют проблемы с самой сетью.

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

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

Использование Maven Failsafe и JUnit @Category для разделения интеграционных тестов

Существует несколько способов разделения интеграционных тестов. По умолчанию Failsafe выбирает любой класс с суффиксом «IT» или «ITCase» или с префиксом «IT». Однако для некоторых сред тестирования также требуются суффиксы или префиксы, что делает использование этого подхода громоздким. Другой подход заключается в размещении интеграционных тестов в отдельном исходном каталоге. Я решил использовать JUnit @Category, так как я также использую Concordion, который требует суффикса в своих тестовых классах.

В оставшейся части этой статьи только документируется, как я реализовал совет из статьи Джона Доубла 2012 года, озаглавленной «Юнит-тесты и интеграционные тесты с категориями Maven и JUnit» . Вы можете найти мой исходный код здесь .

Создание категории JUnit

Создание категории JUnit — это просто создание пустого интерфейса. На самом деле, это все! См. ниже:

1
2
3
4
5
package com.orangeandbronze.test;
 
public interface IntegrationTest {
 
}

Теперь я могу применить этот «маркерный интерфейс» в качестве категории к моим интеграционным тестам — в приведенном ниже примере к SectionDaoTest.

1
2
3
4
5
6
7
import org.junit.experimental.categories.Category;
import com.orangeandbronze.test.IntegrationTest;
 
@Category(IntegrationTest.class)
public class SectionDaoTest extends DaoTest {
    ...
}

Добавление плагинов Surefire и Failsafe

Теперь добавьте плагины Surefire и Failsafe. Мне нужно исключить все тесты, отмеченные IntegrationTest в Surefire (который запускает модульные тесты), и включить все тесты, отмеченные IntegrationTest в Failsafe (который запускает интеграционные тесты). Кроме того, я должен был включить «** / *. Java» или тесты не запускаются, я не знаю почему.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.18.1</version>
  <configuration>
    <excludedGroups>com.orangeandbronze.test.IntegrationTest</excludedGroups>
  </configuration>
</plugin>
<plugin>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>2.18.1</version>
  <configuration>
    <includes>
      <include>**/*.java</include>
    </includes>
    <groups>com.orangeandbronze.test.IntegrationTest</groups>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>integration-test</goal>
        <goal>verify</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Выполнение тестов

Итак, теперь, когда я запускаю mvn test, запускаются только модульные тесты, тогда как когда я запускаю mvn интеграционный тест или mvn verify (я обычно запускаю mvn verify), не только выполняется модульный тест, но и мой проект упаковывается, а затем запуск интеграционных тестов

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