Встроенные Postgres для тестов загрузки Spring

Я создаю приложение Spring Boot при поддержке Postgres, используя Flyway для миграции базы данных. Я сталкивался с проблемами, когда я не могу произвести миграцию, которая генерирует желаемый результат как в Postgres, так и во встроенной базе данных модульного тестирования (даже при включенном режиме совместимости с Postgres). Итак, я собираюсь использовать встроенный Postgres для модульных тестов.

Я наткнулся на встроенную реализацию postgres, которая выглядит многообещающей, но на самом деле не понимаю, как настроить его для работы только в среде модульного тестирования Spring Boot (для тестирования репозиториев Spring Data). Как это настроить с помощью упомянутого инструмента или альтернативной встроенной версии Postgres?


person SingleShot    schedule 23.02.2018    source источник
comment
Почему бы вам просто не использовать свою фактическую базу данных Postgres, ту, которую вы используете в производстве, и ту, с которой, таким образом, вы хотите, чтобы ваш код работал?   -  person JB Nizet    schedule 24.02.2018
comment
Да, есть и другие варианты, но мы предпочитаем, чтобы модульные тесты, такие как @DataJpaTest, можно было запускать без установки базы данных на локальном компьютере.   -  person SingleShot    schedule 26.02.2018
comment
@JBNizet Основная причина - конвейеры CI / CD. Когда вы запускаете тесты как часть конвейера CI / CD, вы обычно находитесь в изолированной среде и не можете или не должны получать доступ к внешним ресурсам. Кроме того, базы данных могут иметь протоколы безопасности, которые вы не хотите внедрять в контейнеры конвейера CI. Есть еще много причин, но это самая веская.   -  person aeskreis    schedule 25.11.2020


Ответы (5)


Я являюсь автором библиотеки embedded-database-spring-test, которая был упомянут @MartinVolejnik. Думаю, библиотека должна отвечать всем вашим потребностям (PostgreSQL + Spring Boot + Flyway + интеграционное тестирование). Мне очень жаль, что у вас возникли проблемы, поэтому я создал простое демонстрационное приложение , демонстрирующий использование библиотеки вместе со средой Spring Boot. Ниже я кратко изложил некоторые основные шаги, которые вам нужно сделать.

Конфигурация Maven

Добавьте следующую зависимость maven:

<dependency>
    <groupId>io.zonky.test</groupId>
    <artifactId>embedded-database-spring-test</artifactId>
    <version>2.0.1</version>
    <scope>test</scope>
</dependency>

Конфигурация пролетного пути

Добавьте следующее свойство в конфигурацию вашего приложения:

# Sets the schemas managed by Flyway -> change the xxx value to the name of your schema
# flyway.schemas=xxx // for spring boot 1.x.x
spring.flyway.schemas=xxx // for spring boot 2.x.x

Кроме того, убедитесь, что вы не используете org.flywaydb.test.junit.FlywayTestExecutionListener. Поскольку у библиотеки есть собственный прослушиватель выполнения теста, который может оптимизировать инициализацию базы данных, и эта оптимизация не имеет никакого эффекта, если применяется FlywayTestExecutionListener.

Пример

Пример тестового класса, демонстрирующего использование встроенной базы данных:

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureEmbeddedDatabase
public class SpringDataJpaAnnotationTest {

    @Autowired
    private PersonRepository personRepository;

