内容简介:Spring Data JPA提供了一种创建数据库查询并使用嵌入式H2数据库进行测试的简便方法。但在某些情况下,对真实数据库进行测试会更有利可图,特别是如果我们使用依赖于提供程序的查询。在本教程中,我们将演示如何使用
Spring Data JPA提供了一种创建数据库查询并使用嵌入式H2数据库进行测试的简便方法。
但在某些情况下,对真实数据库进行测试会更有利可图,特别是如果我们使用依赖于提供程序的查询。
在本教程中,我们将演示如何使用 Testcontainers 与Spring Data JPA和PostgreSQL数据库进行集成测试。
在我们之前的教程中,我们 主要使用@Query注释 创建了一些数据库 查询 ,我们现在将对其进行测试。
要在我们的测试中使用PostgreSQL数据库,我们必须添加 Testcontainers依赖 与测试范围和 PostgreSQL驱动 我们的pom.xml:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.10.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.5</version>
</dependency>
我们还在test resources目录下创建一个application.properties文件,在该目录中我们指示Spring使用正确的驱动程序类,并在每次测试运行时创建和删除该方案:
spring.datasource.driver-<b>class</b>-name=org.postgresql.Driver spring.jpa.hibernate.ddl-auto=create-drop
. 单一测试用法
要在单个测试类中开始使用PostgreSQL实例,我们必须首先创建容器定义,然后使用其参数建立连接:
@RunWith(SpringRunner.<b>class</b>)
@SpringBootTest
@ContextConfiguration(initializers = {UserRepositoryTCIntegrationTest.Initializer.<b>class</b>})
<b>public</b> <b>class</b> UserRepositoryTCIntegrationTest <b>extends</b> UserRepositoryCommonIntegrationTests {
@ClassRule
<b>public</b> <b>static</b> PostgreSQLContainer postgreSQLContainer = <b>new</b> PostgreSQLContainer(<font>"postgres:11.1"</font><font>)
.withDatabaseName(</font><font>"integration-tests-db"</font><font>)
.withUsername(</font><font>"sa"</font><font>)
.withPassword(</font><font>"sa"</font><font>);
<b>static</b> <b>class</b> Initializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
<b>public</b> <b>void</b> initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
</font><font>"spring.datasource.url="</font><font> + postgreSQLContainer.getJdbcUrl(),
</font><font>"spring.datasource.username="</font><font> + postgreSQLContainer.getUsername(),
</font><font>"spring.datasource.password="</font><font> + postgreSQLContainer.getPassword()
).applyTo(configurableApplicationContext.getEnvironment());
}
}
}
</font>
在上面的示例中,我们使用 JUnit中的@ClassRule 在执行测试方法之前设置数据库容器。我们还创建了一个实现ApplicationContextInitializer的静态内部类 。 作为最后一步,我们将@ContextConfiguration批注应用于我们的测试类,初始化类作为参数。
通过执行这三个操作,我们可以在发布Spring上下文之前设置连接属性。
被测试的用例:
@Modifying @Query(<font>"update User u set u.status = :status where u.name = :name"</font><font>) <b>int</b> updateUserSetStatusForName(@Param(</font><font>"status"</font><font>) Integer status, @Param(</font><font>"name"</font><font>) String name); @Modifying @Query(value = </font><font>"UPDATE Users u SET u.status = ? WHERE u.name = ?"</font><font>, nativeQuery = <b>true</b>) <b>int</b> updateUserSetStatusForNameNative(Integer status, String name); </font>
使用配置的环境测试它们:
@Test
@Transactional
<b>public</b> <b>void</b> givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationJPQL_ThenModifyMatchingUsers(){
insertUsers();
<b>int</b> updatedUsersSize = userRepository.updateUserSetStatusForName(0, <font>"SAMPLE"</font><font>);
assertThat(updatedUsersSize).isEqualTo(2);
}
@Test
@Transactional
<b>public</b> <b>void</b> givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationNative_ThenModifyMatchingUsers(){
insertUsers();
<b>int</b> updatedUsersSize = userRepository.updateUserSetStatusForNameNative(0, </font><font>"SAMPLE"</font><font>);
assertThat(updatedUsersSize).isEqualTo(2);
}
<b>private</b> <b>void</b> insertUsers() {
userRepository.save(<b>new</b> User(</font><font>"SAMPLE"</font><font>, </font><font>"email@example.com"</font><font>, 1));
userRepository.save(<b>new</b> User(</font><font>"SAMPLE1"</font><font>, </font><font>"email2@example.com"</font><font>, 1));
userRepository.save(<b>new</b> User(</font><font>"SAMPLE"</font><font>, </font><font>"email3@example.com"</font><font>, 1));
userRepository.save(<b>new</b> User(</font><font>"SAMPLE3"</font><font>, </font><font>"email4@example.com"</font><font>, 1));
userRepository.flush();
}
</font>
在上面的场景中,第一个测试以成功结束,但第二个测试抛出 InvalidDataAccessResourceUsageException 并显示以下消息:
Caused by: org.postgresql.util.PSQLException: ERROR: column <font>"u"</font><font> of relation </font><font>"users"</font><font> does not exist </font>
如果我们使用H2嵌入式数据库运行相同的测试,则两个测试都将成功完成,但PostgreSQL不接受SET子句中的别名。我们可以通过删除有问题的别名来快速修复查询:
@Modifying @Query(value = <font>"UPDATE Users u SET status = ? WHERE u.name = ?"</font><font>, nativeQuery = <b>true</b>) <b>int</b> updateUserSetStatusForNameNative(Integer status, String name); </font>
这次两次测试都成功完成。在此示例中,我们使用Testcontainers来识别本机查询的问题,否则在切换到生产中的真实数据库之后会显示该问题。我们还应该注意到,使用JPQL查询通常更安全,因为Spring会根据所使用的数据库提供程序正确地进行转换。
共享数据库实例
在上一段中,我们描述了如何在单个测试中使用Testcontainers。在实际情况中,由于启动时间相对较长,我们希望在多个测试中重用相同的数据库容器。
现在让我们通过扩展PostgreSQLContainer 并覆盖 start()和stop()方法来创建数据库容器创建的公共类:
<b>public</b> <b>class</b> BaeldungPostgresqlContainer <b>extends</b> PostgreSQLContainer<BaeldungPostgresqlContainer> {
<b>private</b> <b>static</b> <b>final</b> String IMAGE_VERSION = <font>"postgres:11.1"</font><font>;
<b>private</b> <b>static</b> BaeldungPostgresqlContainer container;
<b>private</b> BaeldungPostgresqlContainer() {
<b>super</b>(IMAGE_VERSION);
}
<b>public</b> <b>static</b> BaeldungPostgresqlContainer getInstance() {
<b>if</b> (container == <b>null</b>) {
container = <b>new</b> BaeldungPostgresqlContainer();
}
<b>return</b> container;
}
@Override
<b>public</b> <b>void</b> start() {
<b>super</b>.start();
System.setProperty(</font><font>"DB_URL"</font><font>, container.getJdbcUrl());
System.setProperty(</font><font>"DB_USERNAME"</font><font>, container.getUsername());
System.setProperty(</font><font>"DB_PASSWORD"</font><font>, container.getPassword());
}
@Override
<b>public</b> <b>void</b> stop() {
</font><font><i>//do nothing, JVM handles shut down</i></font><font>
}
}
</font>
通过将 stop()方法留空,我们允许JVM处理容器关闭。我们还实现了一个简单的单例模式,其中只有第一个测试触发容器启动,每个后续测试使用现有实例。在 start()方法中,我们使用 System#setProperty 将连接参数设置为环境变量。
我们现在可以将它们放在application.properties 文件中:
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
现在让我们在测试定义中使用我们的实用程序类:
@RunWith(SpringRunner.<b>class</b>)
@SpringBootTest
<b>public</b> <b>class</b> UserRepositoryTCAutoIntegrationTest {
@ClassRule
<b>public</b> <b>static</b> PostgreSQLContainer postgreSQLContainer = BaeldungPostgresqlContainer.getInstance();
<font><i>// tests</i></font><font>
}
</font>
与前面的示例一样,我们将@ClassRule 注释应用于包含容器定义的字段。这样,在创建Spring上下文之前,将使用正确的值填充DataSource连接属性。
现在,我们只需定义一个使用BaeldungPostgresqlContainer 实用程序类实例化的@ClassRule注释字段, 就可以使用相同的数据库实例实现多个测试。
结论
在本文中,我们介绍了使用Testcontainers对真实数据库实例执行测试的方法。
我们使用Spring 的ApplicationContextInitializer机制查看单个测试用法的示例 ,以及实现可重用数据库实例化的类。
我们还展示了Testcontainers如何帮助识别多个数据库提供程序的兼容性问题,尤其是对于本机查询。
与往常一样,本文中使用的完整代码可 在GitHub上获得 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- iOS 中使用Jenkins进行持续集成
- 在Shell中进行独立的集成测试
- .net core 集成 sentry 进行异常报警
- 使用ZeroCode对SpringBoot应用进行集成测试
- 使用 CODING 进行 Hexo 项目的持续集成
- 使用 CODING 进行 Spring Boot 项目的集成
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Mechanics of Web Handling
David R. Roisum
This unique book covers many aspects of web handling for manufacturing, converting, and printing. The book is applicable to any web including paper, film, foil, nonwovens, and textiles. The Mech......一起来看看 《The Mechanics of Web Handling》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
图片转BASE64编码
在线图片转Base64编码工具