背景
之前聊过很多测试方法,但主要是单测
如果是集成测试依赖的中间件也是外部服务上的中间件。
比如服务器上部署的redis、mysql等
今天我们要讨论的就是如何脱离外部服务器上的中间件,本地基于docker进行容器化集成测试。
实际很多开源项目的集成测试都是这么干的
Testcontainers是啥
Testcontainers是一个Java库,用于在JVM测试中管理Docker容器。
如果我们想要进行不依赖外部服务器的集成测试,Testcontainers必不可少
接下来我们就演示一个最简单spring boot + redis的集成测试案例帮助大家更好的学习如何在spring boot中不依赖外部中间件进行容器化集成测试
测试
引入依赖
首先我们需要引入测试相关的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.17.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.20.4</version>
<scope>test</scope>
</dependency>
</dependencies>
核心依赖的测试依赖主要是如下几个
spring-boot-starter-test:spring boot测试依赖
junit-jupiter:junit5测试依赖
testcontainers:testcontainers测试依赖
编写集成测试
这里先给出所有代码,下面再逐步分析核心代码
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
@Testcontainers
class TestControllerTest {
@Container
public static GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:6.2.6"))
.withExposedPorts(6379);
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("redis.host", redis::getHost);
registry.add("redis.port", () -> redis.getMappedPort(6379).toString());
}
private final static Long SLEEP_TIME = 3L;
@Autowired
private FluxCacheProperties cacheProperties;
@Autowired
private TestController testController;
@Autowired
private DefaultFluxCacheManager cacheManager;
@Test
public void testFirstCacheByCaffeine() {
List<StudentVO> vos = testController.firstCacheByCaffeine("aaa");
StudentVO vo = vos.get(0);
int age = vo.getAge();
List<StudentVO> vos1 = testController.firstCacheByCaffeine("aaa");
StudentVO vo1 = vos1.get(0);
int age1 = vo1.getAge();
assertEquals(age, age1);
List<StudentVO> vos2 = testController.firstCacheByCaffeine("bb");
StudentVO vo2 = vos2.get(0);
assertNotEquals(age, vo2.getAge());
}
}
添加@Testcontainers注解,表示使用Testcontainers 来管理 Docker 容器
使用@Container注解管理redis容器
@Container
public static GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:6.2.6"))
.withExposedPorts(6379);
表示启动一个redis docker容器,版本为6.2.6,并且映射到主机的6379端口
使用DynamicPropertySource进行动态注入redis的host和port
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
System.out.println(redis.getHost());
System.out.println(redis.getMappedPort(6379).toString());
registry.add("redis.host", redis::getHost);
registry.add("redis.port", () -> redis.getMappedPort(6379).toString());
}
这里因为我的配置类中读取的配置key为redis.host和redis.port,所以这里动态注入这两个值
比如我的redission配置类如下
@Configuration
public class RedissonConfig {
@Value("${redis.host}")
private String redisLoginHost;
@Value("${redis.port}")
private Integer redisLoginPort;
@Value("${redis.password}")
private String redisLoginPassword;
@Bean
public RedissonClient redissonClient() {
return createRedis(redisLoginHost, redisLoginPort, redisLoginPassword);
}
private RedissonClient createRedis(String redisHost, Integer redisPort, String redisPassword) {
Config config = new Config();
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://" + redisHost + ":" + redisPort + "");
/* if (Objects.nonNull(redisPassword)) {
singleServerConfig.setPassword(redisPassword);
}*/
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
config.setCodec(new JsonJacksonCodec(objectMapper));
return Redisson.create(config);
}
}
如果使用的是其他key,这里动态注入你设置的key即可
这样配置完成后我们就可以直接运行我们的集成测试代码了,比如我这里是testFirstCacheByCaffeine
测试
直接运行测试类,如果是初次运行我们观察log会发现有下载镜像的log,然后就是一些正常的docker启动log
然后可以看到我们的测试正常启动,无需要依赖外部的redis,我们在启动集成测试的时候testcontainers会自动帮我们启动相应的docker容器
总结
Testcontainers是一个非常好用的库,可以帮助我们在集成测试的时候脱离外部服务器的依赖,本地基于docker进行容器化集成测试
这样我们就可以更好的保证我们的集成测试的独立性,同时也可以更好的保证我们的测试的稳定性
一些常用的开源框架的集成测试也是基于Testcontainers来进行的
Testcontainers + ci/cd可以在每次代码修改后进行自动化测试,保证代码的质量
作者:小奏技术
链接:https://juejin.cn