用于 Spring 启动测试的嵌入式 Postgres
Embedded Postgres for Spring Boot Tests
我正在构建一个 Spring 启动应用程序,由 Postgres 支持,使用 Flyway 进行数据库迁移。我一直遇到无法在 Postgres 和嵌入式单元测试数据库(即使启用了 Postgres 兼容模式)中生成生成所需结果的迁移的问题。所以我正在考虑使用嵌入式 Postgres 进行单元测试。
我发现 an embedded postgres 看起来很有前途的实现,但并没有真正看到如何在 Spring Boot 的单元测试框架内将其设置为 运行(仅用于测试 Spring 数据存储库)。如何使用上述工具或 Postgres 的替代嵌入式版本进行设置?
看看这个:https://github.com/zonkyio/embedded-database-spring-test。需要明确的是,它用于集成测试。这意味着 Spring 上下文在单独测试期间被初始化。
根据工具文档,您需要做的就是在 class:
上方放置 @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 {
}
..然后在测试上方使用它 class:
@RunWith(SpringRunner.class)
@PostgresDataJpaTest // custom composite annotation
public class SpringDataJpaTest {
// class body...
}
你可以试试https://github.com/TouK/dockds。此 auto-configures 一个 docker 包含的数据库。
我是 embedded-database-spring-test library that was mentioned by @MartinVolejnik. I think the library should meet all your needs (PostgreSQL + Spring Boot + Flyway + integration testing). I'm really sorry that you're having some trouble, so I've created a simple demo app 的作者,它演示了库与 Spring 引导框架的使用。下面我总结了一些你需要做的基本步骤。
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
,则此优化无效。
例子
演示嵌入式数据库使用的测试示例class:
@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");
});
}
}
下面的配置适用于 Spring Boot 2.0。
与 embedded-database-spring-test 相比的优势在于,此解决方案不会将 Flyway 推入 class 路径,可能会扰乱 Spring 引导的自动配置。
@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);
}
}
专家:
<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>
class 基于我在这里找到的代码:https://github.com/nkoder/postgresql-embedded-example
我将其修改为使用 HikariDatasource
(Spring Boot 的默认设置)以实现正确的连接池。 binariesDir
和 dataDir
用于避免重复测试中昂贵的提取+ initdb。
该问题的另一个非常干净的解决方案是使用 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 - Databases
TestContainers - Postgres Module
我正在构建一个 Spring 启动应用程序,由 Postgres 支持,使用 Flyway 进行数据库迁移。我一直遇到无法在 Postgres 和嵌入式单元测试数据库(即使启用了 Postgres 兼容模式)中生成生成所需结果的迁移的问题。所以我正在考虑使用嵌入式 Postgres 进行单元测试。
我发现 an embedded postgres 看起来很有前途的实现,但并没有真正看到如何在 Spring Boot 的单元测试框架内将其设置为 运行(仅用于测试 Spring 数据存储库)。如何使用上述工具或 Postgres 的替代嵌入式版本进行设置?
看看这个:https://github.com/zonkyio/embedded-database-spring-test。需要明确的是,它用于集成测试。这意味着 Spring 上下文在单独测试期间被初始化。
根据工具文档,您需要做的就是在 class:
上方放置@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 {
}
..然后在测试上方使用它 class:
@RunWith(SpringRunner.class)
@PostgresDataJpaTest // custom composite annotation
public class SpringDataJpaTest {
// class body...
}
你可以试试https://github.com/TouK/dockds。此 auto-configures 一个 docker 包含的数据库。
我是 embedded-database-spring-test library that was mentioned by @MartinVolejnik. I think the library should meet all your needs (PostgreSQL + Spring Boot + Flyway + integration testing). I'm really sorry that you're having some trouble, so I've created a simple demo app 的作者,它演示了库与 Spring 引导框架的使用。下面我总结了一些你需要做的基本步骤。
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
,则此优化无效。
例子
演示嵌入式数据库使用的测试示例class:
@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");
});
}
}
下面的配置适用于 Spring Boot 2.0。
与 embedded-database-spring-test 相比的优势在于,此解决方案不会将 Flyway 推入 class 路径,可能会扰乱 Spring 引导的自动配置。
@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);
}
}
专家:
<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>
class 基于我在这里找到的代码:https://github.com/nkoder/postgresql-embedded-example
我将其修改为使用 HikariDatasource
(Spring Boot 的默认设置)以实现正确的连接池。 binariesDir
和 dataDir
用于避免重复测试中昂贵的提取+ initdb。
该问题的另一个非常干净的解决方案是使用 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 - Databases
TestContainers - Postgres Module