Статьи

Тестирование докеризованных баз данных SQL

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

Так что для постоянных тестов использование Docker — действительно хороший подход. Но, как обычно, у этого подхода есть некоторые недостатки.

Во-первых , очевидно, что вам нужно установить Docker на всех машинах, которые должны выполнять тесты, это не большая проблема, но что-то, что нужно учитывать, а также Docker внутри проблемы Docker .

Во-вторых , вам нужно как-то автоматизировать запуск и остановку контейнера.

Третий — контейнеры Docker эфемерны. Это означает, что когда вы запускаете контейнер, в данном случае контейнер с сервером SQL , вам необходимо перенести туда схему базы данных.

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

Первая и вторая проблемы исправлены с помощью Arquillian Cube ( http://arquillian.org/arquillian-cube/ ). Он управляет жизненным циклом контейнеров, автоматически запуская и останавливая их до и после выполнения тестового класса. Также он определяет, когда вы сталкиваетесь с ситуацией DinD, и соответствующим образом настраивает запущенные контейнеры.

Arquillian Cube предлагает три различных способа определения контейнера (ов).

  • Определение файла docker-compose.
  • Определение объекта-контейнера.
  • Использование контейнерного объекта DSL.

Для этого поста используется контейнерный объект DSL. Чтобы определить контейнер, который должен быть запущен до выполнения тестов и остановлен после того, как вам нужно будет написать только следующий фрагмент кода.

1
2
3
@ClassRule
public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6")
                                           .withPortBinding(6379);

В этом случае правило JUnit используется, чтобы определить, какое изображение следует использовать в тесте ( redis: 3.2. 6), и добавить в качестве связующего порта порт Redis (6379).

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

Flyway здесь полезен, поскольку вы можете запустить контейнер Docker и затем применить все миграции к пустой базе данных с помощью Flyway .

Четвертая проблема может быть исправлена ​​с помощью таких инструментов, как DBUnit . iI переводит вашу базу данных в известное состояние между запусками теста, заполняя базу данных известными данными и очищая ее после выполнения теста.

Arquillian интегрируется с обоими этими инструментами ( Flyway и DBUnit ) среди других с его расширением, называемым Arquillian Persistence Extension (aka APE ),

Пример использования APE с DBUnit показан в следующем фрагменте:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Rule
public ArquillianPersistenceRule arquillianPersistenceRule = new ArquillianPersistenceRule();
 
@DbUnit
@ArquillianResource
RdbmsPopulator rdbmsPopulator;
 
// To populate data
rdbmsPopulator.forUri(URI.create("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"))
              .withDriver(Driver.class)
              .withUsername("sa")
              .withPassword("")
              .usingDataSet("heroes.yml")
            .execute();
 
// To clean data
 
rdbmsPopulator.forUri(URI.create("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"))
              .withDriver(Driver.class)
              .withUsername("sa")
              .withPassword("")
              .usingDataSet("heroes.yml")
            .clean();

Вы можете использовать Arquillian runner, как показано в dbunit-ftest-example или как показано в предыдущем фрагменте, используя правило JUnit. Выбор того или другого зависит от ваших требований к тестированию.

Итак, как все сходится в Arquillian, так что вы можете загрузить контейнер Docker с базой данных SQL, такой как PostgreSQL , перед выполнением тестового класса, затем перенести схему SQL и заполнить ее данными, выполнить метод test, а затем очистить всю базу данных так, следующий метод теста находит чистую базу данных и, наконец, после выполнения класса теста контейнер Docker уничтожается?

Давайте посмотрим на это в следующем примере:

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
57
58
59
60
61
62
63
public class FilmLibraryTest {
 
    public static final String DB = "filmlibrary";
    public static final String USERNAME = "postgres";
    public static final String PASSWORD = "postgres";
 
    @ClassRule
    public static ContainerDslRule postgresql = new ContainerDslRule("postgres:9.6.2-alpine")
        .withPortBinding(5432)
        .withEnvironment("POSTGRES_PASSWORD", PASSWORD,
            "POSTGRES_USER", USERNAME,
            "POSTGRES_DB", DB);
 
    @Rule
    public ArquillianPersistenceRule arquillianPersistenceRule = new ArquillianPersistenceRule();
 
    @Flyway
    @ArquillianResource
    RdbmsPopulator flywayRdbmsPopulator;
 
    @DbUnit
    @ArquillianResource
    RdbmsPopulator dbUnitRdbmsPopulator;
 
 
    @Test
    public void should_find_all_hollywood_films() {
 
        final URI jdbcUri = URI.create(
            String.format("jdbc:postgresql://%s:%d/%s", postgresql.getIpAddress(), postgresql.getBindPort(5432), DB));
 
        flywayRdbmsPopulator
            .forUri(jdbcUri)
            .withDriver(Driver.class)
            .withUsername(USERNAME)
            .withPassword(PASSWORD)
            .usingDataSet("db/migration")
            .execute();
 
        dbUnitRdbmsPopulator.forUri(jdbcUri)
            .withDriver(Driver.class)
            .withUsername(USERNAME)
            .withPassword(PASSWORD)
            .usingDataSet("hollywoodfilms.yml")
            .withOptions(options()
                            .schema("hollywood")
                            .build()
            )
            .execute();
 
        // Code under test
 
        flywayRdbmsPopulator
            .forUri(jdbcUri)
            .withDriver(Driver.class)
            .withUsername(USERNAME)
            .withPassword(PASSWORD)
            .usingDataSet("db/migration")
            .clean();
 
    }
 
}
1
2
3
4
5
6
# src/test/resources/
 
films:
 
  - title: Trolls
    release: 08/10/2016
1
2
3
-- src/test/resources/db/migration/
CREATE SCHEMA hollywood;
CREATE TABLE hollywood.films (title text, release text);

Тест не настолько сложен, и он в значительной степени говорит о том, что он делает на каждом этапе. Вы создаете Docker- контейнер с использованием Arquillian Cube DSL , а также настраиваете популяции, просто используя Arquillian APE DSL .

Таким образом, благодаря Arquillian Cube и Arquillian APE вы можете сделать свой тест полностью изолированным от времени выполнения, он будет всегда выполняться с той же версией базы данных PostgreSQL, и каждое выполнение метода тестирования будет изолированным.

Вы можете увидеть полный код на https://github.com/arquillian/arquillian-extension-persistence/tree/2.0.0/arquillian-ape-sql/standalone/dbunit-flyway-ftest