51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

使用 Testcontainers 测试 Redis

1、概览 {#1概览}

Testcontainers 是一个用于创建临时 Docker 容器进行单元测试的 Java 库。当我们想要避免使用实际服务器进行测试时,它非常有用。

本文将会带你了解如何在 Spring Boot 中使用 Testcontainers 测试 Redis。

2、项目设置 {#2项目设置}

使用任何测试容器的首要前提是在运行测试的机器上安装 Docker。

安装好 Docker 后,就可以开始设置 Spring Boot 应用了。

在此应用中,我们将设置一个 Redis Hash、一个 Repository 和一个使用 Repository 与 Redis 交互的 Service。

2.1、依赖 {#21依赖}

添加所需的 spring-boot-starter-testspring-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.17.2</version> 
    <scope>test</scope> 
</dependency>

2.2、配置 {#22配置}

application.properties 文件中添加 Redis 连接的详细信息:

spring.redis.host=127.0.0.1
spring.redis.port=6379

3、应用设置 {#3应用设置}

我们要创建一个小型应用,向 Redis 数据库读写 Product(产品)。

3.1、Entity {#31entity}

创建 Product 类。

@RedisHash("product")
public class Product implements Serializable {
    private String id;
    private String name;
    private double price;

    // 构造函数、get、set 方法省略
}

@RedisHash 注解用于告诉 Spring Data Redis 该类应存储在 Redis Hash 中。

以 Hash 形式保存适用于不包含嵌套对象的实体。

3.2、Repository {#32repository}

接下来,为 Product Hash 定义一个 Repository:

@Repository
public interface ProductRepository extends CrudRepository<Product, String> {
}

CrudRepository 接口已经实现了 save、update、delete 和 findXxx 方法。

3.3、Service {#33service}

最后,创建一个使用 ProductRepository 执行读写操作的 Service:

@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);
    }

    // 其他方法
}

然后,Controller 或其他 Service 就可以使用该 Service 对 Product 执行 CRUD 操作。

在实际应用中,这些方法可能包含更复杂的逻辑,但在本教程中,只关注 Redis 交互。

4、测试 {#4测试}

现在,为 ProductService 编写测试,以测试 CRUD 操作。

4.1、测试 Service {#41测试-service}

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());
}

测试假设在 properties 中指定的 URL 上运行了一个 Redis 数据库。如果没有运行 Redis 实例或服务器无法连接,测试就会出错。

4.2、使用 Testcontainers 运行一个 Redis 容器 {#42使用-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、更改连接信息 {#43更改连接信息}

此时,我们已经运行了 Redis 容器,但还没有更改应用使用的连接信息。

只需使用系统属性(System Properties)覆盖 application.properties 文件中的连接详细信息即可:

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 容器的其他配置 {#44redis-容器的其他配置}

另外,还可以通过 @Testcontainers 注解使用 Jupiter 集成来管理 Redis 容器的生命周期。使用该集成时,可以使用 @Container 注解来标记容器,以便进行生命周期管理。

接下来,我们使用这种方法为测试配置 Redis 容器。

首先,在项目的 pom.xml 文件中添加 junit-jupitertestcontainers-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>

接下来,使用 @Container 注解来定义 REDIS_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());
}

一切OK!我们可以看到,Redis 容器可用于测试方法。而且,无需更改其他测试方法,可以跟以前一样使用它们。


参考:https://www.baeldung.com/spring-boot-redis-testcontainers

赞(2)
未经允许不得转载:工具盒子 » 使用 Testcontainers 测试 Redis