随着互联网的繁荣发展,测试人员的整体水平也在不断的提升,各大公司对测试人员的要求也早已不再只停留在功能测试上。Docker、CI/CD、DevOps等等,这些不断提升开发质量和效率的流程和技术无不提醒着测试人员丰富自己的技术栈。为了保证覆盖更多的场景、保证产品和交付质量,我们决定统计接口/黑盒测试的覆盖率。
JaCoCo
简单点来说,JaCoCo通过使用探针技术修改Jar文件、Class文件的字节码进行插桩,插桩后使用Jacocoagent的方式dump出jacoco.exec文件,通过第三方工具,解析exec文件后可得到覆盖率。Jacoco的插桩方式一共有两种:
1.On-the-fly:JVM中通过-javaagent参数指定特定的jar文件启动Instrumentation的代理程序,代理程序在通过Class Loader装载一个class前判断是否转换修改class文件,将统计代码插入class,测试覆盖率分析可以在JVM执行测试代码的过程中完成。
2.Offline:在测试前先对文件进行插桩,然后生成插过桩的class或jar包,测试插过桩 的class和jar包后,会生成动态覆盖信息到文件,最后统一对覆盖信息进行处理,并生成报告。
在本次的黑盒测试覆盖率统计的实践过程中,我们采用的是On-the-fly的插桩模式,可以在运行中进行埋点和数据采集,可以实时的拿到覆盖率统计数据而无需中途停止服务或重启服务。
实际场景
我们的项目部署在容器中,容器通过K8S集群进行调度和管理,整个项目部署的流程由搭建在K8S集群外的Jenkins管理,实际部署的场景如下图所示:
这里需要简单的为大家介绍一下K8S中各个port的作用。在K8S集群中,我们经常会用到这中 port:port、targetport、nodeport
1.port,service暴露在cluster ip上的端口号,使提供给集群内部客户访问的service入口
2.targetport,targetPort是pod上的端口,从port和nodeport上到来的数据最终经过kube-proxy流入到后端pod的targetport上进入容器。
3.nodeport,是kubernetes提供给集群外部客户访问service入口的一种方式(另一种方式是LoadBalancer)
总的来说,port和nodeport都是service的端口,前者暴露给集群内客户访问服务,后者暴露给集群外客户访问服务。从这两个端口到来的数据都需要经过反向代理kube-proxy流入后端pod的targetPod,从而到达pod上的容器内。
1.修改yaml文件和Dockerfile
为了后面我们能够dump生成jacoco.exec文件,我们需要修改项目的yaml文件,这样就可以通过ip和端口号访问到我们jacocoagent的覆盖率统计服务,如下:
首先要在yaml文件中新建一个人service(注意不要和已有的service重名),然后指定服务的targetport和nodeport(注意不要使用已经被占用的端口号)。配置好yaml文件以后,我们还需要修改Dockerfile文件,使用Jacocoagent代理服务来启动我们的服务,Dockerfile如下:
FROM docker.toby.local:5000/jdk8:centos7.4-toby MAINTAINER "toby <toby@test.com>" RUN mkdir -p /export/Logs/ ARG JAR_FILE COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-javaagent:/opt/JaCoCo/jacoco/lib/jacocoagent.jar=includes=*,output=tcpserver,port=8146,address=0.0.0.0","-jar","/app.jar"] CMD ["java","-Xmx2048m","-jar","/app.jar"] |
在Dockerfile中我们使用到了Jacocoagent来启动我们的服务,启动项参数:
Includes,应包含在执行分析中的类名列表。列表条目由冒号(:)分隔,可以使用通配符(*和?)。除性能优化或技术角落情况外,通常不需要此选项。
Output,用于写入覆盖数据的输出方法。有效选项包括:
file:在VM终止时,执行数据将写入destfile属性中指定的文件。
tcpserver:代理侦听由address、 port属性指定的TCP端口上的传入连接。执行数据被写入此TCP连接。
tcpclient:启动时,代理连接到address、 port 属性指定的TCP端口。执行数据被写入此TCP连接。
none:不要产生任何输出。
Address,输出方法tcpserver或输出方法连接时 要绑定的IP地址或主机名 tcpclient。在tcpserver模式中,值“ *”使代理接受任何本地地址上的连接。
Port,输出方法为tcpserver或在输出方法连接时绑定到的端口tcpclient。在 tcpserver模式下,端口必须可用,这意味着如果多个JaCoCo代理应在同一台机器上运行,则必须指定不同的端口。
需要注意,Dockerfile中的jar包启动参数中的port需要与yaml文件中的nodeport保持一致,只有这样我们才可以将Jacoco统计覆盖率的服务暴露给集群外部进行访问
2.配置Ant任务
在步骤1中,我们已经在容器中配置好了Jacoco的服务。通过Jenkins进行自动化部署,过程是:从git上拉取最新的代码,编译成jar包然后制作镜像,在容器中运行,最后将容器交给K8S集群进行管理调度。如果想要dump出Jacoco.exec文件,我们还需要配置ant任务。
<?xml version="1.0" encoding="UTF-8"?> <project name="ins-activity-test" default="run" xmlns:jacoco="antlib:org.jacoco.ant"> <!--Jacoco的安装路径--> <property name="jacocoAntPath" value="/opt/jacoco/jacoco/lib/jacocoant.jar"/> <!--生成.exec文件的路径,Jacoco就是根据这个文件生成HTML报告的--> <property name="jacocoExecPath" value="/opt/jacoco/jacocodata/jacocoExec/"/> <!--生成覆盖率报告的路径--> <property name="jacocoReportPath" value="/opt/jacoco/jacocodata/jacocoReport/"/> <!--远程服务的ip地址 --> <!--远程服务的ip地址,如果用的是docker部署的,此处填的ip和端口不是docker内部的ip而是服务器的ip和端口,服务器的端口需要与docker容器内java应用的端口做映射 --> <property name="server_ip" value="10.207.248.199"/> <!--前面javaagent配置的远程服务打开的端口,要跟上面配置的一样--> <property name="server_port_manager" value="30778"/> <!--源代码路径--> <property name="SrcPath" value="/root/.jenkins/workspace/jacoco-test/src/main/java/com/jd/ins/marketing/service/"/> <!--.class文件路径--> <property name="ClassesPath" value="/root/.jenkins/workspace/jacoco-test/target/classes/com/jd/ins/marketing/service/"/> <!--让ant知道去哪儿找Jacoco--> <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath path="${jacocoAntPath}"/> </taskdef> <target name="run"> <echo message="start..."/> <echo message="dump..."/> <antcall target="dump"/> <echo message="merge..."/> <antcall target="merge"/> <echo message="report..."/> <antcall target="report"/> <echo message="end..."/> </target> <!--dump任务: 根据前面配置的ip地址,和端口号,访问目标服务,并生成.exec文件。 reset=true时,会在dump出exec文件后,清空覆盖率数据; append=false时,dump出的exec文件会覆盖原有的exec文件;append=true时,dump出的exec文件 追加至原有的exec文件; --> <target name="dump"> <jacoco:dump address="${server_ip}" reset="true" destfile="${jacocoExecPath}/jacoco_ins_marketing_service.exec" port="${server_port_manager}" append="true"/> </target> <target name="merge"> <jacoco:merge destfile="${jacocoExecPath}/jacoco_ins_marketing_service.exec"> <fileset dir="${jacocoExecPath}" includes="*.exec"/> </jacoco:merge> </target> <!--jacoco任务: 根据前面配置的源代码路径和.class文件路径, dump后生成的.exec文件,生成最终的html覆盖率报告。--> <target name="report"> <jacoco:report> <executiondata> <file file="${jacocoExecPath}/jacoco_ins_marketing_service.exec"/> </executiondata> <structure name="JaCoCo Report"> <!--group name 对应生成的报告中的列表名--> <group name="demo"> <sourcefiles encoding="UTF-8"> <fileset dir="${SrcPath}"/> </sourcefiles> <classfiles> <fileset dir="${ClassesPath}"/> </classfiles> </group> </structure> <html destdir="${jacocoReportPath}" encoding="utf-8"/> <csv destfile="${jacocoReportPath}/report.csv"/> <xml destfile="${jacocoReportPath}/report.xml"/> </jacoco:report> </target> </project> |
在这个步骤中需要注意的是ant任务中的server_ip是K8S master的ip,端口号是我们前面配置的nodeport的值。
3.dump生成测试覆盖率报告
在最后一步中,我们需要将配置好的build.xml文件上传到存有我们源代码的服务器上(我们是Jenkins部署的服务器),然后分别执行
ant dump ant report |
即可生成测试覆盖率的报告,展示如下
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理