1. 概述
Testcontainers 是一個用於創建臨時 Docker 容器以進行單元測試的 Java 庫。它在我們需要避免使用實際服務器進行測試時非常有用。
在本教程中,我們將學習如何在測試使用 Redis 的 Spring Boot 應用程序時使用 Testcontainers。
2. 項目設置
使用任何測試容器的前提是,必須在運行測試的機器上安裝 Docker。
安裝 Docker 後,我們就可以開始設置我們的 Spring Boot 應用程序。
在應用程序中,我們將設置一個 Redis 哈希、一個倉庫(repository)和一個服務,該服務將使用倉庫與 Redis 進行交互。
2.1. 依賴項
首先,我們需要為項目添加所需的依賴項。
首先,我們將添加以下依賴項:Spring Boot Starter Test 和 Spring Boot Starter Data Redis。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>接下來,讓我們添加 Testcontainers 依賴項:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>2.2. 自動配置
由於我們不需要進行任何高級配置,因此可以使用自動配置來建立與 Redis 服務器的連接。
為此,我們需要將 Redis 連接詳細信息添加到 application.properties 文件中:
spring.redis.host=127.0.0.1
spring.redis.port=63793. 應用程序設置
我們首先開始編寫主應用程序的代碼。我們將構建一個小型應用程序,該應用程序能夠讀取和寫入產品信息到 Redis 數據庫。
3.1. 實體
讓我們從 產品 類開始:
@RedisHash("product")
public class Product implements Serializable {
private String id;
private String name;
private double price;
// Constructor, getters and setters
}@RedisHash 註解用於告知 Spring Data Redis 此類應存儲在 Redis 哈希中。將實體存儲為哈希對於不包含嵌套對象的實體來説是有效的。
3.2. 倉庫
接下來,我們可以為我們的 產品 哈希定義一個倉庫:
@Repository
public interface ProductRepository extends CrudRepository<Product, String> {
}CRUD 倉庫接口已經實現了我們需要的保存、更新、刪除和查找產品的方法。因此,我們無需自行定義任何方法。
3.3. 服務
最後,我們創建一個服務,該服務使用 ProductRepository 執行讀取和寫入操作:
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public Product getProduct(String id) {
return productRepository.findById(id).orElse(null);
}
// other methods
}此服務可供控制器或服務使用,以對產品執行 CRUD 操作。
在實際應用中,這些方法可能包含更復雜的邏輯,但對於本教程的目的,我們將僅關注 Redis 交互。
4. 測試
我們將為我們的 ProductService 編寫測試,以測試 CRUD 操作。
4.1. 測試服務
讓我們為 ProductService 編寫一個集成測試。
@Test
void givenProductCreated_whenGettingProductById_thenProductExistsAndHasSameProperties() {
Product product = new Product("1", "Test Product", 10.0);
productService.createProduct(product);
Product productFromDb = productService.getProduct("1");
assertEquals("1", productFromDb.getId());
assertEquals("Test Product", productFromDb.getName());
assertEquals(10.0, productFromDb.getPrice());
}這假設 Redis 數據庫正在指定在屬性中 URL 上運行。如果未運行 Redis 實例或我們的服務器無法連接到它,則測試將遇到錯誤。
4.2. 使用 Testcontainers 啓動 Redis 容器
我們通過在測試運行時運行 Redis 測試容器來解決這個問題。然後,我們將從代碼本身更改連接詳細信息。
讓我們看一下創建和運行測試容器的代碼:
static {
GenericContainer<?> redis =
new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine")).withExposedPorts(6379);
redis.start();
}讓我們來理解這段代碼的不同部分:
- 我們創建了一個新的容器,使用鏡像 redis:5.0.3-alpine
- 默認情況下,Redis 實例將在端口 6379 上運行。要暴露此端口,可以使用 withExposedPorts() 方法。 這會暴露該端口並將它映射到主機機器上的一個隨機端口
- start() 方法將啓動容器並等待其準備就緒
- 我們已將此代碼添加到靜態代碼塊中,以便在注入依賴項和運行測試之前運行它
4.3. 修改連接信息
此時,我們已經啓動了一個 Redis 容器,但尚未修改應用程序使用的連接信息。要做到這一點,我們只需要通過使用系統屬性,在 <em >application.properties</em > 文件中覆蓋連接信息:
static {
GenericContainer<?> redis =
new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine")).withExposedPorts(6379);
redis.start();
System.setProperty("spring.redis.host", redis.getHost());
System.setProperty("spring.redis.port", redis.getMappedPort(6379).toString());
}我們已將 spring.redis.host 屬性設置為容器的 IP 地址。
我們可以通過獲取映射端口 6379 來設置 spring.redis.port 屬性。
現在,當測試運行時,它們將連接到容器上運行的 Redis 數據庫。
4.4. 使用 Redis 容器的替代配置
或者,我們可以通過使用 @Testcontainers 註解,在 Jupiter 集成中管理 Redis 容器的生命週期。 使用這種集成時,可以使用 @Container 註解來標記容器進行生命週期管理。 讓我們採用這種方法來配置 Redis 容器用於我們的測試。
首先,我們需要在項目的 pom.xml 文件中添加 junit-jupiter 和 testcontainers-redis-junit-jupiter 依賴項:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.17.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.redis.testcontainers</groupId>
<artifactId>testcontainers-redis-junit-jupiter</artifactId>
<version>1.4.6</version>
<scope>test</scope>
</dependency>接下來,讓我們定義靜態字段 REDIS_CONTAINER,並使用 @Container 註解:
@Container
private static final RedisContainer REDIS_CONTAINER =
new RedisContainer(DockerImageName.parse("redis:5.0.3-alpine")).withExposedPorts(6379);需要注意的是,被定義為靜態字段的容器將在測試方法之間共享,並且只會被啓動一次。
此外,讓我們使用 @DynamicPropertySource 註解定義 registerRedisProperties() 方法,以配置應用程序的連接屬性。
@DynamicPropertySource
private static void registerRedisProperties(DynamicPropertyRegistry registry) {
registry.add("spring.redis.host", REDIS_CONTAINER::getHost);
registry.add("spring.redis.port", () -> REDIS_CONTAINER.getMappedPort(6379).toString());
}最後,讓我們驗證我們的配置是否按預期工作:
@Test
void givenRedisContainerConfiguredWithDynamicProperties_whenCheckingRunningStatus_thenStatusIsRunning() {
assertTrue(REDIS_CONTAINER.isRunning());
}太好了!我們可以看到 Redis 容器已準備好用於測試方法。此外,我們無需修改其他測試方法,可以像它們原本的樣子使用它們。
5. 結論
在本文中,我們學習瞭如何使用 Redis Testcontainer 運行測試。我們還探討了 Spring Data Redis 的某些方面,以瞭解如何使用它。