当前位置: 首页 > news >正文

基于云技术的集成测试代码覆盖率收集的一站式解决方案

作者: 孙天衣,于清国,石俊娟,沈燕玉


背景

代码覆盖率是衡量产品测试效果很重要的指标。得到单元测试的代码覆盖率相对比较简单。然而,web应用的测试人员经常会为收集集成测试或者端到端测试的代码覆盖率而伤脑筋。其中的主要原因是测试人员往往对这个领域的技术比较陌生,而且现有的方案比较复杂,容易出错。举例来讲,目前有一个方案不是很自动化,需要用户手工修改很多地方。我们经过调研,决定开发一个基于云技术的自动的一站式解决方案来收集端到端测试的代码覆盖率。我们开发的这个方案名为“ICoCo”(这个名称的意思是集成测试代码覆盖率Integration Code Coverage))。

 

下面对一个代码覆盖率的工具JaCoCo做一个简单的介绍,因为ICoCo就是基于JaCoCo来开发的。JaCoCo一个非常突出的功能就是其能够利用一个代理在代码运行的过程动态注入指令来收集代码覆盖率。用户不要提前在项目的pom文件或者其他配置文件中做任何的修改。他们需要做的仅仅是在web容器(tomcat或其他)的启动参数中加上一行命令:-javaagent:${DIR}/jacocoagent.jar=output=tcpserver,port=*,address=*”当然,目录、端口需要指定。 而且,代码覆盖率的结果很容易下载下来,因为Jacoco的代理启动了一个TCP的服务器。请参阅附录[3]获取更多的有关JaCoCo的信息。

 

利用持续集成工具来运行测试脚本是目前一种非常普遍的做法。我们目前使用的是Jenkins服务器。CI job被创建出来执行测试任务。我们的方案和Jenkins服务器作了无缝的集成。关于Jenkins的详细信息,请参阅附录[1][2]

概述

我们不仅利用了JaCoCo的功能,而且还利用了公司内部的云平台的API来解决应用的安装,部署问题。

如上所述,既然所有的测试工作都是在Jenkins服务器上执行,我们决定开发一个Jenkins 插件来完成所有的代码覆盖率的收集工作。Jenkins 插件很容易与现有的测试任务集成。现有的测试任务不用做任何修改,只要新建一个使用ICoCo的CI job, 把先用的测试任务的链接配置进去即可。关于如何开发Jenkins的插件,请参阅附录[4]。 对ICoCo的设计需求如下:

1)     能够自动把被测试的应用部署到测试的服务器。

2)     能够把JaCoCo的代理下载并修改web 容器启动参数以加载代理;

3)     能够合并子项目的代码覆盖率的结果。

4)     能够很容易扩展来支持不同种类的web 应用。

5)     以上所有的工作能够自动完成。

除了以上功能需求,我们在开发过程中尽可能复用已有的库或者服务,避免“重新发明轮子”。

目前这个方案不仅能够支持单个应用的测试,而且也能够支持多个应用的测试。

下图是ICoCo的配置界面图。我们可以看到配置非常简洁。



                                                                        Figure1


下图是集成测试的代码覆盖率在Sonar上的展示图。从图中可以看到,Sonar能够把集成测试的代码覆盖率和单元测试的代码覆盖率很好的汇聚起来。目前公司要求所有的集成测试和端到端测试都要收集代码覆盖率,ICoCo帮助测试人员很好的完成了任务。



                                                                                        Figure2

ICoCo基本工作流程

1.       如有需要,部署web应用到服务器。用户需要在部署之前编译和上传安装包。这一步是可选步骤。如果用户针对服务器上当前版本测试,这一步可忽略。

2.       下载Jacoco 代理,修改启动参数,并重启web应用来启动Jacoco 代理。

3.       触发测试的CI job以进行端到端测试。在测试的过程中,JaCoCo 代理记录下代码覆盖率。

4.       利用JaCoCo的maven 插件下载代码覆盖率结果。