    @Test
    public void testEmbeddedDatabase() {
        Optional<Person> personOptional = personRepository.findById(1L);

        assertThat(personOptional).hasValueSatisfying(person -> {
            assertThat(person.getId()).isNotNull();
            assertThat(person.getFirstName()).isEqualTo("Dave");
            assertThat(person.getLastName()).isEqualTo("Syer");
        });
    }
}
person Tomáš Vaněk    schedule 27.02.2018
comment
Спасибо. Все мои тесты проходят! Хотя у меня в журналах много ошибок: Failed to create a Non-Pooling DataSource from PostgreSQL JDBC Driver 42.1.1 for postgres at jdbc:postgresql://localhost:54436/postgres: org.postgresql.util.PSQLException: Connection to localhost:54436 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. FATAL: the database system is starting up И все же работает. - person SingleShot; 27.02.2018
comment
@SingleShot Похоже, это вызвано версией драйвера postgres jdbc. Вы используете версию 42.1.1, но otj-pg-embedded:0.9.0 компонент, от которого мы зависим, скомпилирован с версией 9.4.1208. Вероятно, существует некоторая несовместимость между этими двумя версиями драйверов jdbc, которая приводит к описанным ошибкам. - person Tomáš Vaněk; 27.02.2018
comment
К сожалению, самый последний стабильный выпуск компонента otj-pg-embedded все еще использует драйвер jdbc версии 9.4.1208. Первой версией, которая использует 42.1.x драйвер postgres, является otj-pg-embedded:0.11.1, но она зависит от промежуточной версии Spring Boot и не является частью центрального репозитория maven. - person Tomáš Vaněk; 27.02.2018
comment
Поэтому единственное решение - понизить версию драйвера postgres или дождаться новой версии otj-pg-embedded компонента, которая будет совместима с более новыми версиями драйверов postgres. - person Tomáš Vaněk; 27.02.2018
comment
Я согласен с этим. Мне действительно пришлось использовать 9.4.1212, чтобы удалить ошибки. Но это здорово. Спасибо за помощь! - person SingleShot; 28.02.2018
comment
Поддерживает ли библиотека Liquibase? - person naXa; 06.05.2019
comment
Да. Если в контексте Spring нет компонента Flyway, создается пустая база данных, и эта база данных может быть инициализирована любым инструментом, включая Liquibase. Единственная отсутствующая функция - это возможность вернуть базу данных в состояние по умолчанию, но это предусмотрено планом. - person Tomáš Vaněk; 07.05.2019
comment
@ TomášVaněk ваша библиотека отлично работает с Docker (свойства zonky / test / database / postgres / docker / image). Но при отладке я не знаю, как начать тестирование с уже созданным образом базы данных. Не могли бы вы дать мне информацию об этом? Я надеюсь иметь флаг zonky.test.database.use.image.created = true или что-то в этом роде, потому что каждый раз, когда я отлаживаю, я жду много времени, чтобы снова загрузить докер / изображение. Буду благодарен, если вы предоставите мне дополнительную информацию. Заранее спасибо. - person Jonathan JOhx; 07.07.2019
comment
@JonathanJohx благодарит за отзыв. На данный момент нет возможности повторно использовать ранее созданный или существующий контейнер, вы можете только изменить изображение. В любом случае, в моем случае новый контейнер запускается примерно через 5 секунд, поэтому я думаю, что запускать его каждый раз заново - не проблема. Однако, если у вас другое мнение, вы можете создать запрос функции в проекте github.. - person Tomáš Vaněk; 12.07.2019
comment
@ TomášVaněk спасибо за ваш ответ, возможно, я не смог вас понять. Я имею в виду, что у меня много таблиц в моем SQL-скрипте, тогда, когда я отлаживаю или он работает, требуется много времени для создания / вставки таблиц в докере изображения / контейнера, я думаю, так что есть какой-то способ сохранить уже созданы таблицы / база данных и изображение / контейнер? Это займет 2 минуты, если нет, я могу сформулировать хорошую функцию в вашем репо и, возможно, поработать над этим, спасибо. - person Jonathan JOhx; 13.07.2019
comment
@JonathanJohx О, теперь я понял. К сожалению, такого способа сохранить уже созданные контейнеры нет. Единственный способ ускорить загрузку базы данных - это записать данные прямо в контейнер в template1. Таким образом, все тестовые базы данных будут содержать подготовленные данные, и миграция по маршруту будет происходить быстрее. - person Tomáš Vaněk; 17.07.2019
comment
Привет, Томас, большое спасибо за эту замечательную библиотеку. У меня он быстро настроен и работает, однако я натыкаюсь на проблему с AuditingEntityListener Spring. Он изменит таблицы сущностей с двумя столбцами created_at и updated_at. Мне это нужно по крайней мере для некоторых объектов, чтобы я мог вести историю версий. Проблема в том, что в моих сценариях миграции Flyway эти столбцы не добавляются явно, и поэтому, когда я запускаю свой интеграционный тест, он выдает исключение, потому что один из этих столбцов отсутствует. У вас есть какие-нибудь предложения по этому поводу? Заранее спасибо. :) - person Serg Derbst; 03.06.2020
comment
@SergDerbst Не могли бы вы открыть новый выпуск и предоставить больше подробнее о проблеме? Если столбцы не определены в пролетной миграции, как эти столбцы создаются в производственной среде? - person Tomáš Vaněk; 05.06.2020
comment
@ TomášVaněk Спасибо, может быть. Но сейчас я считаю, что это не проблема вашей библиотеки, а скорее связано с аннотациями Spring EnableJpaAuditing и SpringBootTest. Очевидно, необходимые события при обновлении или создании объекта не генерируются и не прослушиваются. Я собираюсь сначала исследовать это, так что, может быть, позже. :) - person Serg Derbst; 06.06.2020
comment
@ TomášVaněk может помочь мне с этим - мы используем UUID в postgres, и когда flayway запускает миграцию, у меня возникла проблема ОШИБКА: функция uuid_generate_v4 () не существует. В prod DB мы делаем это с помощью такого сценария 'create extension uuid-ossp; есть ли способ сделать это с вашей библиотекой? - person Алексеев станис&; 22.06.2021
comment
@ Алексеевстанислав Убедитесь, что вы используете последнюю версию библиотеки. В некоторых старых версиях была ошибка, связанная с расширением uuid, но она уже должна быть исправлена. Протестировал сейчас, все работает нормально. Если проблема не исчезнет, ​​вы можете открыть ее здесь: github.com/zonkyio/embedded-postgres- двоичные файлы - person Tomáš Vaněk; 23.06.2021

