開發/產品平價旨在縮小開發和生產環境之間的差距。本文針對工具差距,特別是在使用 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中文網其他相關文章!