专栏名称: 51CTO
51CTO官方公众号——聚焦最新最前沿最有料的IT技术资讯、IT行业精华内容、产品交流心得。本订阅号为大家提供各种技术干货,还会不定期的举办有奖活动,敬请关注。
目录
相关文章推荐
新浪科技  ·  #基金经理会爱上自己投资的股票# ... ·  4 天前  
新浪科技  ·  【#中国电动汽车占全球总销量近70%#】# ... ·  5 天前  
新浪科技  ·  【#斗鱼向大学生索赔600万结果出炉#】#斗 ... ·  5 天前  
51好读  ›  专栏  ›  51CTO

五分钟教程:如何在Docker当中运行平行测试

51CTO  · 公众号  · 科技媒体  · 2017-06-12 11:45

正文


如果想在Docker中运行平行测试,你可能会引起容器名冲突的错误,下面我们来谈谈如何避免这种错误。当你在CI环境下运行测试时,有时候要运行平行测试。这种平行程序需要使用编译工具,如詹金斯、Gradle或者Maven插件。

如果你在使用Docker作为应用程序的外部测试工具(例如数据库、邮件服务器、FTP服务器),你会发现一个大问题,就是在运行平行测试时,在Docker主机里,所有文件会新建一个相同命名的容器,以至于在你开始第二个平行测试时,你会得到一个关于冲突的容器名失败,因为Docker主机试图启动两个相同命名的容器,或者这两个容器具有相同的绑定端口。


针对这个问题,你可以这样解决:

  • 你可以用一个Docker主机分别给每个文件做平行测试。

  • 你可以重复使用相同的Docker主机并且使用Arquillian Cube Star运算器。

Arquillian Cube是Arquillian的一个扩展,可以用来在你的测试中管理Docker容器。使用Arquillian Cube,你需要在计算机上运行一个Docker保护(可以是本地的或者不是),但大多数是本地的。

Arquillian Cube提供了三种不同的方法来定义容器:

  • 定义一个容器组成文件

  • 定义一个容器对象

  • 使用容器对象DSL


在下面的事例中,我将向您展示如何使用Docker组成和容器对象DSL。Star运算器可以让你指示Arquillian Cube随机命名Cube,并且可以调整链接。这样一来,在执行平行测试时,就不会因为名称或者绑定端口发生冲突了。

