【DEVOPS】Jenkins+ Zentao + JMeter自动化测试集成

基于Jenkins + Zentao+ JMeter实现自动化测试集成de起步阶段。

1. 前言

千呼万唤之下,公司内部的自动化测试流程终于蹒跚起步,可惜放眼整个公司,从理论体系到实际落地经验,完全没有现成的果子可以摘。

想要让自动化测试真正在公司内部落地,我们需要从零开始摸索。

本文尝试建立一个起步的自动化测试流程,实现Jmeter脚本自动执行,测试执行结果结构化存储,测试结果消息通知,需求-代码-测试关联的无人工参与,最大化地降低研发人员和测试人员的心智负担,提升协同效率。

2. 流程介绍

相关流程图:流程图 -> CI - 基于JMeter自动化测试

流程顺序(测试人员视角)

  1. 测试人员预先以需求为粒度,编写相应的JMeter测试脚本;并按照“产品-模块-需求”的禅道层级结构来组织创建相应的文件目录结构来存放相应的JMeter测试脚本。
    在这里插入图片描述

  2. 测试人员从Zentao端,以禅道中的测试套件(对应禅道"产品"概念下的一个需求)为粒度,点击提供的二次开发按钮"执行该测试套件",发起指令调用Jenkins相应的任务。
    在这里插入图片描述

  3. Jenkins通过调用JMeter执行用户所选择的测试套件,并将生成的测试报告按照约定的目录结构进行存储。
    在这里插入图片描述

  4. Jenkins在执行完JMeter测试用例之后,需要收集每个测试用例的执行情况,生成自定义格式的汇总信息报表。
    在这里插入图片描述

  5. Jenkins将上一步生成的报表对应的远程访问链接,通过IM工具,QQ邮箱实时推动给在Zentao端发起调用指令的用户。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  6. Jenkins还需要将报表对应的远程访问链接,反写回Zentao中对应测试套件的操作日志下,形成最终的闭环。
    在这里插入图片描述

3. 环境准备

按照以上流程图,涉及到的相关组件有:

  1. Jenkins安装 - 略。作为持续流水线执行的推动者。(被部署在一台Windows Server2012服务器上)
  2. JMeter安装 - 略。执行实际的自动化测试。(与Jenkins一起,被部署在一台Windows Server2012服务器上)
  3. Tomcat安装 - 略。用作文件服务器。(与Jenkins一起,被部署在一台Windows Server2012服务器上)
  4. Zentao安装 - 略。用作产品生命周期管理。(部署在单独的服务器上)
  5. KIM即时消息通知,QQ邮箱等。

4. Jenkins端

作为流程实现的核心点之一,Jenkins的主要配置项是通过pipeline脚本来体现的。


properties([
    parameters([
	    string(name: 'SCRIPTS_WILL_EXECUTE', defaultValue:"XXXXX",  description: '将要被执行的脚本. 如果是多个脚本, 请使用 , 进行分隔'),
	    string(name: 'TEST_SUITE_ID', defaultValue:"555555",  description: 'testsuite id'),
	    string(name: 'TEST_SUITE_NAME', defaultValue:"XXXXXXX",  description: 'testsuite name'),
	    string(name: 'PRODUCT_NAME', defaultValue:"XXXXZ",  description: '产品名称'),
		string(name: 'NOTICED_USER', defaultValue:"XXZ",  description: 'Job执行完毕之后的被通知对象'),

    ])
])

def zNotifyWin(String RECEIVERS, String MSG_TITLE ,String MSG_CONTENT, String EXECUTE_RESULT = "SUCCESS"){
	["powershell.exe","/c","E:/_devops/notifyByIM.ps1 ${RECEIVERS} ${MSG_TITLE} ${MSG_CONTENT} ${EXECUTE_RESULT}"].execute()
}