5.       如有需要,合并代码覆盖率结果。

代码覆盖率结果生成之后,如有需要,可以利用Sonar的Jenkins 插件把结构上传到Sonar的服务器以进行测试质量监控。




架构和实现细节

ICoCo的实现为Jenkins 插件。其架构是典型的三层架构。值得注意的地方是如果需要Jenkins plugin在一个分布式的Jenkins系统上工作,有一些特别的处理。细节请参考参考文档[4]。因为有一些公司内部的API,这个项目的源代码不会被公开。下面会给出一些代码示例作为参考。

1.       表现层 ICoCo的界面允许用户配置各种相关参数,如是否部署,测试CI job链接等。

Jenkins是通过一个称之为Jelly文件的配置文件来实现插件的界面开发。下面是一个实例代码。具体细节可参阅参考文档[5]。

<j:jelly xmlns:j="jelly:core"xmlns:st="jelly:stapler" xmlns:d="jelly:define"xmlns:l="/lib/layout"xmlns:t="/lib/hudson" xmlns:f="/lib/form">

  <f:entry title="Name" field="name">

   <f:textbox />

  </f:entry>

</j:jelly>

2.       控制层 ICoCo的逻辑的主要入口点为ICOCOBuilder 类中的perform 方法。ICOCOBuilder类继承了Hudson.Task.Builder 类。各种功能,比如部署,安装JaCoCo 代理,触发测试,收集测试覆盖率等等,全部实现在perform方法中。界面输入的各个参数的值被传递到ICOCOBuilder中的相应的成员变量中, 以供各个功能使用。

3.       接口层  接口或者服务在各种功能执行的时候被调用。

a)       云中负责部署的API在部署的过程中被调用;

b)       云的管理接口被调用来下载和安装JaCoCo 代理,没有使用SSH直接链接。原因主要是避免输入用户名,密码。

c)       Jenkins服务器的API被调用来触发测试的CI job;

d)       JGit库的API被用来访问git服务器来下载源代码,用以生成代码覆盖率报告。代码示例如下:


public void downloadSourceFromGitRepo(File localRepoDir, String gitUrl, String commitId) throws IOException {
	        if (localRepoDir.exists() == true) {
	            FileUtils.delete(localRepoDir, FileUtils.RECURSIVE);
	        }
	        Git localRepo = null;
	        try {
	        	  localRepo = Git.cloneRepository()
	                      .setURI(gitUrl)
	                      .setDirectory(localRepoDir)
	                      .call();
	              localRepo.checkout().setStartPoint(commitId).setName(commitId).call();
	        } catch (Exception e) {
	            RuntimeException re = new RuntimeException("Git clone failed in method downloadSourceFromGitRepo");
	            re.initCause(e);
	            throw re;
	        } 
	        finally {
	            if (localRepo != null) {
	                localRepo.close();
	            }
	        }	 	        
	        OutputLogger.remoteLogging("Downloading source code done.");

e)       Jacocomaven 插件被调用用来生成最终的代码覆盖率报告。

下面是Jacoco Maven 插件的调用代码示例:

-q -Dmaven.test.skip=true -Denforcer.skip=true org.jacoco:jacoco-maven-plugin:0.7.0.201403182114:dump -Djacoco.destFile=target/" + destFile + " -Djacoco.port=8084 -Djacoco.address=" + host + " org.jacoco:jacoco-maven-plugin:0.7.0.201403182114:report -f " + pomPath;

f)       合并子项目的代码覆盖率的下载结果。合并代码覆盖率调用了Jacoco的API。代码示例如下:

private static class MergeCCCallable implements Callable<Boolean, IOException> {
    	/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private String rootFolder;

    	public MergeCCCallable(final String rootFolder){
    		this.rootFolder = rootFolder + File.separator;
    	}
		public Boolean call() throws IOException {
			mergeJacocoResult();
			return Boolean.FALSE;
		}
		
