Java库(例如JUnit和Mockito)使Java中的单元测试变得简单,但是集成测试没有万能药。除了模拟的单元测试外,集成测试对于确保访问外部服务时软件的正确功能也很重要。以前,我们针对此问题采用了不同的方法,例如:
- 嵌入服务
- 运行服务
- 运行服务的Docker容器
嵌入服务可能很困难(尤其是如果不是Java的话)。直接或远程运行它们会增加大量工作,以配置维护所有开发人员和您的持续集成服务并使之可用。为该服务运行Docker容器是我们的首选方法,但是在本地进行设置,以及将其作为CI构建的一部分,这与基本测试方法都不合。对于我们来说,这是一个普遍而令人沮丧的痛点。
测试容器
我们最近切换到使用TestContainers来解决此问题。TestContainers是MIT许可的开源项目,用于运行Docker容器进行JUnit测试。
好处
与我们以前的方法相比,这有很多好处。首先,我们针对服务的真实实例(而不是某些模拟版本或嵌入式版本)运行测试,这些行为可能会有所不同。由于容器仅用于测试,因此它们与其他测试是隔离的,并且可重现性更高。设置和配置在它们所属的测试类中完成。它还使您可以轻松地针对服务的不同版本重复测试,从而可以正确确定兼容性并检查服务的新版本不需要更改自己的代码。
用例
这种用于集成测试的技术可以轻松地应用于多个用例,其中一些具有更专业的支持:
- 资料库
- 测井服务
- 网页服务
- 使用Docker Compose进行复杂的多服务交互
- 使用Selenium进行UI测试
例如
以下示例显示了如何为Redis设置集成测试。
<span style="color:#f8f8f2"><span style="color:#eff0f9"><code>import org.junit.Rule;
import org.junit.Test;
import org.testcontainers.containers.GenericContainer;
import redis.clients.jedis.Jedis;
public class RedisIntegrationTest {
private static final String DOCKER_IMAGE = "redis:3.2.9";
private final Jedis jedis;
@Rule // 1
public static GenericContainer redis = new GenericContainer(DOCKER_IMAGE).withExposedPorts(6379); // 2, 3
@Before
public void setUp() throws Exception {
Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379)); // 4
}
@Test
public void yourTest() {
//Your test code here
}
}
</code></span></span>
@Rule
为每个测试运行一个新的容器,您可以更改@ClassRule
为测试类的单个容器。- 它还支持使用来将Dockerfiles或Docker Compose文件用于非标准,未发布的容器
DockerComposeContainer
。 - 其他类似的标准docker配置
.withVolume(), withEnv(), withCommand()
可用。 - 从
GenericContainer
对象获取所需的配置。
缺点
没有!开个玩笑,也许有一些缺点,但是我们认为它们是值得的:
- 您需要可使用docker来运行测试。(即在您的本地设备和CI服务器上),但是谁没有呢?
- 您需要使Docker可用(这可能需要公开Docker套接字)
- 您必须下载图像,但这仅需要一次。
- Windows支持仅在Alpha中。