pipeline {
   agent any
	environment{
		CURRENT_TIME = new Date().format("yyyyMMddHHmmss")
		CURRENT_TIME_READABLE = new Date().format("yyyy-MM-dd HH:mm:ss")

		EXECUTED_SCRIPT_BASE_PATH = "F:/02server"
		// 为了最大化的兼容现有流程和人员习惯,我们决定以中文来组织测试脚本的存放目录结构, 于是这里我们需要将zentao传递来的参数进行url解码(相应的, zentao那边会对参数进行url编码之后进再行传递)
		SCRIPTS_WILL_EXECUTE_DECODED = URLDecoder.decode("${SCRIPTS_WILL_EXECUTE}","UTF-8")
		TEST_SUITE_NAME_DECODED = URLDecoder.decode("${TEST_SUITE_NAME}","UTF-8")
		PRODUCT_NAME_DECODED = URLDecoder.decode("${PRODUCT_NAME}","UTF-8")
	}
   stages {
		stage('Initialize') {
            steps {
                powershell """
                    echo "${JOB_NAME}"
                    echo "清空工作空间"
		        """	

				zNotifyWin("${NOTICED_USER}", "第${BUILD_NUMBER}构建-${TEST_SUITE_ID}-${TEST_SUITE_NAME_DECODED}","启动","skip")
				cleanWs()
            }
        }
        stage('执行JMeter测试') {
            steps {
				script{
				    def scriptArrWillExecute = "${SCRIPTS_WILL_EXECUTE_DECODED}".split(',')

					for(scriptWillExecute in scriptArrWillExecute){
					    def NAME_OF_USE_CASE = "${scriptWillExecute}".split('/')[-1]
						def NAME_OF_USE_CASE_FINAL = "${NAME_OF_USE_CASE}"[4..-1]
						scriptWillExecute = "${scriptWillExecute}".replace("${NAME_OF_USE_CASE}","${NAME_OF_USE_CASE_FINAL}")
												
						powershell """
							\$env:path = \$env:path +"; C:/apache-jmeter-5.1.1/bin/"
							mkdir ${EXECUTED_SCRIPT_BASE_PATH}/03report/html/${scriptWillExecute}/${NAME_OF_USE_CASE_FINAL}-${CURRENT_TIME} -Force
							mkdir ${EXECUTED_SCRIPT_BASE_PATH}/03report/jtl/${scriptWillExecute}/ -Force

							jmeter -n -t ${EXECUTED_SCRIPT_BASE_PATH}/01script/${scriptWillExecute}.jmx -l ${EXECUTED_SCRIPT_BASE_PATH}/03report/jtl/${scriptWillExecute}/${NAME_OF_USE_CASE_FINAL}-${CURRENT_TIME}.jtl -e -o ${EXECUTED_SCRIPT_BASE_PATH}/03report/html/${scriptWillExecute}/${NAME_OF_USE_CASE_FINAL}-${CURRENT_TIME}
							
						"""				
					}
				}
            }
        }     
        stage('抓取JMeter执行结果') {
            steps {
                
                script{
				    def scriptArrWillExecute = "${SCRIPTS_WILL_EXECUTE_DECODED}".split(',')
					
					def links = []
					def htmlResult = ""
					for(scriptWillExecute in scriptArrWillExecute){
					    def NAME_OF_USE_CASE = "${scriptWillExecute}".split('/')[-1]
						def NAME_OF_USE_CASE_FINAL = "${NAME_OF_USE_CASE}"[4..-1]
						scriptWillExecute = "${scriptWillExecute}".replace("${NAME_OF_USE_CASE}","${NAME_OF_USE_CASE_FINAL}")
												
						// 抓取当前测试用例脚本的执行通过成功率
						def passedResults = (["powershell.exe","/c","E:/_devops/jmeterGrapExecuteResult.ps1 ${EXECUTED_SCRIPT_BASE_PATH}/03report/html/${scriptWillExecute}/${NAME_OF_USE_CASE_FINAL}-${CURRENT_TIME}/content/js/dashboard.js"].execute().text.readLines());
						
						// 成功数量, 失败数量, 成功比例
						def okCount = passedResults[0]
						def notOkCount = passedResults[1]
						def okPercent = passedResults[2]
							
						
						def link = "${NAME_OF_USE_CASE}; 通过率 ${okPercent} ; http://172.XX.X.XXX:8080/infces2/${scriptWillExecute}/${NAME_OF_USE_CASE_FINAL}-${CURRENT_TIME} "
						
						
						links += link
						
						htmlResult += """
							<p>
								<a target="_blank" href="${link}">${scriptWillExecute}/${NAME_OF_USE_CASE_FINAL}-${CURRENT_TIME} ; 通过率 ${okPercent}</a>
							</p>
							<br />
						"""
					}


					env.LINKS = links.join("\r\n")
					env.HTML_RESULT = htmlResult
                }
            }
        }
       stage("反写Zentao") {
			steps{
				script{
					// 注意去修改禅道中的  config/config.php 文件 
					//		  $config->features->apiGetModel    = false; 把false修改为true
                    (["powershell.exe","/c","E:/_devops/zentaoPushActionAndHistory.ps1 ${TEST_SUITE_ID} 第${BUILD_NUMBER}构建_${BUILD_URL}"].execute().text.readLines());			
                }
			}
	   }
       stage("推送邮件结果") {
			steps{
				script {	
					emailext(
						to: 'XXXXX@qq.com,YYYYY@qq.com',
						//charset:'UTF-8',
						//mimeType: 'text/html',
						subject: "${PRODUCT_NAME_DECODED}-${TEST_SUITE_NAME_DECODED}-测试成功率(?)-${TEST_SUITE_ID}-${BUILD_NUMBER}",
						body: """
							执行脚本: 
							${SCRIPTS_WILL_EXECUTE_DECODED},
							
							结果参见:
							${env.LINKS}
							
							
						
						""",
						attachmentsPattern: '*.html'
					)
				}
			}
	   }	   
    }
	post {
		success {
			zNotifyWin("${NOTICED_USER}", "第${BUILD_NUMBER}构建-${TEST_SUITE_ID}-${TEST_SUITE_NAME_DECODED}","","SUCCESS")
			
		}
		failure {
			echo 'pipeline post failure'	
			zNotifyWin("${NOTICED_USER}", "第${BUILD_NUMBER}构建-${TEST_SUITE_ID}-${TEST_SUITE_NAME_DECODED}","", "FAIL")
		}
	}
}