我们来看一个例子:


  1. plugins { 

  2.     id "io.spring.dependency-management" version "1.0.2.RELEASE" 

  3.  

  4.  

  5. apply plugin: 'java' 

  6.  

  7. repositories { 

  8.     mavenCentral() 

  9.     jcenter()  

  10.  

  11. dependencyManagement { 

  12.     imports { 

  13.         mavenBom 'org.jboss.arquillian:arquillian-bom:1.1.13.Final'  

  14.     } 

  15.  

  16. dependencies { 

  17.  

  18.     testCompile 'junit:junit:4.12' 

  19.     testCompile 'org.jboss.arquillian.junit:arquillian-junit-standalone' 

  20.     testCompile 'org.arquillian.cube:arquillian-cube-docker:1.3.2' 

  21.  

  22. test { 

  23.     maxParallelForks = 2 

  24.     testLogging.showStandardStreams = true 

  1. #src/test/docker/docker-compose.yml 

  2.  

  3. redis*: 

  4.   image: redis:3.0.7 

  5.   ports: 

  6.     - "6379" 

  1. @RunWith(Arquillian.class) 

  2. public class TestOne { 

  3.  

  4.     @HostPort(containerName = "redis*", value = 6379) 

  5.     private int portBinding; 

  6.   


  7.    @Test 

  8.     public void should_print_port_binding() throws InterruptedException { 

  9.         System.out.println(TestOne.class.getCanonicalName() + " - " + portBinding); 

  10.         TimeUnit.SECONDS.sleep(4); 

  11.     } 

  12.  


你可以看到Docker-组成yml文件从一个典型的Docker-组成文件发生的重要转变,它的名字是以星号(*)或者redis*结束的,这个名字应该是动态生成的。这里有三个测试,我们给大家演示第一个,其他两个也大同小异。

基本上,打印控制台绑定端口连接到服务器。最终建立一个gradle文件,执行两个平行试验,所以如果你在gradle运行测试,你会发现两个测试是同时执行的,当一个完成的时候,另一个也执行完毕了。然后,当你检查输出时,你会看到下一个输出。


  1. org.superbiz.parallel.runner.TestOne STANDARD_OUT 

  2.     CubeDockerConfiguration: 

  3.       serverUri = tcp://192.168.99.100:2376 

  4.       machineName = dev 

  5.       certPath = /Users/alex/.docker/machine/machines/dev 

  6.       tlsVerify = true 

  7.       dockerServerIp = 192.168.99.100 

  8.       definitionFormat = COMPOSE 

  9.       clean = false 

  10.       removeVolumes = true 

  11.       dockerContainers = containers: 

  12.       redis_9efae4a8-fcb5-4f9e-9b1d-ab591a5c4d5a: 

  13.         alwaysPull: false 

  14.         image: redis:3.0.7 

  15.         killContainer: false 

  16.         manual: false 

  17.         portBindings: !!set {56697->6379/tcp: null

  18.         readonlyRootfs: false 

  19.         removeVolumes: true 

  20.     networks: {} 

  21.  

  22.  

  23.  

  24. org.superbiz.parallel.runner.TestThree STANDARD_OUT 

  25.     CubeDockerConfiguration: 

  26.       serverUri = tcp://192.168.99.100:2376 

  27.       machineName = dev 

  28.       certPath = /Users/alex/.docker/machine/machines/dev 

  29.       tlsVerify = true 

  30.       dockerServerIp = 192.168.99.100 

  31.       definitionFormat = COMPOSE 

  32.       clean = false 

  33.       removeVolumes = true 

  34.       dockerContainers = containers: 

  35.       redis_88ff4b81-80cc-43b3-8bbe-8638dd731d8e: 

  36.         alwaysPull: false 

  37.         image: redis:3.0.7 

  38.         killContainer: false 

  39.         manual: false 

  40.         portBindings: !!set {56261->6379/tcp: null

  41.         readonlyRootfs: false 

  42.         removeVolumes: true 

  43.     networks: {} 

  44.  

  45.     //...... 

  46.  

  47. org.superbiz.parallel.runner.TestThree > should_print_port_binding STANDARD_OUT 

  48.    org.superbiz.parallel.runner.TestOne - 56261 

  49.  

  50. org.superbiz.parallel.runner.TestOne > should_print_port_binding STANDARD_OUT 

  51.    org.superbiz.parallel.runner.TestOne - 56697 

  52.  

  53. org.superbiz.parallel.runner.TestTwo > should_print_port_binding STANDARD_OUT 

  54.   org.superbiz.parallel.runner.TestOne - 56697 


正如你在日志中看到的,容器的名字不是Redis或redis*,但是redis后面跟一个UUID。此外你还可以看到,当输出打印时,绑定端口在每个情况下都是不同的。


如果你不想用docker组成的方式,你也可以用DSL容器对象的方法以编程方式定义容器,也可以支持Star运算器。我们来看看这种情况下的事例:


  1. @ClassRule 

  2. public static ContainerDslRule redisStar =  

  3.   new ContainerDslRule("redis:3.2.6", "redis*") 

  4.   .withPortBinding(6379); 


这个使用容器对象的方法是相同的,你需要Arquillian Cube1.4.0来运行它以及容器对象。基于这一特点,你可以运行任何程度的平行测试,因为Arquillian Cube可以照顾到命名和端口绑定问题。请注意,在容器之间链接的情况下,你依然需要运行Star运算器,它将在运行时解决某些问题。了解更多Star运算器请点击这里


原文作者:Alex Soto
原文标题:Running Parallel Tests in Docker


点击“阅读原文”可查看完整代码