开发/产品平价旨在缩小开发和生产环境之间的差距。本文针对工具差距,特别是在使用 Spring Testcontainers 进行集成测试方面,作为使开发和生产尽可能相似的一种方式。
在进行涉及数据库的集成测试时,我们必须仔细管理所有的CRUD操作。这在集中式数据库环境中至关重要,在这种环境中,诸如 TestDeleteUserByID_ShouldReturnOk() 之类的测试可能会“意外地”决定删除自 2015 年以来一直与我们合作的最忠实客户的帐户?♂️
为了减轻此类风险,我们可以考虑数据库事务等解决方案来隔离测试数据。例如,测试可以启动一个事务来修改数据,然后在最后回滚,从而使数据库保持原始状态。
但是,这引发了一个关键问题:测试的内容是什么?
如果隔离失败并且代码执行的更改未回滚,导致数据泄漏到生产环境怎么办?这种情况下的潜在损害是巨大的。
另外,使用 H2DB 等内存数据库进行独立测试也带来了一些挑战。即使设置很容易,H2DB 与 RDBMS 不同,因此开发环境和生产环境之间的测试很可能会产生不同的结果,因此我们不能相信这些结果。
https://stackoverflow.com/questions/62778900/syntax-error-h2-database-in-postgresql-compatibility
下一个问题较少的解决方案是克隆数据库,通过类似生产的环境提供风险较小的方法。然而,这种方法也有其局限性。鉴于 ORM 自动创建和设置生产数据库模式,我们需要考虑如何保持克隆的开发数据库同步。
“Testcontainers 是一个支持 JUnit 测试的 Java 库,提供通用数据库、Selenium Web 浏览器或任何其他可以在 Docker 容器中运行的东西的轻量级一次性实例。”
它最初是为 Java 开发的,后来扩展到支持其他语言,如 Go、Rust 和 .NET。
Testcontainers 的主要思想是提供一个可从 IDE 运行的按需基础设施,无需模拟或使用内存服务即可进行测试,并且可以自动清理。
我们可以通过三个步骤来实现这一目标:
Testcontainers 库文档
在集成测试的基类ApplicationIntegrationTests中,我们定义了一个静态的PostgreSQLContainer。此容器用于从此类派生的所有测试实例。
@Testcontainers 注解可以发现所有用 @Container 注解的字段,管理其容器生命周期方法,并启动容器。
@DynamicPropertySource 注解允许我们动态地将属性注入到我们的测试环境中。
@Testcontainers @ActiveProfiles("test") public abstract class ApplicationIntegrationTests { @Container protected static PostgreSQLContainer<?> postgres=new PostgreSQLContainer<>("postgres:17.2-alpine") .withDatabaseName("testcontainersproject") .withUsername("root") .withPassword("root"); @DynamicPropertySource static void initialize(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url",postgres::getJdbcUrl); registry.add("spring.datasource.username",postgres::getUsername); registry.add("spring.datasource.password",postgres::getPassword); } }
或者,我们可以跳过使用@Testcontainers和@Container,而是直接使用@BeforeAll和@AfterAll来管理容器生命周期。这种方法可以更好地控制容器启动和停止的时间和方式
@BeforeAll public static void runContainer(){ postgres.start(); } @AfterAll static void stopContainers() { postgres.stop(); }
在@AfterAll回调方法中,我们显式停止Postgres容器。但是,即使我们没有显式停止容器,Testcontainers 也会在测试运行结束时自动清理并关闭容器。
现在我们可以通过扩展 ApplicationIntegrationTests 来创建集成测试,如下所示。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc public class CategoryControllerTest extends ApplicationIntegrationTests { private static final String CATEGORY_ENDPOINT="/categories"; @Autowired private MockMvc mockMvc; @Autowired private CategoryRepository categoryRepository; @Test void TestGetAllCategories_ShouldReturnOk() throws Exception { List<Category> categories = List.of( new Category("Electronics", "All kinds of electronic gadgets from smartphones to laptops"), new Category("Books", "A wide range of books from novels to educational textbooks") ); categoryRepository.saveAll(categories); MvcResult mvcResult=mockMvc.perform( get(CATEGORY_ENDPOINT). contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isOk()) .andReturn(); var response=mvcResult.getResponse().getContentAsString(); assertNotNull(response); assertFalse(response.isEmpty()); } }
以上是开发/产品奇偶校验:Spring Boot Testcontainers的详细内容。更多信息请关注PHP中文网其他相关文章!