		public void mergeJacocoResult() throws IOException {
			List<File> fList = getCCFileList();
			if (fList.size() == 0) {
				throw new RuntimeException(
						"No code coverage result file has been found!");
			}
			final ExecFileLoader loader = new ExecFileLoader();
			for (final File f : fList) {
				loader.load(f);
			}
			final File disFile = new File(rootFolder + "target/icoco.exec");
			loader.save(disFile, false);
		}

		public List<File> getCCFileList() {
			final List<File> res = new ArrayList<File>();
			// collect result files in the target folder of root folder
			String rootFld = rootFolder + "target";
			OutputLogger.remoteLogging(rootFld);
			File folder = new File(rootFld);
			File[] list = folder.listFiles();
			if (list != null){
				for (final File f : list) {
					if (f.getName().matches("^icocoTmp.*.exec")) {
						res.add(f);
					}
				}
			}
			return res;
		}
    	
    }



Figure4

小结

到目前为止,端到端测试中都应用了这个解决方案,取得了不错的效果。下载,安装配置非常简单,用户不要对代码覆盖率的收集细节有深入的了解,节约了大量的时间。

参考资料

1.        The official web site forJenkins: http://jenkins-ci.org/

2.        The description about Jenkinson Wikipedia: http://en.wikipedia.org/wiki/Jenkins_%28software%29

3.        http://www.eclemma.org/jacoco/

4.        http://ccoetech.ebay.com/tutorial-dev-jenkins-plugin-distributed-jenkins 

5.        https://wiki.jenkins-ci.org/display/JENKINS/Basic+guide+to+Jelly+usage+in+Jenkins



相关文章:

  • 使用github pages + issues + api建立个人博客
  • MapReduce的详细过程
  • 基于Jmeter和Jenkins的自动化性能测试的一站式解决方案
  • jQuery动态载入JS文件研究
  • SolrCloud之分布式索引及与Zookeeper的集成
  • Kafka的分布式架构设计与High Availability机制
  • JS方法代理
  • Hadoop作业性能指标及参数调优实例 (一)Hadoop作业性能异常指标
  • Hadoop作业性能指标及参数调优实例 (二)Hadoop作业性能调优7个建议
  • Hadoop作业性能指标及参数调优实例 (三)Hadoop作业性能参数调优方法
  • 漫谈程序控制流
  • Hadoop集群硬盘故障分析与自动化修复
  • jQuery数据赋值解析
  • Apache Kylin的快速数据立方体算法——概述
  • eBay RUM实践
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • Android系统模拟器绘制实现概述
  • emacs初体验
  • es6(二):字符串的扩展
  • Java新版本的开发已正式进入轨道,版本号18.3
  • ng6--错误信息小结(持续更新)
  • webpack+react项目初体验——记录我的webpack环境配置
  • 对超线程几个不同角度的解释
  • 前端技术周刊 2019-01-14:客户端存储
  • 前端学习笔记之观察者模式
  • 一个JAVA程序员成长之路分享
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • $ git push -u origin master 推送到远程库出错
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (1)bark-ml
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (floyd+补集) poj 3275
  • (十)T检验-第一部分
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET Core 中插件式开发实现
  • .NET Core中的去虚
  • .net 发送邮件
  • .Net 垃圾回收机制原理(二)
  • .NET4.0并行计算技术基础(1)
  • :O)修改linux硬件时间
  • @AutoConfigurationPackage的使用
  • @html.ActionLink的几种参数格式
  • [20180224]expdp query 写法问题.txt
  • [acwing周赛复盘] 第 94 场周赛20230311
  • [Editor]Unity Editor类常用方法
  • [ERROR]-Error: failure: repodata/filelists.xml.gz from addons: [Errno 256] No more mirrors to try.
  • [GN] Vue3.2 快速上手 ---- 核心语法2
  • [IE技巧] IE8中HTTP连接数目的变化
  • [javaSE] 数据结构(二叉查找树-插入节点)
  • [JS入门到进阶] 7条关于 async await 的使用口诀,新学 async await?背10遍,以后要考!快收藏