Статьи

Тестирование приложений Spring Data + Spring Boot с помощью Arquillian (часть 2)

В предыдущем посте я писал о том, как тестировать приложение Spring Data с помощью Docker с Arquillian Cube . Тест выглядел так:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT)
@ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class)
public class PingPongSpringBootTest {
 
    @ClassRule
    public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6")
                                                .withPortBinding(6379);
 
    @Autowired
    TestRestTemplate restTemplate;
 
    @Test
    public void should_get_pongs() {
 
        // given
 
        restTemplate.postForObject("/ping", "pong", String.class);
        restTemplate.postForObject("/ping", "pung", String.class);
 
        // when
 
        final List<String> pings = restTemplate.getForObject("/ping", List.class);
 
        // then
 
        assertThat(pings)
            .hasSize(2)
            .containsExactlyInAnyOrder("pong", "pung");
    }
 
    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(),
                "spring.redis.host=" + redis.getIpAddress(),
                "spring.redis.port=" + redis.getBindPort(6379)
            );
        }
    }
 
}

Этот тест просто запускает контейнер Redis , затем заполняет данные, используя restTemplate и метод post , затем выполняет тестируемую логику (тестирование метода GET HTTP ) и, наконец, останавливает контейнер Redis .
Это хорошо, это работает, но есть несколько проблем:

  • Во-первых, мы используем REST API для подготовки набора данных теста. Проблема здесь в том, что тест может завершиться неудачей не из-за сбоя тестируемого кода, а из-за подготовки теста (вставка данных).
  • Во-вторых, если конечная точка POST меняет формат / местоположение, вам нужно помнить об изменении везде в тестах, где она используется.
  • Последнее заключается в том, что каждый тест должен покидать среду, которая была найдена до выполнения, поэтому тест изолирован от всех выполнений. Проблема в том, что для этого нужно удалить предыдущие элементы, вставленные методом POST . Это означает, что нужно добавить метод УДАЛИТЬ HTTP, который не всегда может быть реализован в конечной точке, или он может быть ограничен некоторыми конкретными пользователями, поэтому необходимо иметь дело со специальными средствами аутентификации.

Чтобы избежать этой проблемы, было создано Arquillian Persistence Extension (aka APE ). Это расширение интегрируется с DBUnit и Flyway для баз данных SQL , NoSQLUnit для баз данных без SQL и коллекциями Postman для служб REST, так что вы можете заполнить свой бэкэнд перед тестированием реального варианта использования теста и очистить постоянное хранилище после его выполнения.

Также данные о населении хранятся внутри файла, что означает, что их можно использовать повторно во всех тестах и ​​легко изменять в случае любого обновления схемы.
Давайте посмотрим на пример части 1 поста, но обновляем, чтобы использовать APE .

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT)
@ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class)
public class PingPongSpringBootTest {
 
    @ClassRule
    public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6")
                                                .withPortBinding(6379);
 
    @Rule
    public ArquillianPersistenceRule arquillianPersistenceRule = new ArquillianPersistenceRule();
 
    @Autowired
    TestRestTemplate restTemplate;
 
    @Redis
    @ArquillianResource
    NoSqlPopulator populator;
 
    @Test
    public void should_get_pongs() {
 
        // given
 
        populator.forServer(redis.getIpAddress(), redis.getBindPort(6379))
                 .usingDataSet("pings.json")
                 .execute();
 
        // when
 
        final List<String> pings = restTemplate.getForObject("/ping", List.class);
 
        // then
 
        assertThat(pings)
            .hasSize(2)
            .containsExactlyInAnyOrder("pong", "pung");
    }
 
    @After
    public void clean_database() {
        populator.forServer(redis.getIpAddress(), redis.getBindPort(6379))
            .clean();
    }
 
    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(),
                "spring.redis.host=" + redis.getIpAddress(),
                "spring.redis.port=" + redis.getBindPort(6379)
            );
        }
    }
 
}

И файл ( pings.json ), используемый для заполнения экземпляра Redis данными, выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
{
  "data" : [
    {
      "list" : [
        {
          "key" : "ping",
          "values" : [
            {
              "value" : "pong"
            },
            {
              "value" : "pung"
            }
          ]
        }
      ]
    }
  ]
}
Обратите внимание, что в этом тесте вы заменили вызовы POST на что-то, что непосредственно вставляется в хранилище. Таким образом вы избегаете любых сбоев, которые могут возникнуть в логике вставки (которая не является тестируемой деталью). Наконец, после каждого метода тестирования экземпляр Redis очищается, поэтому другие тесты обнаруживают, что Redis очищен и находится в известном состоянии.
Проект можно найти по адресу https://github.com/arquillian-testing-microservices/pingpongbootredis.