以上代码已经能够清晰地说明执行逻辑,这里就不再赘述。

5. Zentao端

对于Zentao端,主要的扩展点是收集用户意图,以参数的形式传递给Jenkins,发起任务调用:

# .\zentao\module\testsuite\view\browse.html.php 
#	为每个testsuite增加一个执行该testsuite的按钮
$runCaseURL = $this->createLink('testsuite', 'runCase', "suiteID=$suite->id&confirm=yes");
		echo html::a("javascript:ajaxDelete(\"$runCaseURL\", \"suiteList\", \"执行当前套件[$suite->id]?\")", '<i></i>', '', "title='执行套件' class='icon-play'");
		

# .\zentao\module\testsuite\control.php
/**
 * Run a test suite.
 *
 * @param  int    $suiteID
 * @param  string $confirm yes|no
 * @param  string $cases array
 * @access public
 * @return void
 */
public function runCase($suiteID, $confirm = 'no', $cases=array())
{
	if($confirm == 'no')
	{
		die(js::confirm($this->lang->testsuite->confirmDelete, inlink('delete', "suiteID=$suiteID&confirm=yes")));
	}
	else
	{
		$currentAccount = $this->app->user->account;
		$currentRealname = $this->app->user->realname;
		
		$suite = $this->testsuite->getById($suiteID);
		
		$productID = $suite->product;
		$this->products = $this->loadModel('product')->getPairs('nocode');
		$productName = isset($this->products[$productID]) ? $this->products[$productID] : '';
		
		// 直接从 本类里的 view() 方法中抄来的
		$cases        = $this->testsuite->getLinkedCases($suiteID);
		$modules      = $this->loadModel('tree')->getOptionMenu($suite->product, 'case');
		
		// 排序, 以安排testsuite中测试用例的执行顺序
		array_multisort(array_column( $cases , 'title' ),SORT_ASC, $cases );
		
		$casesWillExecute = array();
		foreach($cases as $caseID => $case)
		{
			$casesWillExecute[$caseID] = $modules[$case->module] . "/" . $case->title ;
		}

		common::http("http://admin:admin@172.XX.X.XXX:8080/jenkins/job/%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95/job/000-LQ-BatchExecuteJmxByModule/buildWithParameters?delay=0sec",array("SCRIPTS_WILL_EXECUTE"=>urlencode(join(",", $casesWillExecute)) , "TEST_SUITE_ID"=>$suiteID, "TEST_SUITE_NAME"=>urlencode($suite->name),"PRODUCT_NAME"=>urlencode($productName), "NOTICED_USER"=>$currentAccount),array());			
				
		$this->executeHooks($suiteID);
		
		/* if ajax request, send result. */
		if($this->server->ajax)
		{
			if(dao::isError())
			{
				$response['result']  = 'fail';
				$response['message'] = dao::getError();
			}
			else
			{
				$response['result']  = 'success';
				$response['message'] = '';
			}
			$this->send($response);
		}
		die(js::reload('parent'));
		
	}
	
}

