用于 Spring Boot 测试的嵌入式 Postgres
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/48956743/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Embedded Postgres for Spring Boot Tests
提问by SingleShot
I'm building a Spring Boot app, backed by Postgres, using Flyway for database migrations. I've been bumping up against issues where I cannot produce a migration that generates the desired outcome in both Postgres, and the embedded unit test database (even with Postgres compatibility mode enabled). So I am looking at using embedded Postgres for unit tests.
我正在构建一个 Spring Boot 应用程序,由 Postgres 支持,使用 Flyway 进行数据库迁移。我一直在遇到一些问题,即我无法生成在 Postgres 和嵌入式单元测试数据库中生成所需结果的迁移(即使启用了 Postgres 兼容模式)。所以我正在考虑使用嵌入式 Postgres 进行单元测试。
I came across an embedded postgresimplementation that looks promising, but don't really see how to set it up to run within Spring Boot's unit test framework only (for testing Spring Data repositories). How would one set this up using the mentioned tool or an alternative embedded version of Postgres?
我遇到了一个看起来很有前途的嵌入式 postgres实现,但并没有真正了解如何将它设置为仅在 Spring Boot 的单元测试框架中运行(用于测试 Spring Data 存储库)。如何使用上述工具或 Postgres 的替代嵌入式版本进行设置?
回答by Tomá? Vaněk
I'm the author of the embedded-database-spring-testlibrary 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 appthat demonstrates the use of the library together with Spring Boot framework. Below I have summarized the basic steps that you need to do.
我是@MartinVolejnik 提到的嵌入式数据库弹簧测试库的作者。我认为该库应该满足您的所有需求(PostgreSQL + Spring Boot + Flyway + 集成测试)。很抱歉您遇到了一些麻烦,因此我创建了一个简单的演示应用程序,演示了该库与 Spring Boot 框架的结合使用。下面我总结了您需要执行的基本步骤。
Maven configuration
Maven配置
Add the following maven dependency:
添加以下 maven 依赖项:
<dependency>
<groupId>io.zonky.test</groupId>
<artifactId>embedded-database-spring-test</artifactId>
<version>1.5.2</version>
<scope>test</scope>
</dependency>
Flyway configuration
飞行路线配置
Add the following property to your application configuration:
将以下属性添加到您的应用程序配置中:
# 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
Further, make sure that you do not use org.flywaydb.test.junit.FlywayTestExecutionListener. Because the library has its own test execution listener that can optimize database initialization and this optimization has no effect if the FlywayTestExecutionListeneris applied.
此外,请确保您不使用org.flywaydb.test.junit.FlywayTestExecutionListener. 因为该库有自己的测试执行侦听器,可以优化数据库初始化,如果FlywayTestExecutionListener应用了该优化,则此优化无效。
Spring Boot 2 Configuration
Spring Boot 2 配置
Since Spring Boot 2, there is a compatibility issue with Hibernate and Postgres Driver. So you may need to add the following property to your application configuration to fix that:
从 Spring Boot 2 开始,存在与 Hibernate 和 Postgres 驱动程序的兼容性问题。因此,您可能需要将以下属性添加到您的应用程序配置中以解决该问题:
# Workaround for a compatibility issue of Spring Boot 2 with Hibernate and Postgres Driver
# See https://github.com/spring-projects/spring-boot/issues/12007
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
Example
例子
An example of test class demonstrating the use of the embedded database:
演示嵌入式数据库使用的测试类示例:
@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");
});
}
}
回答by Mateusz Stefek
The configuration below works well with Spring Boot 2.0.
下面的配置适用于 Spring Boot 2.0。
The advantage over embedded-database-spring-testis that this solution doesn't push Flyway into the classpath, possibly messing up Spring Boot's autoconfiguration.
与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>
The class is based on the code I found here: https://github.com/nkoder/postgresql-embedded-example
该课程基于我在此处找到的代码:https: //github.com/nkoder/postgresql-embedded-example
I modified it to use HikariDatasource(Spring Boot's default) for proper connection pooling. The binariesDirand dataDirare used to avoid costly extraction+initdb in repeated tests.
我将其修改为使用HikariDatasource(Spring Boot 的默认值)进行正确的连接池。的binariesDir和dataDir用于避免在反复试验昂贵萃取+ initdb的。
回答by Martin Volejnik
Take a look at this: https://github.com/zonkyio/embedded-database-spring-test. Just to be clear, it's meant for integration testing. Meaning the Spring context is initialised during the individual test.
看看这个:https: //github.com/zonkyio/embedded-database-spring-test。需要明确的是,它用于集成测试。这意味着在单个测试期间初始化 Spring 上下文。
As per the tools documentation, all you need to do is to place @AutoConfigureEmbeddedDatabaseannotation above 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...
}
}
and add Maven dependency:
并添加 Maven 依赖项:
<dependency>
<groupId>io.zonky.test</groupId>
<artifactId>embedded-database-spring-test</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
To use it together with @DataJpaTestyou need to disable the default test database by using the annotation @AutoConfigureTestDatabase(replace = NONE):
要与它一起使用,@DataJpaTest您需要使用注释禁用默认测试数据库@AutoConfigureTestDatabase(replace = NONE):
@RunWith(SpringRunner.class)
@AutoConfigureTestDatabase(replace = NONE)
@AutoConfigureEmbeddedDatabase
@DataJpaTest
public class SpringDataJpaTest {
// class body...
}
To make the use more comfortable you could also create a composite annotation, something like:
为了使使用更舒适,您还可以创建一个复合注释,例如:
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigureTestDatabase(replace = NONE)
@AutoConfigureEmbeddedDatabase
@DataJpaTest
public @interface PostgresDataJpaTest {
}
..and then use it above your test class:
..然后在你的测试类上面使用它:
@RunWith(SpringRunner.class)
@PostgresDataJpaTest // custom composite annotation
public class SpringDataJpaTest {
// class body...
}
回答by magiccrafter
Another quite clean solution to that problem is to use the TestContainerslibrary. The only caveat is that it requires Docker.
该问题的另一个非常干净的解决方案是使用TestContainers库。唯一需要注意的是它需要 Docker。
Integration Test:
集成测试:
@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() {
...
}
Dependency configuration:pom.xml:
依赖配置pom.xml::
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
build.gradle:
build.gradle:
testCompile "org.testcontainers:postgresql:x.x.x"
Links:
TestContainers - Databases
TestContainers - Postgres Module
回答by Tomasz Wielga
You can try https://github.com/TouK/dockds. This auto-configures a docker contained database.
你可以试试https://github.com/TouK/dockds。这会自动配置 docker 包含的数据库。

