我正在构建一个由Postgres支持的Spring Boot应用程序,使用Flyway进行数据库迁移。我一直在遇到无法产生迁移的问题,这些迁移在Postgres和嵌入式单元测试数据库中都会产生预期的结果(即使启用了Postgres兼容模式)。所以我正在考虑使用嵌入式Postgres进行单元测试。
我遇到了看起来很有希望的an embedded postgres实现,但是没有真正看到如何将其设置为仅在Spring Boot的单元测试框架中运行(用于测试Spring Data存储库)。如何使用上述工具或Postgres的替代嵌入版本来设置它?
答案 0 :(得分:18)
我是@MartinVolejnik提到的embedded-database-spring-test库的作者。我认为该库应该满足您的所有需求(PostgreSQL + Spring Boot + Flyway +集成测试)。我很抱歉您遇到了麻烦,所以我创建了一个simple demo app来演示如何将该库与Spring Boot框架一起使用。下面我总结了您需要做的基本步骤。
Maven配置
添加以下maven依赖项:
<dependency>
<groupId>io.zonky.test</groupId>
<artifactId>embedded-database-spring-test</artifactId>
<version>1.4.1</version>
<scope>test</scope>
</dependency>
Flyway配置
将以下属性添加到您的应用程序配置中:
# 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
,则此优化不起作用。
Spring Boot 2配置
自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
示例强>
演示嵌入式数据库使用的测试类示例:
@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");
});
}
}
答案 1 :(得分:2)
以下配置可与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);
}
}
行家:
<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。
答案 2 :(得分:1)
看看这个: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...
}
答案 3 :(得分:0)
另一个解决该问题的方法是使用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