使用資料庫編寫整合測試提供了多種測試資料庫選項。一種有效的選項是使用真實資料庫,以確保我們的整合測試與生產行為緊密相關。
在本教程中,我們將演示如何使用嵌入式 PostgreSQL進行 Spring Boot 測試並回顧一些替代方案。
我們首先新增Spring Data JPA 依賴項,因為我們將使用它來建立我們的儲存庫:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
|
要為 Spring Boot 應用程式編寫整合測試,我們需要包含Spring Test 依賴項:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
|
最後,我們需要包含嵌入式 Postgres 依賴項:<dependency> <groupId>com.opentable.components</groupId> <artifactId>otj-pg-embedded</artifactId> <version>1.0.3</version> <scope>test</scope> </dependency>
|
另外,讓我們為國際測試設定基本配置:spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=create-drop
|
我們在執行測試之前指定了PostgreSQLDialect並啟用了模式重新建立。首先,讓我們建立在測試中使用的Person實體:
@Entity public class Person { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column private String name; <font>// getters and setters<i> }
|
現在,讓我們為實體建立一個 Spring Data Repository:public interface PersonRepository extends JpaRepository<Person, Long> { }
|
之後我們來建立一個測試配置類:@Configuration @EnableJpaRepositories(basePackageClasses = PersonRepository.class) @EntityScan(basePackageClasses = Person.class) public class EmbeddedPostgresConfiguration { private static EmbeddedPostgres embeddedPostgres; @Bean public DataSource dataSource() throws IOException { embeddedPostgres = EmbeddedPostgres.builder() .setImage(DockerImageName.parse(<font>"postgres:14.1")) .start(); return embeddedPostgres.getPostgresDatabase(); } public static class EmbeddedPostgresExtension implements AfterAllCallback { @Override public void afterAll(ExtensionContext context) throws Exception { if (embeddedPostgres == null) { return; } embeddedPostgres.close(); } } }
|
在這裡,我們指定了儲存庫和實體的路徑。我們使用EmbeddedPostgres構建器建立了資料來源,並選擇了測試期間要使用的 Postgres 資料庫的版本。此外,我們新增了EmbeddedPostgresExtension以確保在執行測試類後關閉嵌入式 Postgres 連線。最後,讓我們建立測試類:@DataJpaTest @ExtendWith(EmbeddedPostgresExtension.class) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ContextConfiguration(classes = {EmbeddedPostgresConfiguration.class}) public class EmbeddedPostgresIntegrationTest { @Autowired private PersonRepository repository; @Test void givenEmbeddedPostgres_whenSavePerson_thenSavedEntityShouldBeReturnedWithExpectedFields(){ Person person = new Person(); person.setName(<font>"New user"); Person savedPerson = repository.save(person); assertNotNull(savedPerson.getId()); assertEquals(person.getName(), savedPerson.getName()); } }
|
我們使用@DataJpaTest註釋來設定基本的 Spring 測試上下文。我們使用EmbeddedPostgresExtension擴充套件了測試類,並將EmbeddedPostgresConfiguration附加到測試上下文。之後,我們成功建立了一個Person實體並將其儲存在資料庫中。Flyway 整合
Flyway是一種流行的遷移工具,可幫助管理架構更改。當我們使用它時,將其包含在我們的國際測試中非常重要。在本節中,我們將瞭解如何使用嵌入式 Postgres 來完成此操作。讓我們從依賴項開始:
<dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency>
|
之後,讓我們在 flyway 遷移指令碼中指定資料庫模式:CREATE SEQUENCE IF NOT EXISTS person_seq INCREMENT 50; ; CREATE TABLE IF NOT EXISTS person( id bigint NOT NULL, name character varying(255) ) ;
|
現在我們可以建立測試配置:@Configuration @EnableJpaRepositories(basePackageClasses = PersonRepository.class) @EntityScan(basePackageClasses = Person.class) public class EmbeddedPostgresWithFlywayConfiguration { @Bean public DataSource dataSource() throws SQLException { return PreparedDbProvider .forPreparer(FlywayPreparer.forClasspathLocation(<font>"db/migrations")) .createDataSource(); } }
|
我們指定了資料來源 bean,並使用PreparedDbProvider和FlywayPreparer定義了遷移指令碼的位置。最後,這是我們的測試類:@DataJpaTest(properties = { <font>"spring.jpa.hibernate.ddl-auto=none" }) @ExtendWith(EmbeddedPostgresExtension.class) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ContextConfiguration(classes = {EmbeddedPostgresWithFlywayConfiguration.class}) public class EmbeddedPostgresWithFlywayIntegrationTest { @Autowired private PersonRepository repository; @Test void givenEmbeddedPostgres_whenSavePerson_thenSavedEntityShouldBeReturnedWithExpectedFields(){ Person person = new Person(); person.setName("New user"); Person savedPerson = repository.save(person); assertNotNull(savedPerson.getId()); assertEquals(person.getName(), savedPerson.getName()); List<Person> allPersons = repository.findAll(); Assertions.assertThat(allPersons).contains(person); } }
|
我們禁用了spring.jpa.hibernate.ddl-auto屬性,以允許 Flyway 處理架構更改。之後,我們將Person實體儲存在資料庫中併成功檢索它。TestContainer
嵌入式 Postgres 專案的最新版本在後臺使用TestContainers 。因此,一種替代方法是直接使用 TestContainers 庫。讓我們從新增必要的依賴項開始:
<dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <version>1.19.8</version> <scope>test</scope> </dependency>
|
現在我們將建立初始化類,在其中為我們的測試配置PostgreSQLContainer :public class TestContainersInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, AfterAllCallback { private static final PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer( <font>"postgres:14.1") .withDatabaseName("postgres") .withUsername("postgres") .withPassword("postgres"); @Override public void initialize(ConfigurableApplicationContext applicationContext) { postgreSQLContainer.start(); TestPropertyValues.of( "spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(), "spring.datasource.username=" + postgreSQLContainer.getUsername(), "spring.datasource.password=" + postgreSQLContainer.getPassword() ).applyTo(applicationContext.getEnvironment()); } @Override public void afterAll(ExtensionContext context) throws Exception { if (postgreSQLContainer == null) { return; } postgreSQLContainer.close(); } }
|
我們建立了PostgreSQLContainer例項並實現了ApplicationContextInitializer介面來設定測試上下文的配置屬性。此外,我們還實現了 AfterAllCallback以在測試後關閉 Postgres 容器連線。現在,讓我們建立測試類:@DataJpaTest @ExtendWith(TestContainersInitializer.class) @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ContextConfiguration(initializers = TestContainersInitializer.class) public class TestContainersPostgresIntegrationTest { @Autowired private PersonRepository repository; @Test void givenTestcontainersPostgres_whenSavePerson_thenSavedEntityShouldBeReturnedWithExpectedFields() { Person person = new Person(); person.setName(<font>"New user"); Person savedPerson = repository.save(person); assertNotNull(savedPerson.getId()); assertEquals(person.getName(), savedPerson.getName()); } }
|
在這裡,我們使用TestContainersInitializer擴充套件了測試,並使用@ContextConfiguration註釋指定了測試配置的初始化程式。我們建立了與上一節相同的測試用例,併成功地將Person實體儲存在測試容器中執行的 Postgres 資料庫中。Zonky 嵌入式資料庫
Zonky 嵌入式資料庫是作為嵌入式 Postgres 的一個分支建立的,並繼續支援沒有 Docker 的測試資料庫選項。讓我們新增使用此庫所需的依賴項:
<dependency> <groupId>io.zonky.test</groupId> <artifactId>embedded-postgres</artifactId> <version>2.0.7</version> <scope>test</scope> </dependency> <dependency> <groupId>io.zonky.test</groupId> <artifactId>embedded-database-spring-test</artifactId> <version>2.5.1</version> <scope>test</scope> </dependency>
|
之後我們就可以編寫測試類了:@DataJpaTest @AutoConfigureEmbeddedDatabase(provider = ZONKY) public class ZonkyEmbeddedPostgresIntegrationTest { @Autowired private PersonRepository repository; @Test void givenZonkyEmbeddedPostgres_whenSavePerson_thenSavedEntityShouldBeReturnedWithExpectedFields(){ Person person = new Person(); person.setName(<font>"New user"); Person savedPerson = repository.save(person); assertNotNull(savedPerson.getId()); assertEquals(person.getName(), savedPerson.getName()); } }
|
在這裡,我們使用ZONKY提供程式指定了@AutoConfigureEmbeddedDatabase註釋,使我們能夠在沒有 Docker 的情況下使用嵌入式 Postgres 資料庫。此庫還支援其他提供程式,例如 Embedded 和 Docker。最後,我們已成功將 Person 實體儲存在資料庫中。