与Maven和Docker的集成测试

Docker是其中的新热点之一。 与传统虚拟机相比,它具有一套不同的技术和思想,并通过容器的思想实现了相似但同时又有所不同的事物:几乎所有VM都具有强大的功能,但速度更快,并且具有非常有趣的附加功能。

在本文中,我假设您已经对Docker有所了解,并且知道如何与之交互。 如果不是这种情况,我可以建议您从以下链接开始:

  • http://www.docker.io/gettingstarted
  • http://coreos.com/docs/launching-containers/building/getting-started-with-docker/
  • http://robknight.org.uk/blog/2013/05/drupal-on-docker/

我对该主题的个人贡献是向您展示可能的工作流 ,该工作流允许您从Maven作业中启动和停止Docker容器

我研究此功能的原因是为了帮助使用Maven构建的Java项目中的测试和集成测试 。 这个问题是众所周知的:您的代码与外部系统和服务交互。 根据您实际编写的内容,这可能意味着数据库,消息代理,Web服务等。

测试这些交互的常用策略是:

  • 在内存服务器中; 用Java实现,通常很快,但是很多时候它们的局限性在于它们不是真实的东西
  • 您实现的存根服务层,以提供所需的接口。
  • 真实的外部流程 (有时是远程的),用于测试真实的交互。

这些策略是可行的,但通常需要付出很多努力才能付诸实践。 最完整的一种使用适当的外部服务,这给隔离带来了问题:假设您正在与数据库进行交互,并且在其他人访问相同资源的同时执行读/写操作。 同样,您可能会找到正确的工作流,这些工作流涉及创建单独的模式等等,但是,这又是额外的工作,而且通常不是很简单的活动。

如果我们能拥有与这些外部系统相同的机会,但完全孤立的话,那不是很好吗? 如果我也增加报价,您会怎么看?

Docker是为我们提供这一机会的工具。

在测试套件的开头,您可以使用所需的所有服务启动一组Docker容器,并在结束时将其拆解。 您的Maven工作可以成为这些服务的唯一使用者,并且需要所有隔离功能。 您可以在Dockerfile的帮助下轻松编写所有脚本,最后,它们只不过是一系列顺序的命令行调用。

让我们看看如何启用所有这些功能。

显然,第一个前提条件是在系统上安装Docker。 您可能已经知道Docker技术取决于Linux内核的功能,所以您必须使用Linux或需要传统VM的帮助来托管D​​ocker服务器进程。

这是官方文档指南,向您展示如何在不同的Linux发行版中进行安装: http : //docs.docker.io/en/latest/installation/

相反,这是一个非常快速的指南,显示了如何在MacOSX上进行安装: http : //blog.javabien.net/2014/03/03/setup-docker-on-osx-the-no-brainer-way /

准备就绪并安装Docker后,需要应用特定的配置

在最新版本的Docker中,默认情况下仅通过Unix套接字公开其远程API。 尽管我们可以使用正确的代码与它们进行交互,但我发现通过HTTP与API进行交互要容易得多。 为此,您必须将特定标志传递给Docker守护程序,以使其也监听HTTP。

我正在使用Fedora,要修改的配置文件是/usr/lib/systemd/system/docker.service