Еще одно довольно чистое решение этой проблемы - использовать библиотеку TestContainers. Единственное предостережение - для этого требуется Docker.

Интеграционный тест:

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(initializers = {ApplicationTestsIT.Initializer.class})
public class ApplicationTestsIT {

    private static int POSTGRES_PORT = 5432;

    @Autowired
    private FooRepository fooRepository;

    @ClassRule
    public static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres")
            .withDatabaseName("foo")
            .withUsername("it_user")
            .withPassword("it_pass")
            .withInitScript("sql/init_postgres.sql");

    static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            TestPropertyValues.of(
                    "spring.data.postgres.host=" + postgres.getContainerIpAddress(),
                    "spring.data.postgres.port=" + postgres.getMappedPort(POSTGRES_PORT),
                    "spring.data.postgres.username=" + postgres.getUsername(),
                    "spring.data.postgres.password=" + postgres.getPassword()
            ).applyTo(configurableApplicationContext.getEnvironment());
        }
    }

    @Test
    public void fooRepositoryTestIT() {
        ...
    }

Конфигурация зависимости:
pom.xml:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <scope>test</scope>
</dependency>

build.gradle:

testCompile "org.testcontainers:postgresql:x.x.x"

Ссылки:
TestContainers - Базы данных
TestContainers - модуль Postgres

person magiccrafter    schedule 21.12.2019

Приведенная ниже конфигурация хорошо работает с Spring Boot 2.0.

Преимущество перед embedded-database-spring-test в том, что это решение не работает. • протолкнуть Flyway в путь к классам, возможно, нарушив автоконфигурацию Spring Boot.

@Configuration
@Slf4j
public class EmbeddedPostgresConfiguration {

    @Bean(destroyMethod = "stop")
    public PostgresProcess postgresProcess() throws IOException {
        log.info("Starting embedded Postgres");

        String tempDir = System.getProperty("java.io.tmpdir");
        String dataDir = tempDir + "/database_for_tests";
        String binariesDir = System.getProperty("java.io.tmpdir") + "/postgres_binaries";

        PostgresConfig postgresConfig = new PostgresConfig(
                Version.V10_3,
                new AbstractPostgresConfig.Net("localhost", Network.getFreeServerPort()),
                new AbstractPostgresConfig.Storage("database_for_tests", dataDir),
                new AbstractPostgresConfig.Timeout(60_000),
                new AbstractPostgresConfig.Credentials("bob", "ninja")
        );

        PostgresStarter<PostgresExecutable, PostgresProcess> runtime =
                PostgresStarter.getInstance(EmbeddedPostgres.cachedRuntimeConfig(Paths.get(binariesDir)));
        PostgresExecutable exec = runtime.prepare(postgresConfig);
        PostgresProcess process = exec.start();

        return process;
    }

    @Bean(destroyMethod = "close")
    @DependsOn("postgresProcess")
    DataSource dataSource(PostgresProcess postgresProcess) {
        PostgresConfig postgresConfig = postgresProcess.getConfig();

        val config = new HikariConfig();
        config.setUsername(postgresConfig.credentials().username());
        config.setPassword(postgresConfig.credentials().password());
        config.setJdbcUrl("jdbc:postgresql://localhost:" + postgresConfig.net().port() + "/" + postgresConfig.storage().dbName());

        return new HikariDataSource(config);
    }
}

Maven:

        <dependency>
            <groupId>ru.yandex.qatools.embed</groupId>
            <artifactId>postgresql-embedded</artifactId>
            <version>2.9</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>

Класс основан на коде, который я нашел здесь: https://github.com/nkoder/postgresql-embedded-example

Я изменил его, чтобы использовать HikariDatasource (по умолчанию Spring Boot) для правильного пула соединений. binariesDir и dataDir используются, чтобы избежать дорогостоящего извлечения + initdb в повторных тестах.

person Mateusz Stefek    schedule 03.07.2018
comment
Базовый github.com/yandex-qatools/postgresql-embedded больше не поддерживается активно. . Они предлагают перейти на testcontainers.org/modules/databases/postgres, но это доступен только в том случае, если у вас есть докер в вашей среде разработки или если движок докера доступен на удаленном порту. - person dschulten; 15.05.2019
comment
Ты прав. С тех пор, как я написал этот ответ, я пару раз использовал Testcontainers. Testcontainers кажется лучшим инструментом для большинства проектов. Единственным недостатком может быть зависимость от Docker. - person Mateusz Stefek; 30.11.2019