6. 优化

  1. 只执行套件中的部分用例。
    在这里插入图片描述

7. Links

  1. 【DEVOPS】zentao二次扩展开发实践 - ERP任务报工同步
  2. jenkins+ant+jmeter实现自动化集成(详解)
  3. Using powershell to read html content
  4. Jenkins高级篇之Pipeline方法篇-Pipeline Basic Steps-3-方法mail
  5. Zentao官方手册 - api机制简介
ZenTaoATF是由禅道开发团队开发的自动化测试框架,它只有一个脚本文件,可以支持各种脚本语言编写的自动化测试脚本,语法简单,使用灵活,后续会和禅道项目管理软件进行绑定和集成。 它的基本工作原理如下: 首先,你需要写一个测试脚本来实现你正常的测试逻辑。现在支持php, python, ruby, lua, tcl, bash。 这个测试脚本里面按照自己所测程序的业务逻辑编写,比如链接数据库,查询某一个记录等等。 然后通过注释的方式为这个测试脚本写上基本的用例信息:标题,步骤和预期结果,采用的是yaml语法格式。 << title: helloworld. expect: helloworld. TC 然后执行zt脚本,zt脚本会扫描当前目录下面的带有用例标志的脚本,然后调用执行,得出每个脚本的实际输出,然后和用例里面事先记录好的预期结果进行比对,如果相匹配,则成功,如果失败,则计算diff信息。 相比较于其他的自动化测试框架或者单元测试框架来将,ZenTaoATF的主要特点如下: 1. 简单 1.1 程序简单 整个自动化测试框架只有一个zt文件,使用php开发,安装和运行都非常的方便。 1.2 语法简单 ZenTaoATF的语法只有简单的标题,步骤,预期结果,采用yaml格式,撰写比较方便,可读性也很好。 2. 跨语言 ZenTaoATF框架本身是使用PHP开发的,但它支持各种各样的脚本语言,比如python, ruby, lua, tcl, bash, bat等。 3. 跨平台 ZenTaoATF可以运行在各个平台下面。(现在暂时只支持linux,windows1.1版本加入) 4. 和禅道项目管理软件集成后续版本我们会和禅道开源项目管理软件进行集成和绑定,脚本和用例可以做到有机的结合。 下载地址:https://github.com/easysoft/zentaoatf/archive/zentaoatf.1.0.20130318.zip 项目地址:https://github.com/easysoft/zentaoatf/如何安装:1. 现在zentaoatf暂时只支持linux和bsd系统,请先安装好php的运行环境。 2. 下载zentaoatf包,将其解压缩到一个目录。 3. 解压缩之后,有一个zt的php脚本。使用命令chmod a rx zt 给它增加可执行权限。 4. 为了后面方便使用,可以考虑将zt这个脚本拷贝到/usr/local/bin目录下面。如何运行:  1. 下载zentaoatf包之后,里面有两个目录,一个是基本的语法,一个是各种脚本语言的格式。 2. 可以在这个目录下面执行执行 zt,就可以运行自动化测试脚本。执行的效果如下: 标签:ZenTaoATF  自动化测试
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值