在我们开发的过程中,经常会用到一些第三方的产品或者中间件,如:redis,mq,db等等,然而,因为这些产品的引入,测试过程变得复杂起来,因为中间件的缺失(可能本地有,但是换了环境就没有了),导致测试用例编写很麻烦。
TestContainer可以通过和docker结合,让我们在编写测试用例的时候,很方便的启动docker容器(容器内有我们需要的第三方产品),这样,我们就可以更好的完善测试用例。
https://www.testcontainers.org/
org.testcontainers
testcontainers
1.12.0
@ClassRule
public static GenericContainer redis = new GenericContainer("redis:5.0.5")
.withExposedPorts(6379);
实际上,new GenericContainer("redis:5.0.5")
就会创建并启动容器,而withExposedPorts(6379)
是用来暴露容器的端口号的,这些是对容器的一些配置,这种配置会有很多,后面再做介绍。redis.getContainerIpAddress()
redis.getMappedPort(6379)
类似于启动命令的-P选项
暴露:
在创建容器的时候通过new GenericContainer(...).withExposedPorts(6379)
方法可以将容器内使用的6379端口号暴露出来;
这种方式暴露出来的端口,在主机会有一个随机的端口号进行映射,
获取:
由于是随机的,所以我们无法在测试代码中提前写死要连接的端口号,这需要我们通过以下的方法来获取主机上与容器内对应的端口号,这有两种方法:
container.getFirstMappedPort()
:这会获取到第一个端口映射出来的端口,如果我们的容器中只暴露出来了一个端口(如redis的6379),这种方式比较好用。container.getMappedPort(int port)
:找到容器内port端口映射出来的端口,当有多个端口的时候,使用这种方式。使用技巧:
在Spring项目中,我们通常通过配置文件来指定这些参数,如:spring.redis.port。可是,在项目启动之前,由于容器没有启动,我们并不知道这个端口是多少,那么就没法在配置文件中配置端口了,我们如下设置:
@BeforeClass
public static void init() {
System.setProperty("spring.redis.host", redis.getContainerIpAddress());
System.setProperty("spring.redis.port", redis.getFirstMappedPort().toString());
}
注意这里使用的注解是@BeforeClass,这样,我们就可以在Spring容器中通过@Value("${spring.redis.port}")
的方式获取到随机生成的端口了
类似启动命令的-p选项。
这种方式在暴露的同时进行了映射绑定。
new GenericContainer(...).withCreateContainerCmdModifier(
cmd -> {
//对主机端口和docker中的端口进行绑定,前面的是主机端口,后面的是docker中的端口
cmd.withPortBindings(new PortBinding(Ports.Binding.bindPort(8000), new ExposedPort(6379)));
})
这样的话,我们就可以在代码中明确的使用8000端口来访问redis了
container.getContainerIpAddress()
方法即可,默认可以直接使用localhost
`Testcontainers.exposeHostPorts(localServerPort)` ,这种用法可能不多
new GenericContainer(...).withCommand("redis-server --port 7777")
container.execInContainer("touch", "/somefile.txt");
Container.ExecResult lsResult = container.execInContainer("ls", "-al", "/");
String stdout = lsResult.getStdout();
int exitCode = lsResult.getExitCode();
assertTrue(stdout.contains("somefile.txt"));
assertTrue(exitCode == 0);
new GenericContainer(...).withEnv("API_TOKEN", "foo")
new GenericContainer(...)
.withClasspathResourceMapping("redis.conf",
"/etc/redis.conf",
BindMode.READ_ONLY)
有如下两种方式获取容器内的运行日志:
使用container.getLogs()
方法会获取容器从启动到当前的日志字符串
ToStringConsumer toStringConsumer = new ToStringConsumer();
container.followOutput(toStringConsumer, OutputType.STDOUT);
这里有一堆的Consumer,如:ToStringConsumer、Slf4jLogConsumer等等