Взгляните на это: https://github.com/zonkyio/embedded-database-spring-test. Для ясности, он предназначен для интеграционного тестирования. Это означает, что контекст Spring инициализируется во время индивидуального теста.

Согласно документации по инструментам, все, что вам нужно сделать, это разместить аннотацию @AutoConfigureEmbeddedDatabase над классом:

@RunWith(SpringRunner.class)
@AutoConfigureEmbeddedDatabase
@ContextConfiguration("/path/to/app-config.xml")
public class FlywayMigrationIntegrationTest {

    @Test
    @FlywayTest(locationsForMigrate = "test/db/migration")
    public void testMethod() {
        // method body...
    }
}

и добавьте зависимость Maven:

<dependency>
  <groupId>io.zonky.test</groupId>
  <artifactId>embedded-database-spring-test</artifactId>
  <version>1.1.0</version>
  <scope>test</scope>
</dependency>

Чтобы использовать его вместе с @DataJpaTest, вам необходимо отключить тестовую базу данных по умолчанию с помощью аннотации @AutoConfigureTestDatabase(replace = NONE):

@RunWith(SpringRunner.class)
@AutoConfigureTestDatabase(replace = NONE)
@AutoConfigureEmbeddedDatabase
@DataJpaTest
public class SpringDataJpaTest {
// class body...
}

Чтобы сделать использование более удобным, вы также можете создать составную аннотацию, например:

@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigureTestDatabase(replace = NONE)
@AutoConfigureEmbeddedDatabase
@DataJpaTest
public @interface PostgresDataJpaTest {
}

..а затем используйте его над своим тестовым классом:

@RunWith(SpringRunner.class)
@PostgresDataJpaTest // custom composite annotation
public class SpringDataJpaTest {
// class body...
}
person Martin Volejnik    schedule 23.02.2018
comment
Спасибо, я проверю это. Однако, если он не работает с @DataJpaTest, мне нужно будет найти что-нибудь еще. - person SingleShot; 26.02.2018
comment
@SingleShot Я отредактировал свой ответ, чтобы отразить ваш комментарий. Мы активно использовали эту библиотеку в моем последнем проекте, и она покрыла практически все наши потребности в тестировании. Я очень рекомендую это. - person Martin Volejnik; 26.02.2018
comment
Спасибо! Я пытаюсь заставить его работать, но немного борюсь. У меня есть еще одна проблема в том, что мы используем Flyway для миграций, которые не выполняются с вышеуказанной настройкой. Я сообщу, если смогу разобраться. - person SingleShot; 27.02.2018
comment
Если у вас есть предложения, я был бы признателен. Спасибо! - person SingleShot; 27.02.2018
comment
@SingleShot Вы используете аннотацию @FlywayTest? Вы помещаете его либо над тестовым методом, либо над тестовым классом, и вы можете указать путь к миграциям в аннотации. - person Martin Volejnik; 27.02.2018
comment
@SingleShot Мы не использовали его с @DataJpaTest, поскольку мы использовали полный контекст, но я обнаружил следующее: github.com/spring-projects/spring-boot/issues/5716 Так что вам может потребоваться добавить и @ImportAutoConfiguration(FlywayAutoConfiguration.class). - person Martin Volejnik; 27.02.2018

Вы можете попробовать https://github.com/TouK/dockds. Это автоматически настраивает базу данных, содержащуюся в докере.

person Tomasz Wielga    schedule 26.02.2018
comment
Спасибо, но я прошу совсем не об этом. - person SingleShot; 27.02.2018
comment
Может я не понял твой вопрос. Хммм ... и я до сих пор не знаю. Я думал, вы используете встроенную конфигурацию PostgreSQL для Spring Boot. DockDS - это такая вещь. Он поддерживается Docker, но он работает без проблем и хорошо работает с инструментами CI, такими как Travis и GitlabCI. Жизненный цикл экземпляра базы данных, связанный с контекстом приложения Spring. - person Tomasz Wielga; 27.02.2018
comment
Привет, Томаш. Под встроенным я подразумеваю, что он выполняется внутри модульных тестов Java. Spring Boot предлагает 3 встроенные базы данных для модульного тестирования, но я ищу альтернативу, использующую Postgres. Цель состоит в том, чтобы любой желающий мог создать и протестировать наше приложение без необходимости устанавливать что-либо с помощью Java и Maven. Спасибо. - person SingleShot; 27.02.2018