[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.io
After=network.target[Service]
ExecStart=/usr/bin/docker -d -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock
Restart=on-failure[Install]
WantedBy=multi-user.target

与默认值相比,唯一的修改是添加了-H tcp://127.0.0.1:4243

现在,在重新加载systemd脚本并重新启动服务之后,我有了一个Docker守护程序,该守护程序向我展示了一个不错的REST API ,可以用curl戳一下。

sudo systemctl daemon-reload
sudo systemctl restart docker
curl http://127.0.0.1:4243/images/json # returns a json in output

您可能还希望此配置能够在将来的Docker rpm更新中保留下来。 为了实现这一点,您必须将刚刚修改的文件复制到可以保留rpm更新的位置。 在systemd实现此目标的正确方法是:

sudo cp /usr/lib/systemd/system/docker.service /etc/systemd/system

如果您使用的是Ubuntu ,则必须配置其他文件。 查看此页面: http : //blog.trifork.com/2013/12/24/docker-from-a-distance-the-remote-api/

现在,我们拥有与Docker轻松交互所需的一切。

您可能会期望我在此向您介绍如何使用Maven Docker插件 。 不幸的是,事实并非如此。 还没有这样的插件 ,或者至少我不知道它。 我正在考虑编写一个,但是目前我已经借助GMaven插件,一些Groovy代码和Java库Rest-assured的帮助快速解决了我的问题。

这是启动Docker容器代码

import com.jayway.restassured.RestAssured
import static com.jayway.restassured.RestAssured.*
import static com.jayway.restassured.matcher.RestAssuredMatchers.*
import com.jayway.restassured.path.json.JsonPath
import com.jayway.restassured.response.ResponseRestAssured.baseURI = "http://127.0.0.1"
RestAssured.port = 4243// here you can specify advance docker params, but the mandatory one is the name of the Image you want to use
def dockerImageConf = '{"Image":"${docker.image}"}'
def dockerImageName = JsonPath.from(dockerImageConf).get("Image")log.info "Creating new Docker container from image $dockerImageName"
def response =  with().body(dockerImageConf).post("/containers/create")if( 404 == response.statusCode ) {log.info "Docker image not found in local repo. Trying to dowload image '$dockerImageName' from remote repos"response = with().parameter("fromImage", dockerImageName).post("/images/create")def message = response.asString()//odd: rest api always returns 200 and doesn't return proper json. I have to grepif( message.contains("404") ) fail("Image $dockerImageName NOT FOUND remotely. Abort. $message}")log.info "Image downloaded"// retry to create the containerresponse = with().body(dockerImageConf).post("/containers/create")if( 404 == response.statusCode ) fail("Unable to create container with conf $dockerImageConf: ${response.asString()}")
}def containerId = response.jsonPath().get("Id")log.info "Container created with id $containerId"// set the containerId to be retrieved later during the stop phase
project.properties.setProperty("containerId", "$containerId")log.info "Starting container $containerId"
with().post("/containers/$containerId/start").asString()def ip = with().get("/containers/$containerId/json").path("NetworkSettings.IPAddress")log.info "Container started with ip: $ip" System.setProperty("MONGODB_HOSTNAME", "$ip")
System.setProperty("MONGODB_PORT", "27017")

这是阻止他们的人

import com.jayway.restassured.RestAssured
import static com.jayway.restassured.RestAssured.*
import static com.jayway.restassured.matcher.RestAssuredMatchers.*RestAssured.baseURI = "http://127.0.0.1"
RestAssured.port = 4243def containerId = project.properties.getProperty('containerId')
log.info "Stopping Docker container $containerId"
with().post("/containers/$containerId/stop")
log.info "Docker container stopped"
if( true == ${docker.remove.container} ){with().delete("/containers/$containerId")log.info "Docker container deleted"
}

放心的流利API应该可以提示正在发生的事情,内联注释应该可以澄清它,但是让我添加一些注释。 启动容器的代码是我对docker run功能的实现,如此处的官方API文档所述: http : //docs.docker.io/en/latest/reference/api/docker_remote_api_v1.9/#inside-docker -跑

我要解决特定问题是如何将Docker容器的ID从Maven阶段传播到另一个阶段 。 我已经实现了以下功能:

// set the containerId to be retrieved later during the stop phase project.properties.setProperty("containerId", "$containerId")

我还公开了一些Maven属性,可用于与API交互:

  • docker.image –您要旋转的图像的名称
  • docker.remove.container –如果设置为false,则告诉Maven不要从文件系统中删除已停止的容器(在作业完成后用于检查Docker容器)

例如

mvn verify -Ddocker.image=pantinor/fuse -Ddocker.remove.container=false

您可以在此处找到完整的工作示例。 有人告诉我,有时我的语法着色器脚本会吃一些关键字或更改单词的大小写,因此,如果要复制和粘贴,可能是从Github裁剪的一个更好的主意。

这是使用命令mvn verify运行Maven构建时输出的一部分:

...
[INFO] --- gmaven-plugin:1.4:execute (start-docker-images) @ gmaven-docker ---
[INFO] Creating new Docker container from image {"Image":"pantinor/centos-mongodb"}
log4j:WARN No appenders could be found for logger (org.apache.http.impl.conn.BasicClientConnectionManager).
log4j:WARN Please initialize the log4j system properly.
[INFO] Container created with id 5283d970dc16bd7d64ec08744b5ecec09b57d9a81162826e847666b8fb421dbc
[INFO] Starting container 5283d970dc16bd7d64ec08744b5ecec09b57d9a81162826e847666b8fb421dbc
[INFO] Container started with ip: 172.17.0.2...[INFO] --- gmaven-plugin:1.4:execute (stop-docker-images) @ gmaven-docker ---
[INFO] Stopping Docker container 5283d970dc16bd7d64ec08744b5ecec09b57d9a81162826e847666b8fb421dbc
[INFO] Docker container stopped
[INFO] Docker container deleted...

如果您有任何疑问或建议,请随时告诉我!

完整的Maven`pom.xml`也可以在这里找到: https ://raw.githubusercontent.com/paoloantinori/gmaven_docker/master/pom.xml

<!--?xml version="1.0"?-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelversion>4.0.0</modelversion><artifactid>gmaven-docker</artifactid><groupid>paolo.test</groupid><version>1.0.0-SNAPSHOT</version><name>Sample Maven Docker integration</name><description>See companion blogpost here: </description><build><plugins><plugin><groupid>org.codehaus.gmaven</groupid><artifactid>gmaven-plugin</artifactid><version>1.4</version><configuration><providerselection>2.0</providerselection></configuration><executions><execution><id>start-docker-images</id><phase>test</phase><goals><goal>execute</goal></goals><configuration><source><!--[CDATA[
import com.jayway.restassured.RestAssured
import static   com.jayway.restassured.RestAssured.*
import static   com.jayway.restassured.matcher.RestAssuredMatchers.*RestAssured.baseURI = "http://127.0.0.1"
RestAssured.port = 4243// here you can specify advance docker params, but the mandatory one is the name of the Image you want to use
def dockerImage = '{"Image":"pantinor/centos-mongodb"}'log.info "Creating new Docker container from image $dockerImage"
def response =  with().body(dockerImage).post("/containers/create")if( 404 == response.statusCode ) {log.info "[INFO] Docker Image not found. Downloading from Docker Registry"log.info with().parameter("fromImage", "pantinor/centos-mongodb").post("/images/create").asString()log.info "Image downloaded"
}// retry to create the container
def containerId = with().body(dockerImage).post("/containers/create").path("Id")log.info "Container created with id $containerId"// set the containerId to be retrieved later during the stop phase
project.properties.setProperty("containerId", "$containerId")log.info "Starting container $containerId"
with().post("/containers/$containerId/start").asString()def ip = with().get("/containers/$containerId/json").path("NetworkSettings.IPAddress")log.info "Container started with ip: $ip"System.setProperty("MONGODB_HOSTNAME", "$ip")
System.setProperty("MONGODB_PORT", "27017")
]]--></configuration></execution><execution><id>stop-docker-images</id><phase>post-integration-test</phase><goals><goal>execute</goal></goals><configuration><source><!--[CDATA[
import com.jayway.restassured.RestAssured
import static   com.jayway.restassured.RestAssured.*
import static   com.jayway.restassured.matcher.RestAssuredMatchers.*RestAssured.baseURI = "http://127.0.0.1"
RestAssured.port = 4243def containerId = project.properties.getProperty('containerId')
log.info "Stopping Docker container $containerId"
with().post("/containers/$containerId/stop")
log.info "Docker container stopped"
with().delete("/containers/$containerId")
log.info "Docker container deleted"
]]--></configuration></execution></executions></plugin></plugins></build><dependencies><dependency><groupid>com.jayway.restassured</groupid><artifactid>rest-assured</artifactid><version>1.8.1</version><scope>test</scope></dependency></dependencies>
</project>

参考:在Someday Never Comes博客上,我们的JCG合作伙伴 Paolo Antinori 与Maven和Docker进行了集成测试 。

翻译自: https://www.javacodegeeks.com/2014/03/integration-testing-with-maven-and-docker.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364572.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle转mysql总结经验,oracle转mysql总结(转)

ares-sdk初始开发测试使用的是oracle数据库&#xff0c;由于宁波通商的特殊需要&#xff0c;必须把数据库环境从oracle转向mysql。 现对转换过程中出现的问题及经验总结如下&#xff1a;主键生成策略创建一个专门记录序列的表sequence,记录有当前序列号,序列的间隔如1创建记录当…

vue 顶级组件

快 有时候懒的把一些通用组件写到 template里面去&#xff0c;而业务中又需要用到&#xff0c;比如表示 loading状态这样组件。如果是这样的组件&#xff0c;可以选择把组件手动初始化&#xff0c;让组件在整个app生命周期中始终保持活跃。 如&#xff1a; // a.js import Vu…

2018-2019-2 网络对抗技术 20165329 Exp 8 Web基础

2018-2019-2 网络对抗技术 20165329 Exp 8 Web基础 原理与实践说明 实践内容概述基础问题回答实践过程记录 1.Web前端&#xff1a;HTML2.Web前端&#xff1a;javascipt3.Web后端&#xff1a;MySQL基础4.Web后端&#xff1a;编写PHP网页5.最简单的SQL注入&#xff0c;XSS攻击测试…

为JVM分配内存:一个案例研究

这篇文章是关于最近的性能调整练习的。 与往常一样&#xff0c;这些开始于关于症状的模糊表述。 这次&#xff0c;魔鬼采取了“应用程序速度慢&#xff0c;我们无法访问源代码的形式。 我们有什么改善情况的选择”。 对该应用程序进行仔细研究后发现&#xff0c;它由捆绑在一起…

洛谷P4822 冻结

题目描述 “我要成为魔法少女&#xff01;” “那么&#xff0c;以灵魂为代价&#xff0c;你希望得到什么&#xff1f;” “我要将有关魔法和奇迹的一切&#xff0c;封印于卡片之中„„” 在这个愿望被实现以后的世界里&#xff0c;人们享受着魔法卡片&#xff08;\(SpellCard\…

Vue基础指令集锦

v-model双向绑定数据 <input type"text" v-model"msg"> {{msg}} ###v-on 事件 <div id"box"><button v-on:click"say">按钮</button><button click"say">按钮</button> </div>…

oracle删除实体,oracle 按条件删除、查询表

---查询表的名称&#xff0c;字段信息以及字段注释selectus.table_name, --表名ut.COLUMN_NAME,--字段名称uc.comments,--字段注释ut.DATA_TYPE,--字典类型ut.DATA_LENGTH,--字典长度ut.NULLABLE--是否为空from user_tab_columns utinner JOIN user_col_comments ucon ut.TABLE…

Thymeleaf与Spring集成(第2部分)

1.简介 这是Thymeleaf与Spring教程集成的第二部分。 您可以在此处阅读第一部分&#xff0c;在那里您将学习如何配置该项目。 如本教程第一部分开头所述&#xff0c;Web应用程序将发送两种类型的请求&#xff1a; 插入新访客&#xff1a;将同步请求发送到服务器以添加新访客。…

数据结构(一)

1.数据结构---数据在计算机中的存储和组织。 物理结构&#xff1a;线性存储和链式存储。 逻辑结构&#xff1a;数据的关系和联系&#xff0c;线性结构和非线性结构&#xff08;树一对多&#xff0c;前继&#xff0c;后驱&#xff09;。 数据结构和算法是伴生的&#xff0c;算法…

Vue实例和生命周期

创建一个Vue实例 每个Vue应用都是通过Vue函数创建一个新的Vue实例开始&#xff1a; var vm new Vue({//选项 }) 数据与方法 当一个Vue实例被创建时&#xff0c;它向Vue的响应式系统中加入了其data对象中能找到的所有属性。当这个属性的值发生变化时&#xff0c;视图将产生…

boid模型的Matlab程序,动物集群运动行为模型系列之五-—本科毕业设计.doc

动物集群运动行为模型系列之五-—本科毕业设计动物集群运动模型摘要本文主要模拟了鱼群的集群运动、鱼群躲避捕食者追捕的运动情况以及鸟群觅食运动的模拟&#xff0c;以此研究动物个体间的信息传递机制&#xff0c;同时也是对群体智能的初步探索。针对问题一&#xff0c;需要我…

【 jquery 】常用

$("#input1").show(slide); 渐进显示$("#input1").hide(slide); 渐进隐藏 siblings() 方法返回被选元素的所有同级元素 $("#family_name_pinyin").val(arr[0].replace(" ", "")).siblings("span").hide();…

我们正在破解JDBC,因此您不必

我们喜欢使用JDBC 没人说。 曾经 更严重的是&#xff0c;如果考虑一下&#xff0c;JDBC实际上是一个非常出色的API。 这也可能是Java成为当今流行平台的原因之一 。 在JDK 1.1 之前 &#xff0c; 以及在ODBC之前 &#xff08;这已经很久了&#xff09;&#xff0c;很难想象有任…

python之requests

转载:https://www.cnblogs.com/zhangxinqi/p/9201594.html 阅读目录 1、requests简介2、requests的安装3、requests请求4、请求响应5、requests异常处理6、cookies7、请求会话(Session)8、SSL证书验证9、代理设置10、身份认证11、编码12、其他说明1、requests简介 requests是通…

vue 项目白屏解决方案

在做的项目是使用 vue-cli 脚手架为基础的&#xff0c;只能使用微信浏览器打开的。在某次更新功能代码后&#xff0c;被反馈在一些手机上会出现白屏。经过一番探索&#xff0c;多管齐下解决了问题白屏可能的原因&#xff1a; es6 代码没有被编译成 es5 &#xff1b;文件打包路…

php如何清理网站缓存,php怎么清除opcache缓存

php清除opcache缓存的方法&#xff1a;1、开发环境中修改php.ini文件&#xff0c;将“opcache.revalidate_freq”的值改为1&#xff1b;2、在线上环境中&#xff0c;可以在PHP文件中执行“opcache_reset();”代码&#xff0c;重启web服务器。本教程操作环境&#xff1a;windows…

ActiveMQ中的温度,存储和内存使用百分比

为了有效使用ActiveMQ&#xff0c;了解ActiveMQ如何管理内存和磁盘资源以处理非持久性消息和持久性消息非常重要。 ActiveMQ具有三个关键参数&#xff0c;需要对其进行检查。 临时使用百分比 这是已用于假脱机非持久消息的已分配磁盘存储的百分比 非持久性消息是无法在代理重…

Python 爬虫之 Scrapy 分布式原理以及部署

Scrapy分布式原理 关于Scrapy工作流程 Scrapy单机架构 上图的架构其实就是一种单机架构&#xff0c;只在本机维护一个爬取队列&#xff0c;Scheduler进行调度&#xff0c;而要实现多态服务器共同爬取数据关键就是共享爬取队列。 分布式架构 我将上图进行再次更改 这里重要的就是…

【解决】ERROR in xxx.js from UglifyJs

当我们运行打包脚本 npm run build或者打包ios weexpack build ios有可能会遇到以下报错ERROR in index.js from UglifyJs![](https://img2018.cnblogs.com/blog/1504257/201811/1504257-20181102141127347-447538002.png) 这是因为webpack在打包vue文件时没有成功转换ES6的语法…

linux 给文件添加用户名和密码是什么格式,linux成批添加用户的命令

当我们遇到教学这类情况时我们需要批量添加学生用户&#xff0c;纳闷怎么才能做到成批添加用户呢?下面由学习啦小编为大家整理了linux成批添加用户命令的相关知识&#xff0c;希望大家喜欢!linux批量添加用户命令——newuserslinux批量添加用户流程&#xff1a;批量添中用户流…