有时,当您在CI环境上运行测试时,您希望并行运行测试。 这种并行性是在Maven或Gradle之类的构建工具中编程的,或者是通过使用Jenkins插件编程的。
如果您将Docker用作提供对应用程序外部依赖关系(例如数据库,邮件服务器,ftp服务器等)的测试工具,则可能会发现一个大问题,原因可能是
Docker Hos t是其中之一,当并行运行测试时,所有这些都将尝试启动具有相同名称的容器。 因此,当您(并行)启动第二项测试时,由于尝试在同一Docker主机上启动两个名称相同或两个容器中具有相同绑定端口的容器,因此将出现与该冲突容器名称有关的失败。
至此,您可以做两件事:
- 每个并行测试可以有一个Docker主机 。
- 您可以重用相同的Docker主机并使用Arquillian Cube Star Operator 。
Arquillian Cube是Arquillian扩展,可用于在测试中管理Docker容器。
要使用Arquillian Cube,您需要在计算机上运行Docker守护程序(它可以是本地的,也可以不是本地的),但是可能是在本地。
Arquillian Cube提供了三种定义容器的方法:
- 定义docker-compose文件。
- 定义容器对象。
- 使用容器对象DSL。
在此示例中,我将向您展示如何使用docker-compose和Container Object DSL 。
通过星号运算符,您可以向Arquillian Cube指示您要随机生成多维数据集名称,并且还可以调整链接。 这样,当您并行执行测试时,不会因为名称或绑定端口而发生冲突。
让我们来看一个例子:
plugins {
id "io.spring.dependency-management" version "1.0.2.RELEASE"
}
apply plugin: 'java'
repositories {
mavenCentral()
jcenter()
}
dependencyManagement {
imports {
mavenBom 'org.jboss.arquillian:arquillian-bom:1.1.13.Final'
}
}
dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.jboss.arquillian.junit:arquillian-junit-standalone'
testCompile 'org.arquillian.cube:arquillian-cube-docker:1.3.2'
}
test {
maxParallelForks = 2
testLogging.showStandardStreams = true
}
#src/test/docker/docker-compose.yml
redis*:
image: redis:3.0.7
ports:
- "6379"
@RunWith(Arquillian.class)
public class TestOne {
@HostPort(containerName = "redis*", value = 6379)
private int portBinding;
@Test
public void should_print_port_binding() throws InterruptedException {
System.out.println(TestOne.class.getCanonicalName() + " - " + portBinding);
TimeUnit.SECONDS.sleep(4);
}
}
您可以在泊坞窗,compose.yml文件中看到一个典型的码头工人,撰写文件的一个重要变化,就是这名以星号(*)操作符[Redis的*]结束。 这是您指示Arquillian Cube的名称,该名称应为每次执行动态生成。
然后有三个测试(这里仅显示第一个),它们看起来都一样。 基本上,它打印以控制台绑定端口以连接到服务器。 最后是build.gradle文件,该文件并行执行两个测试。 因此,如果您在Gradle中运行测试( ./gradlew test ),您将看到两个测试同时执行,并且当其中一个测试完成时,其余测试将被执行。 然后,如果您检查输出,您将看到下一个输出:
org.superbiz.parallel.runner.TestOne STANDARD_OUT
CubeDockerConfiguration:
serverUri = tcp://192.168.99.100:2376
machineName = dev
certPath = /Users/alex/.docker/machine/machines/dev
tlsVerify = true
dockerServerIp = 192.168.99.100
definitionFormat = COMPOSE
clean = false
removeVolumes = true
dockerContainers = containers:
redis_9efae4a8-fcb5-4f9e-9b1d-ab591a5c4d5a:
alwaysPull: false
image: redis:3.0.7
killContainer: false
manual: false
portBindings: !!set {56697->6379/tcp: null}
readonlyRootfs: false
removeVolumes: true
networks: {}
org.superbiz.parallel.runner.TestThree STANDARD_OUT
CubeDockerConfiguration:
serverUri = tcp://192.168.99.100:2376
machineName = dev
certPath = /Users/alex/.docker/machine/machines/dev
tlsVerify = true
dockerServerIp = 192.168.99.100
definitionFormat = COMPOSE
clean = false
removeVolumes = true
dockerContainers = containers:
redis_88ff4b81-80cc-43b3-8bbe-8638dd731d8e:
alwaysPull: false
image: redis:3.0.7
killContainer: false
manual: false
portBindings: !!set {56261->6379/tcp: null}
readonlyRootfs: false
removeVolumes: true
networks: {}
//......
org.superbiz.parallel.runner.TestThree > should_print_port_binding STANDARD_OUT
org.superbiz.parallel.runner.TestOne - 56261
org.superbiz.parallel.runner.TestOne > should_print_port_binding STANDARD_OUT
org.superbiz.parallel.runner.TestOne - 56697
org.superbiz.parallel.runner.TestTwo > should_print_port_binding STANDARD_OUT
org.superbiz.parallel.runner.TestOne - 56697
因此,您可以在日志中看到,容器名称不是redis或redis * ,而是redis后跟一个UUID 。 您还可以看到在打印输出时,每种情况下的装订端口都不同。
但是,如果您不想使用docker-compose方法,则还可以使用还支持star运算符的Container Object DSL以编程方式定义容器。 在这种情况下,示例如下所示:
@ClassRule
public static ContainerDslRule redisStar =
new ContainerDslRule("redis:3.2.6", "redis*")
.withPortBinding(6379);
方法是相同的,但是使用容器对象(您需要Arquillian Cube 1.4.0才能与容器对象一起运行)。
请注意,由于Arquillian Cube可以处理命名或端口绑定问题,因此借助此功能,您可以以任意程度的并行执行来运行测试。 请注意,在容器之间进行链接的情况下,您仍然需要使用star运算符,它将在运行时解析。
要了解有关星算子的更多信息,只需检查http://arquillian.org/arquillian-cube/#_parallel_execution
源代码: https : //github.com/lordofthejars/parallel-docker
翻译自: https://www.javacodegeeks.com/2017/05/running-parallel-tests-docker.html