[Jmeter(十二)关联]
关联在实际业务需求中是随处可见的,比如:支付需要提交订单成功的订单号;修改个人资料需要登录成功响应报文信息。。。总之关联无处不在,今天来记一记Jmeter的关联功能。
Jmeter关联的方法比较常用的是正则表达式提取器,正则表达式提取器属于后置处理器,那么久抛出了一个比较大的知识点----正则表达式;
其实,正则表达式就是一种文本模式,相信都在windows我的电脑中搜索过文件嘛,那么肯定使用过“*”,其实都是类似。
记几个比较常用的:
^ ----->为匹配输入字符串的开始位置。
$ ----->为匹配输入字符串的结束位置。
. ------>匹配单字符。
+ ------>匹配一次或多次(大于等于1次)
?------>贪婪符,匹配到立即停止。
\d------->匹配一个数字字符
\n ------>匹配一个换行符
\r ------->匹配一个回车符
。。。。。
正则表达式会写,用这个很eazy。
现如今,restful风格(http+json)的接口很是流行,响应信息为json格式的,那么就还能简单一点,不用正则表达式那么复杂。
而json的数据类型有对象、数组、字符串、数字(整型、浮点)、布尔、null;使用jsonpath语法来进行提取判断。
Jmeter也有专门提取json的提取器,当然是第三方插件咯。。。Json Path Extractor
json是key-value类型的,当然也会碰到数组,关于这些也来记一记。 那么有jsonpath,也就有xpath^_^
同样,它对于xml类型的报文信息提取比较简洁,数节点即可^_^。上方表格第一列便是xpath的相关语法。只是需要谨记的一点就是jsonpath数节点是从0开始数,而xpath数节点是从1开始数。
[Jmeter(十三)Debug Sampler]
上篇记录了关联的一些内容。当然,关联是有“风险”的,经常写的正则表达式,jsonpath,xpath自己认为是没问题的,可是就是跑起来有问题,百思不得其解,究竟是数据问题还是表达式的问题,本篇就来记录一个“神器”-----Debug Sampler
老规矩,先看看官方文档作何解释。
啊哈,它是作为监听变量的一个采样器。
无论是从参数化、关联等涉及到变量输出的内容,Debug Sampler都会进行打印出。
测试计划中设置的变量
参数化的变量
执行。。。。
可以看到无论是测试计划中的变量还是外部文件的变量,DebugSampler在响应报文中都打印了出来,有这样的功能就定位问题速度便会提升好多。
[Jmeter(十四)If Controller]
If Controller---如果控制器;属于逻辑判断类型的组件,其实学过代码的都知道if--else,while等都是常用的逻辑判断关键词,Jmeter也提供了逻辑判断--If Controller便是如此。
写个例子加深印象。
DummySampler是Jmeter第三方插件中的比较典型的一个插件,该插件相当于小型mock,可以自定义请求响应报文内容,mock部分内容后续记载。
此处外边还有一个DummySampler2,是一样的内容,是为了在结果树中能够更直观的看出if--controller的效果。
OK,执行查看结果树内容。
第一个随机数为440,440<500,因此在进入了if判断内,执行结果为Dummy Sampler;
随机数为440,也执行了外边的Dummy Sampler2;可以看到iteration 都为1,因此是同个迭代数,既执行了if条件内,也执行了条件外。
再看下边的内容,其实中结果树中的标识已经看的出来,随机数在哪次迭代小于了500.
501>500,因此不进入if判断
同样的道理。
[Jmeter(十五)while Controller]
while Controller是控制循环的Controller,条件判断的Controller。先看看官方Demo。
while Controller控制它的子对象,直到false为止。并且还提供了三个条件:
1、blank:最后一个循环中失败时的空白退出循环.
2、LAST:最后一个循环中失败时的退出循环。如果在循环失败前的最后一个示例,则不要进入循环。
3、Otherwise:当条件等于字符串“false”时退出(或不进入)循环。
blank、Otherwise不做解释。
以Dummy Sampler为例,添加了三个Dummy Sampler,1是有失败断言的,按照while的条件LAST,1如果失败,不会进入while中。执行以下:
条件判断正确。
将1中的断言disable掉,那么进入while中,到3的断言又是失败的,因此也是循环执行一次:
将3的断言也disable掉,那么Dummy Sampler没有失败的,因此不停的会循环2和3,不会跳出去。
OK,LAST条件的判断便是如此。
[Jmeter(十六)Runtime Controller]
Runtime Controller-----运行时间控制器;控制其下的Sampler运行时间。
该控制器较为简单,官方文档也没作太多说明。照着Blazemeter写个例子:
运行,查看结果。
可以看到运行了5秒。
[Jmeter(十七)Interleave Controller]
Interleave Controller----交错控制器,对于交错控制器官方文档解释的很清楚,上例子:
可以看到运行一次 Interleave Controller下的Sampler,运行一次 Interleave Controller之外的Sampler。如此不停的交错循环。
Demo2:
两个交错控制器,执行结果:
可以看到,循环交错1、A;2、B;3、C。
Demo3:
主交错器选择ignore sub-controller blocks,加循环数,那么效果和Demo2中一致:
OK、 Interleave Controller先记录到这,后续业务场景进行添加补充。
[Jmeter(十八)Random Controller and Random order Controller]
Random Controller就比较简单了,完全随机!毫无章法。
毫无任何规律的运行。
还有一个Random order Controller,随机顺序控制器就像一个简单的控制器,它将最多执行一次子元素,但节点的执行顺序是随机的。
看Demo:
[Jmeter(十九)Module Controller and Include Controller]
Module Controller ---模块控制器
测试计划设置“独立运行没每个线程组”
线程组2中使用Module Controller执行线程组1中的Sampler:
紧接着,将线程组1disable掉,执行:
结果树中是从线程组2开始执行,Module Controller依然可以控制到线程组1中的Dummy Sampler。
可以再增添个Test Fragment,试一试。
This is Module Controller
Include Controller:
旨在使用外部文件片段
Module VS Include Controller:
Module Controller:重新利用可以在同一个JMX。
Include Controller:可以将外部的JMX拉进来使用。
[Jmeter(二十)Beanshell or JSR223]
有关Beanshell和JSR223组件的部分,早就想写一大篇幅随笔进行记录,苦于不知如何去描述这两部分的内容,一直在修改随笔。
介绍一下Beanshell:
Beanshell是轻量级Java,支持对象式的脚本语言特性,亦可嵌入到JAVA源代码中,能动态执行JAVA源代码并为其扩展了脚本语言的一些特性,像JavaScript和perl那样的弱类型、命令式、闭包函数等等特性都不在话下。(飞升传送门:www.beanshell.org/)
Beanshell在Jmeter有着相当强的地位,被称之为Jmeter脚本语言的King。不过前段时间在Blazemeter中看到一篇文章----《IS Beanshell Dead?》,仔细阅读了一番,其大致之意便是自Jmeter3.1以来,JSR223的脚本语言对Groovy的默认化,相对于Beanshell,开发人员更为喜欢Groovy;不过对于我们来说,到底是选择Beanshell还是Groovy?现阶段我觉得没必要讨论这样的话题。不过我认为适当的转变是有必要的,盲目跟风那绝对是大忌!有兴趣的可以去看看,究其根本原因是什么。金庸老爷子是这样描述独孤求败:草木竹石均可为剑。自此精进,渐入无剑胜有剑之境。剑神不是剑法有多高超,而是功力太深厚,草木竹石皆可为剑!Beanshell、Groovy皆可为剑!哈哈,看看Jmeter中的Beanshell和JSR223以及它们所能实现的功能吧!
首先,Beanshell和JSR223组件是涉及代码部分的组件,至于用途,能量所在来进行一一记载:
Beanshell和JSR223几乎在Jmeter的每部分都有涉及到:
1、Sampler
2、PreProcessor
3、PostProcessor
4、Timers
5、Assertions
6、Listeners
那么先添加一个Beanshell Sampler:
图上标注了五部分内容,几乎除了名称和注释以外的所有部分,都是十分重要的内容。来进行一一解读:
先贴官方文档:
Rest bsh.Interpreter before each call:(check box)不做解释,具体见官方文档中给出的示例(或转Best Practices - Beanshell seripting)
Parameters(String Parameters and String 【】bah.args):传递参数,可将GUI脚本中创建的Parameters参数传递至Beanshell脚本中。在Beanshell脚本中引用是使用bsh.args【x】进行实例化。
Script file:导入Beanshell脚本运行文件。文件名存储在脚本变量名中。
Script:脚本编写处。(Beanshell语法)
而最下方的一段话,我是特意框了起来,很多人可能会忽略,但是这段话中列举的变量便是为Beanshell脚本定义的:
SampleResult, ResponseCode, ResponseMessage, IsSuccess, Label, FileName, ctx, vars, props, log
用过的人肯定都很熟悉这里边的变量,具体用途同名一致!
OK、上个简单的例子(该Demo来自Blazemeter的示例,大家有兴趣可以去看看):
使用随机函数Random作为一个随机响应数据。
将Dummy Sampler的响应结果以及响应数据保存至“Response_data”变量中。
添加Beanshell Sampler进行使用该变量:
将Response_data的变量信息打印至日志;
将Label打印至日志;
定义该Sampler的响应状态码以及响应信息。
为直观,添加一个Debug Sampler查看变量读取情况:
响应的随机数为56
在上方的Beanshell Sampler定义的Response Code和message。
Debug Sampler的变量读取是正确的。
日志面板中打印出的Response data以及Label名称。
当然逻辑判断和引入外部jar包在有必要的情况下依然是支持的。
一个将数据写入文件的小Demo:
判断目录下有没有该文件(此处默认为/bin目录下),如果存在,将其删除掉,创建新的文件
Dummy Sampler自定义写入request和response信息。(涉及函数__UUID、__iterationNum)
将该响应信息中id、name、password提取出来,定义为新的变量。
将提取出来的数据写入该文件中。
OK,我已经运行过一遍了,看看写入数据是否成功。
我的/bin目录下是生成了该文件,打开数据是否写入:
可以看到是写入成功了。
Setup Group Thread中做创建文件和数据写入工作。
bzm - Concurrency Thread Group----------该线程组大致就是一个测试setup线程组中的数据过程。
该线程组下进行以参数化的方式读取生成的文件:
OK,Beanshell几乎用于处理复杂业务逻辑,例如生成随机手机号码、随机身份证号、加密、解密等业务。其灵活之处更在于可以实例化外部java文件。
[Jmeter(二十一)Jmeter常见问题及场景应用]
Jmeter作为工具来讲,已经是一个相对比较牛掰的工具,除了它能够支持那么多协议以及方法之外,更在与它的前置处理以及后置处理、同步监控的人性化。当然,所有的工具、框架都是作为业务的支撑,如果不能满足我们实际业务的工作,那么便没有那么大的‘吸引力’。细数在实际工作中遇到的坑吧,都是使用Jmeter如何解决的。
一、乱码问题(Jmeter(三十一)Jmeter Question 之 乱码解读)。
二、实际业务场景---仅需某一接口一次请求,其他接口循环请求(如登陆一次、提现N次)(Jmeter(三十)Jmeter Question 之 循环+事务的妙用)。
三、当前接口需要上个接口的Response信息(正则、xpath、Beanshell、jsonpath),将上个接口进行参数化再进行循环。
典型场景:‘获取余额’需要‘登陆’的Response,那么便需要将登陆接口进行参数化(只为满足业务要求),那么加入循环的话,该如何进行解决。
四、将响应信息写入本地文件中。
五、将执行过程(GUI方式运行)日志写入本地文件中。(有必要筛选error信息)
六、随机模拟生成若干手机号码、身份证号码。(Jmeter(四十)BeanShell范例)
七、加解密处理。(Jmeter(二十四)Jmeter-Question之“加密请求参数”)
八、测试https接口。(Jmeter(二十八)Jmeter-Question之“HTTPS请求”)
九、Jmeter发送电子邮件。
十、线程与线程之间的变量传递。(Jmeter(二十二)Jmeter-Question之“不同线程组之间传递变量”)
十一、Jmeter测试上传、下载文件接口。
十二、ThinkTime的模拟。
十三、Jmeter批量造数据。(Jmeter(二十三)Jmeter-Question之“批量造数据”)
十四、集成Jenkins遇到的坑。(Jmeter(二十六)Jmeter-Question之“集成Jenkins”)
十五、请求类型的坑(content-type)。
十六、监控Jmeter使用的资源手段。
十七、Jmeter分布式部署测试。(Jmeter(四十一)分布式测试(转!))
十八、使用Beanshell进行操作请求头、请求数据等(重构请求数据、cookie等)。
十九、Cookie打印传递。(Jmeter(三十四)Jmeter-Question之“Cookie获取”)
二十、JDBC Sample如何发送多条SQL(Jmeter(三十八)Jmeter Question 之 ‘批量执行SQL语句’)
二十一、启动时,dos终端提示WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5.(Jmeter(四十四)启动提示 Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5.)
二十二、容量测试控制业务比例(Jmeter(四十五)Logic Controllers 之 Throughput Controller(控制业务比例) )
[Jmeter(二十二)“不同线程组之间传递变量”]
Jmeter中增添原件是以线程组为‘基本单位’的,贴张图,显而易见:
测试计划节点下的内容
线程组节点下的内容
因此,变量只能在当前的线程组下进行传递。做个小Demo,立即试试:
测试结论:不同线程组之间的变量不能共享。那么该如何解决该问题呢?
解决方案:使用函数${__setProperty()}
${__property()}
组件:BeanShell Assertion
实现线程组之间的变量公用。
[Jmeter(二十三)“批量造数据”]
日常工作中,无论是在做功能测试、接口测试还是性能测试,经常会有这么一个场景出现,“那个谁谁谁,帮我加几条订单”,“那个某某某,给购物车增添几个产品”,“在数据库加几百条数据”。。。等等,通常少数量,或者能够人为解决的就人工解决,那么如果是数量比较大,数万条数据,总不可能一条一条进行增添吧?
So,在造数据这块通常能够想到的便是数据库通过“存储过程”来进行增添,或者外部程序or工具,Jmeter当然也支持的,做的小Demo:
原件:JDBC连接池
计数器
JDBC Request
具体配置前面的文章已经提到过,此处不再解释
线程组加入循环次数
OK,100条数据批量增添,且主键不重复。
直接运行
可以看到数据库中的数据已经增添成功了!
当然,Jmeter总归是测试工具进行批量增添,Jmeter自身也比较耗机器性能,因此有些情况也需要使用存储过程。
因此,再记一个批量造数据的第二种方法--“存储过程批量造数据”:
DELIMITER $$
drop procedure if exists proc_auto_insertdata$$
create procedure proc_auto_insertdata ()
BEGIN
declare init_data int;
set init_data=1;
while init_data <=20000 DO
insert into users values (init_data,concat('用户-',init_data));
set init_data=init_data+1;
end while;
END$$
[Jmeter(二十四)“加密请求参数”]
日常接口测试碰到参数加密的情况不在少数,当然与之相对的也有解密。直接记录实例:
排除各家用的不一样的加密方式,用的最多的还是MD5加密(16,32)。Jmeter3.2版本已经有解决方案
1、${__MD5(,)}函数(默认32位),当然也有其他类型的加密函数:base64
用法很简单,与一般函数使用一般无二。
粘贴一个实例:
加密之后的结果一致。
2、第一种方法只是Jmeter自带功能以满足需求,当然,也有满足不了的时候,因此第二种方法就显得相对灵活一些,还是MD5(32)加密。
解决思路:使用Beanshell Sampler进行实例化外部jar包;
先贴段MD5加密代码:
1 package hehe.md5;
2
3 import java.security.MessageDigest;
4 import java.security.NoSuchAlgorithmException;
5
6 public class Str2MD5 {
7 //32
8 public String MD5(String sourceStr) {
9 String result = "";
10 try {
11 MessageDigest md = MessageDigest.getInstance("MD5");
12 md.update(sourceStr.getBytes());
13 byte b[] = md.digest();
14 int i;
15 StringBuffer buf = new StringBuffer("");
16 for (int offset = 0; offset < b.length; offset++) {
17 i = b[offset];
18 if (i < 0)
19 i += 256;
20 if (i < 16)
21 buf.append("0");
22 buf.append(Integer.toHexString(i));
23 }
24 result = buf.toString();
25 System.out.println("MD5(" + sourceStr + ",32) = " + result);
26 // System.out.println("MD5(" + sourceStr + ",16) = " + buf.toString().substring(8, 24));
27 } catch (NoSuchAlgorithmException e) {
28 System.out.println(e);
29 }
30 return result;
31 }
32 }
当然,可以在外部写个类进行测试该段代码是否能够加密成功。
1 package hehe.md5;
2 import hehe.md5.Str2MD5;
3
4 public class test {
5 public static void main(String[] args){
6 String res = new Str2MD5().MD5("dj123456");
7 System.out.println(res);
8 }
9 }
测试结果也是与上方一致的。
将该段代码打成jar包,放入Apache Jmeter\lib\ext\目录下使用Beanshell Sampler进行外部实例化:
加密之后的结果与上方一致。
[Jmeter(二十五)Jmeter之系统函数]
都忘了Jmeter4.0已发布((^▽^))具体优化项还没体验,记录一下,传送门:jmeter.apache.org/download_jm…
Jmeter的系统函数已经发布了许多函数,并且实现了N多功能,(可以自行开发自定义函数哦~)记几个常用的函数:
${__time(,)}-->时间戳:
参数一:日期格式---${__time(yyyy-MM-dd-HH-mm-ss,)}
这里边有个误区,大写M是月份,小写m是分钟。
参数二:存放获得当前时间值的参数名称
${__Random(,,)}-->随机函数
填入数值区间即可
${__StringFromFile(,,,)}-->读取本地文件(csv文件)
类似CSV Data Set Config,但是比它更为强大一点,__StringFromFile函数的强大之处在于它可以支持从多个文件中读取数据。
例如给定函数表达式${__StringFromFile(F:\jmeter-project\test-project\config-data\test#‘.’csv,,1,99)},那么__StringFromFile函数将会读取该目录下的test1.csv,test2.csv。。。test99.csv作为需要读取的数据文件。
例如给定函数表达式${__StringFromFile(F:\jmeter-project\test-project\config-data\test0000#‘.’csv,,1,99)},那么__StringFromFile函数将会读取该目录下的test0001.csv。。。test00099.csv作为需要读取的数据文件
参数一:文件名(绝对路径)
参数二:存放取的数据的参数列表
{__intSum(,,)}和{__longSum(,,)}-->分别用来进行整型和长整型数据的加法运算。这两个函数均可为可变参数列表的函数,可用来进行任意个整型或长整型数据的加法运算。
${__setProperty(,,)}-->用于在运行时设置Jmeter中的任何属性的值(参见“线程组之间变量传递”)
{__eval()}和{__evalVar()}-->用于计算一个参数表达式的值。
例如:给定参数值:table = mytable
column = username
username = dennis
SQL = select {table} where userid = '${username}'
使用{__eval({SQL})}便得到的值为 select username from mytable where username = 'dennis'.
{__evalVar()}与前者基本一致,唯一不同的是,{__evalVar()}函数可以将计算后的值存放到一个参数中。
{__threadNum}与{__machineName()}-->这两个函数用于获得执行当前函数的线程号(1开始)和执行当前Test plan的机器名。在调试或记录日志时,可以用这两个函数输出与线程号和机器相关的信息
{__unescapeHtml()}和{__escapeHtml()}-->这两个函数用于对字符串进行编码/解码处理。名称即可看出{__escapeHtml()} 对于任意字符串按照HTML格式进行编码;而{__unescapeHtml()}函数则是对HTML编码后的字符串进行解码。
${__javaScript(,)}->该函数用于执行一段给定的JavaScript脚本,返回值为该JavaScript执行的结果。
${__BeanShell(,)}-->该函数允许用户运行一段自定义的Beanshell脚本,脚本可以用来设置Jmeter的属性和参数值,也可返回数据。详情参见前面Beanshell随笔。
具体函数源码可在jmeter\apache-jmeter-3.2\lib\ext目录下找到ApacheJMeter_functions.jar的包,进行查看其处理逻辑
可仿照其源码进行自定义逻辑,自行开发自定义函数,打包至lib\ext目录下,进行使用。
一个典型的应用:在命令行运行Test plan 时,从命令行指定Thread Group中的线程数和脚本运行时长:
使用__property和__P函数获取属性值
类似这样使用:
命令行中可直接指定线程的数量(默认为1):
jmeter -n -t demo.jmx -Jthread_count =10 -Jduration = 60
该Test plan以10线程运行,运行时长60s(具体会在第三方集成用到)
[Jmeter(二十六)“集成Jenkins”]
Jenkins,最初被称为Hudson,是一个Java语言编写的开源持续集成工具。Jenkins在持续集成领域的市场份额居于主导地位,其被各种规模的团队用于各种语言和技术的项目中,比如.net。ruby。Groovy。Java等。
Jenkins的用户界面非常简单、直观、增加了视觉上的吸引力,其次具有良好的扩展性,能够及其灵活和方便的迎合你的想法。它有数以百计的开源插件可供使用,而且每周都有更多的插件贡献进来。这些开源插件覆盖系统的版本控制、构建工具、代码质量度量、构建通知、外部系统集成、用户界面定制化、游戏等。 ---摘自<Jenkins权威指南>
Jenkins作为一款优秀的集成工具,是非常成功的。从单方面的简洁GUI、功能等优秀特点,更多的是迎合了现今软件的开发模式以及管理模式,本人有了解,在去年的一场Jenkins的技术分享会上,有提到Jenkins的插件已经达到1340多个,真是一个恐怖的数字。因此,称之它为一手插件堆起来的功能,一点也不为过。
当然,Jenkins优秀归优秀,那么它能帮我们来干什么呢?Jmeter又如何和Jenkins进行相互‘沟通’进行工作呢?
(具体Jenkins的安装方式便不在记了)
自3.0起Jmeter,已经有了丰富的图表展示,之前的话是用借用ant进行构建,ant生成图形报表,而3.0以后,可以直接显示自己的图表,便不必要(当然也是也可以的,后续记上)去加ant。
其具体的实现方式还是Jmeter的no GUI方式运行,使用Jenkins的插件将Jmeter生成的图表展现出来。OK,上例子。
使用GUI界面进行调试,线程10,10s内启动,循环10次,一个Sampler,有100次Sampler进行请求。
执行命令,如图,运行生成jtl、log文件
加上-e -o参数(及相应路径)生成Jmeter自带的html报告
OK、我这个是执行过的,报告也展现了出来。
那么Jenkins上是怎么玩的呢?直接上Demo。
直接新建一个自由风格的project。
源码管理、构建触发器暂且用不到(作为一个简单的demo)
直接构建、选用插件Execute Windows batch command;(因为我的tomcat在本地搭建,本地为windos环境,因此会选用该插件,如若为linux,先用Excute shell即可)
上方在终端命令行中运行的命令直接放入该插件。
首先先将本地的jmx文件以Jenkins进行运行一遍,并输出jtl、log的日志文件。
保存并点击“立即构建”
构建成功,红色即失败。
查看本次构建的日志信息。
可以看的出来,本地文件是构建成功的,再至本地日志存放目录,查看日志文件是否生成。
日志文件也是生成了。可使用Jmeter的监控器(结果树等)进行查看日志信息。
OK,再进行加入生成report的参数-e、-o进行生成可视化报告。
立即构建。
至本地查看该report文件是否生成。
截至此处,使用Jenkins进行构建Jmeter工程并生成各种日志文件已经完成。
肯定还是有困惑的,那就是直接在Jenkins查看它的日志报告,Jenkins是完全支持的。
很eazy,增加构建后操作:
插件Publish HTML reports进行查看。
OK,接着构建。
可以看到该结果是成功的,那么至Jenkins目录HTML Report进行查看该报告。
什么鬼,竟然什么信息都查看不到?
网上有相关问题的回答。不过此处也记录一下,这是由于Jenkins的安全机制,将css等页面渲染信息都进行了拦截,可以打开F12,开发者工具进行查看拦截的信息。
那么也是有解决方案的。
有暂时解决方案,也有一劳永逸的解决方案。
整理了一下方案,大致有三种:
1、如果Jenkins是在Tomcat下进行搭建的,那么可以直接修改Tomcat的启动文件参数(具体本人并未实现)
2、使用Jenkins的脚本命令行工具,加一段Groovy代码“System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")”
该方案为暂时性解决方案,本次构建可以进行生效,可是当重启一次Jenkins便会失效,重新加入该段代码。
3、在构建过程中加入一个步骤Execute system Groovy Script,输入“System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")”代码段,便可一劳永逸,即使重启,该段功能也不会失效。
在此,便使用第三种方案进行实现。
OK,再进行构建一次,查看HTML Report。
嗯哼、将数据展现了出来。
收工~~~看书。
对了、如果在windows的环境下,须将Jenkins的工作目录等移动至其他目录(尽量不要放在C盘下),C盘为系统盘,涉及各种权限,工作目录在C盘下进行构建的话,有时会出现,本地的日志文件等全部都生成了,但是Jenkins的构建结果就是失败,报一系列莫名其妙的错误~~
至于Jenkins移动工作目录,很eazy,添加环境变量、工作目录迁移即可,百度一下更精彩!
[Jmeter(二十七)Jmeter Question 之“集成Ant+Jenkins自动化”]
首先介绍一下Ant。Apache Ant,是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发。由Apache软件基金会所提供。
是的、还是Apache家的产品,去其官网看看其简介。
Apache Ant is a Java library and command-line tool whose mission is to drive processes described in build files as targets and extension points dependent upon each other. The main known usage of Ant is the build of Java applications. Ant supplies a number of built-in tasks allowing to compile, assemble, test and run Java applications. Ant can also be used effectively to build non Java applications, for instance C or C++ applications. More generally, Ant can be used to pilot any type of process which can be described in terms of targets and tasks.
Ant is written in Java. Users of Ant can develop their own "antlibs" containing Ant tasks and types, and are offered a large number of ready-made commercial or open-source "antlibs".
Ant is extremely flexible and does not impose coding conventions or directory layouts to the Java projects which adopt it as a build tool.
Software development projects looking for a solution combining build tool and dependency management can use Ant in combination with Apache Ivy.
The Apache Ant project is part of the Apache Software Foundation.
Apache Ant是一个Java库和命令行工具,其任务是将构建文件中描述的进程作为相互依赖的目标和扩展点。Ant的主要用途是构建Java应用程序。Ant提供了许多内置任务,允许编译,组装,测试和运行Java应用程序。Ant还可以有效地用于构建非Java应用程序,例如C或C ++应用程序。更一般地说,Ant可以用来试验任何类型的过程,可以用目标和任务来描述。
Ant是用Java编写的。Ant的用户可以开发自己的包含Ant任务和类型的“antlib”,并提供大量现成的商业或开源“antlibs”。
Ant非常灵活,并且不会将编码约定或目录布局强加给采用它作为构建工具的Java项目。
寻找结合构建工具和依赖管理的解决方案的软件开发项目可以将Ant与Apache Ivy结合使用。
Apache Ant项目是Apache Software Foundation的一部分。
既然也是Apache家的产品,那么也能够与Jmeter进行集成的。
来具体记一记Jmeter是如何与Ant进行集成的,而后又如何延伸至Jenkins的。
Ant的搭建环境不再记录。
其主要的还是有关于xml配置文件的编辑。
1、首先将${jmeter_home}\extras\ant-jmeter-1.1.1.jar,copy至{ant_home}\lib下。
2、定义build.xml文件。
<?xml version="1.0" encoding="UTF-8"?>
<project name="ant-jmeter-test" default="run" basedir=".">
<taskdef resource="net/sf/antcontrib/antlib.xml" />
<!--需要改成自己本地的 Jmeter 目录-->
<property name="jmeter.home" value="F:\jmeter\apache-jmeter-3.2"/>
<property name="report.title" value="report"/>
<!-- jmeter生成jtl格式的结果报告的路径-->
<property name="jmeter.result.jtl.dir" value="F:\jmeter-project\Demoproject\result\jtl"/>
<!--jmeter生成html格式的结果报告的路径-->
<property name="jmeter.result.html.dir" value="F:\jmeter-project\Demoproject\result\html"/>
<!-- 生成的报告的前缀 -->
<property name="ReportName" value="TestReport"/>
<property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}.jtl"/>
<property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}.html"/>
<target name="run">
<antcall target="test"/>
<antcall target="report"/>
<!--antcall target="mail"/-->
</target>
<target name="test">
<taskdef name="jmeter" classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/>
<jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
<!--脚本存放路径-->
<testplans dir="F:\jmeter-project\Demoproject\Script" includes="*.jmx"/>
<property name="jmeter.save.saveservice.output_format" value="xml"/>
</jmeter>
</target>
<path id="xslt.classpath">
<fileset dir="${jmeter.home}/lib" includes="xalan*.jar"/>
<fileset dir="${jmeter.home}/lib" includes="serializer*.jar"/>
</path>
<target name="report">
<tstamp>
<format property="report.datestamp" pattern="yyyy/MM/dd HH:mm"/>
</tstamp>
<xslt
classpathref="xslt.classpath"
force="true"
in="${jmeter.result.jtlName}"
out="${jmeter.result.htmlName}"
style="${jmeter.home}/extras/jmeter-results-detail-report_21.xsl">
<param name="dateReport" expression="${report.datestamp}"/>
</xslt>
<copy todir="${jmeter.result.html.dir}">
<fileset dir="${jmeter.home}/extras">
<include name="collapse.png"/>
<include name="expand.png"/>
</fileset>
</copy>
</target>
<path id="lib_classpath">
<fileset dir="${basedir}/">
<include name="mail*.jar" />
<include name="activation*.jar" />
<include name="commons-email*.jar" />
<include name="ant-contrib*.jar" />
</fileset>
</path>
</project>
其实,在${jmeter_home}\extras\下有一个build.xml的文件,不过相对来说比较繁琐,此处是一个参照网上大神的一个精简版。需要更换的目录,代码段已经插入了相关注释。
此处是构建
F:\jmeter-project\Demoproject\Script目录下的全部.jmx文件,因此使用*.jmx
该目录下存放两个jmx脚本,一共是103个Sampler
3、进入存放build.xml的目录,执行ant命令进行构建。
4、
可以看到,已经构建成功,至日志保存目录进行查看。
这个是Ant构建生成的html报告。
当然报告的模板也是能够进行设计。该处使用的是${jmeter_home}\extras目录下的jmeter-results-report_21.xsl。
OK、再集成至Jenkins又当如何集成呢?
上篇是通过Jmeter的NO-GUI方式直接以Jenkins进行执行dos命令行命令,构建。
使用Ant的话更为简洁。
所需插件:Invoke Ant
填写参数Targets 、 Build File
Targets为build.xml文件中的target name。
Bulid File为build.xml文件在本地的路径
至于构建后的操作:Publish HTML Reports主要是用作显示报告。
OK,开始构建。
构建成功、至本地查看报告生成或者Jenkins面板都可以。
报告文件已生成。
我们作为测试人员,使用Ant无非就是批量执行jmeter的脚本文件,再使用Jenkins进行自动构建(定时自动构建),不就实现自动化了么,日常的回归,冒烟等等皆可满足。
[Jmeter(二十八)“HTTPS请求”]
前面在Jmeter-Question中有提到若干问题,有时间呢,我也会进行继续编写随笔,梳理自己的知识,本篇呢,便来记Jmeter发送https请求的过程
内容大致与blog.csdn.net/fvafuc/arti…
以baidu为例子:(google浏览器)
如图标记处,可进行查看证书、cookie相关信息
点击该证书按钮。查看证书信息。
一路下一步,将该证书导出至本地。
如图所示,该证书已被导出至本地。
接下来,将导出的证书打成.store。
示例dos命令:keytool -import -alias "baidu.store" -file "C:\Users\Richered\Desktop\baidu.cer" -keystore baidu.store
设置访问密码(必须的)
设置完成,最终确认是否信任此证书,“Y”即可,将证书打成.store格式完成。
OK,打开Jmeter进行请求。
将.store的证书导入。
便可以进行相关的https操作了。
出现该框,输入上方dos命令行中设置的访问密码即可。
只是一种使用Jmeter解决https请求的手段。
最后、将大神指出的问题也一并记录。(本人环境正常,并没有碰到,但不代表以后不会碰到)
提示证书错误的话,需要更换JDK的版本(本人1.8).
配置相关环境变量等。
[Jmeter(二十九)Jmeter-Question之“Ant集成报告模板优化”]
也是在和朋友探讨的时候,发现一个问题,Jmeter在与Ant集成的时候,通常选用的模板是jmeter自带的两个样式表
该自带的样式,节省了大家搭建框架的时间,不需要自己重新写样式,当然也相对简洁;
做接口测试时,我们通常跑的接口有很多,其日志的也是相对比较大的,因此对于一些报错原因、响应报文想查看,便形成了一种障碍;自带的模板不带有查看响应报文的样式,因此需要一种能够直观查看一些类似成功率、失败率以及响应有误能够直接查看的样式模板。
找到一份模板,是copy这位大师的模板。www.cnblogs.com/puresoul/p/…
样式源码Copy一份。
1 <?xml version="1.0" encoding="GB2312"?>
2 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
3 <xsl:output method="html" indent="no" encoding="UTF-8" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" doctype-system="http://www.w3.org/TR/html4/loose.dtd"/>
4 <xsl:strip-space elements="*"/>
5
6
7 <xsl:template name="detail">
8 <xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />
9
10 <xsl:if test="$allFailureCount > 0">
11 <h2>Failure Detail</h2>
12
13 <xsl:for-each select="/testResults/*[not(@lb = preceding::*/@lb)]">
14
15 <xsl:variable name="failureCount" select="count(../*[@lb = current()/@lb][attribute::s='false'])" />
16
17 <xsl:if test="$failureCount > 0">
18 <h3><xsl:value-of select="@lb" /></h3>
19
20 <table align="center" class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
21 <tr valign="top">
22 <th>Response</th>
23 <th>Failure Message</th>
24 </tr>
25
26 <xsl:for-each select="/testResults/*[@lb = current()/@lb][attribute::s='false']">
27 <tr>
28 <td><xsl:value-of select="@rc | @rs" /> - <xsl:value-of select="@rm" /></td>
29 <td><xsl:value-of select="assertionResult/failureMessage" /></td>
30 </tr>
31 </xsl:for-each>
32
33 </table>
34 </xsl:if>
35
36 </xsl:for-each>
37 </xsl:if>
38 </xsl:template>
39
40 <xsl:template match="/testResults">
41 <html lang="en">
42 <head>
43 <meta name="Author" content="shanhe.me"/>
44 <title>JMeter Test Results</title>
45 <style type="text/css"><![CDATA[
46
47 * { margin: 0; padding: 0 }
48
49 table.details tr th{
50 color: #ffffff;
51 font-weight: bold;
52 text-align:center;
53 background:#2674a6;
54 line-height:2em;
55 }
56
57 table.details tr:nth-child(odd){background:#FFFFFF;border:1px solid #CCC;line-height:2em;}
58 table.details tr:nth-child(even){background:#EDF3FE;border:1px solid #CCC;line-height:2em;}
59 table.details td{border:1px solid black;}
60 .Failure {
61 font-weight:bold; color:red;
62 }
63 html{ width: 100%; height: 100%; background: #b4b4b4; font-size: 12px }
64 body { width: 95%; height: 95%; margin: 0 auto; }
65 table { border: none; border-collapse: collapse; table-layout: fixed;word-wrap:break-word;word-break:break-all; }
66 #panel-wrap {position:relative;width: 100%;height: 100%;}
67 td { vertical-align: baseline; font-size: 12px }
68
69 #left-panel { position: absolute; left: 0; top: 0; bottom: 0; width: 30%; overflow: auto; background: #dee4ea }
70 #left-panel li.navigation { font-weight: bold; cursor: default; color: #9da8b2; line-height: 18px; background-position: 12px 5px; background-repeat: no-repeat; padding: 0 0 0 25px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAICAYAAAArzdW1AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQqGbO7BEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAKRJREFUGNN1zM0KgkAYheF3RvtXSsGyWhRNaILS7bdt11W0KgJvoPwZp0UlBPUtz3nOJw7Hk7necv5dOA2Qaazo2vZP0LEt9olCVtqQROufKNmuqBuBNAYW4QzXGX6B0bDPcjGnMQYJ8Cg12U59oSzaUJQa4IUAXMclDHwAAn/MxPMw765FZd2QRgopBWmsKCrdfhXnS/4ZYElBXdyxewN008Y8AephLAkqz613AAAAAElFTkSuQmCC) }
71 #left-panel li.success { color: #565b60 }
72 #left-panel li.failure { color: red }
73 #left-panel li { list-style: none; color: black; cursor: pointer }
74 #left-panel li.selected { background-repeat: repeat-x; color: white; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAUCAYAAABMDlehAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBQxLTs5O2gAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAEdJREFUCNc1y7ERgEAMA0GNUhIyGqM2uqKgtyWZhE9v53A/7/A6D7BkMDNgy2AroB2wHTCZv5UMOgFLG1bvd7XBckBlwCXjA5wMOF5iOX/MAAAAAElFTkSuQmCC) }
75 #left-panel div { line-height: 20px; background-position: 25px 3px; background-repeat: no-repeat; padding: 0 0 0 45px }
76 #left-panel div.success { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBULEEc6wzcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAiNJREFUKM99kktIVGEYhp/jzJl08lI6logp2Y2EFkbtaqlFROsWrlq4ioJWQRs37VoUVItWkYEVRGSBlhleCpywDEWxTEuxcURTZ6YzxzP/5WshCOHUt36f93kXnyMi5Lsnb4clI4s4fhkXzp5w8mWcfHBvfEpUxVdCUUU6lUPNHuD86cYtBQX5GhPrM7hRg7GaSDRg2vuUd90WuOPVsOyqy6FFo2yOQHlU1S9z9dZT+S/8I7GCLlkAN4eyAf56mnT6Fy1HLnGuuYa++MS/4e74qMRqfXLaJ9BpfnsrLC0m2BYuoqwUbj/+274JD43OEqmexwvW8NUKXnaZtVSS1pNtAAyOvyC6v48HnUNb4Z7PH8UtTlIQWA5tb2RhYY7kz3l2FleytJYg/qWb8t2KZ/0PN+1hgI6uEUr2jpHKpGlquExVaS0VbjUZL7WxaqIXK6ADQ0n9GNfv9XCttWnD/O57t0TKFklnF3g5fJ/seoaa2D4O1x0F4PlgO9oIftbgFgYMfLgjACGqj0vlsddoUnj+Kt/mxunq72RP+UGqYjWMTA7R+b6dUCSEGEF5hoJQip6BaFs4HJtCyRrKs6wHCovDip/kys0WWpovMpOYBCtoT2N9B5uzWG0Zid8gnFrVFEQDtBaUrxEgXBimaEeER2/uIiK4roPOaMRYjBKsFly3fOO3G06dETGCWIsYjckprMphtEKMAQtgsMYi1mJMQHJ6xvkDKQoyphCzkl0AAAAASUVORK5CYII=) }
77 #left-panel div.failure { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUJOEC5CU8AAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAeVJREFUKM+NkDtok2EUhp8vl9ZLo/EyKI6KFgqCKC4OClrBWUQEcRRx1cGpk3WyInWrgoMZKkW8thYaEYQ0i7WC2ngrNDTERHJvkv/L/3//dxwc7F8jeOAsh/c973OOEhG61aPnaen7maXYt4MLZ4+pbppQt+F06jNH3QWOb8pxUs+SmJzjv83hxY8SVy3wNdtVneiHqe54IhLoB4/TUkyMyOrKj5yXoVtPZK02kLyYK7OnlqFWzgcCGtUC/YUJ3n5a/jd28tU7ORTN0myUA6Jms8bpWIa798elqzn1fokjThrpVBC3ETzNbYAuca59j/Hp+b/N869Tsk8tgVMCXQk+RlfQuk1/tMLMwzsSMCcm5zjhvoR2AdpF0GuwO4aqttS05ZSbZHhsBoAIwI83Cdkd/460XDAOG02d24MxvlR8dsUUh3f2UHaEtgdbWCHz4oZwcVCp66PP5FLhKjEc8DXaCMsNy8DYn/SnZ+L0hhWOb/F8yLs9fDtwk8j+VpqwrlC34PrgGEu2bhlYhZ1b8dncq3AMeBaUr/k6NUyk4ChKzu+N2hc6Bqody+WDG8g2fLatD7F3axjPgmvAtYJvIbouhhIRrl0ZktnkBGIt1gqeMXQ8D2MMiCIUCqFEsFhEQMSykCuqX0MzLAUJTzRsAAAAAElFTkSuQmCC) }
78 #left-panel div.detail { display: none }
79 #right-panel { position: absolute; right: 0; top: 0; bottom: 0; left: 30%; overflow: auto; background: white }
80 #right-panel .group { font-size: 12px; font-weight: bold; line-height: 16px; padding: 0 0 0 18px; counter-reset: assertion; background-repeat: repeat-x; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAQCAYAAADXnxW3AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBUkDq8pxjkAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAADdJREFUCNdVxrERwDAMAzGK0v47eS6Z927SpMFBAAbkvSvnRk5+7K5cVfLMyN39bWakJAjA5xw9R94jN3tVhVEAAAAASUVORK5CYII=) }
81 #right-panel .zebra { background-repeat: repeat; padding: 0 0 0 18px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAmCAYAAAAFvPEHAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDEBYWFlNztEcAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAABdJREFUCNdjYKAtePv5338mBgYGBpoQAGy1BAJlb/y6AAAAAElFTkSuQmCC) }
82
83 #right-panel .data { line-height: 19px; }
84 #right-panel pre.data { white-space: pre }
85 #right-panel tbody.failure { color: red }
86 #right-panel td.key { min-width: 108px }
87 #right-panel td.delimiter { min-width: 18px }
88 #right-panel td.assertion:before { counter-increment: assertion; content: counter(assertion) ". " }
89 #right-panel td.assertion { color: black }
90 #right-panel .trail { border-top: 1px solid #b4b4b4 }
91
92 ]]></style>
93 <script type="text/javascript"><![CDATA[
94
95 var onclick_li = (function() {
96 var last_selected = null;
97 return function(li) {
98 if( last_selected == li )
99 return;
100 if( last_selected )
101 last_selected.className = "";
102 last_selected = li;
103 last_selected.className = "selected";
104 document.getElementById("right-panel").innerHTML = last_selected.firstChild.nextSibling.innerHTML;
105 return false;
106 };
107 })();
108
109 var patch_timestamp = function() {
110 var spans = document.getElementsByTagName("span");
111 var len = spans.length;
112 for( var i = 0; i < len; ++i ) {
113 var span = spans[i];
114 if( "patch_timestamp" == span.className )
115 span.innerHTML = new Date( parseInt( span.innerHTML ) );
116 }
117 };
118
119 var patch_navigation_class = (function() {
120
121 var set_class = function(el, flag) {
122 if(el) {
123 el.className += flag ? " success" : " failure";
124 }
125 };
126
127 var traverse = function(el, group_el, flag) {
128 while(1) {
129 if(el) {
130 if(el.className == 'navigation') {
131 set_class(group_el, flag);
132 group_el = el;
133 flag = true;
134 } else {
135 var o = el.firstChild;
136 o = o ? o.className : null;
137 flag = flag ? (o == 'success') : false;
138 }
139 el = el.nextSibling;
140 } else {
141 set_class(group_el, flag);
142 break;
143 }
144 }
145 };
146
147 return function() {
148 var o = document.getElementById("result-list");
149 o = o ? o.firstChild : null;
150 if(o)
151 traverse(o, null, true);
152 };
153 })();
154
155 window.onload = function() {
156 patch_timestamp();
157 patch_navigation_class();
158 var o = document.getElementById("result-list");
159 o = o ? o.firstChild : null;
160 o = o ? o.nextSibling : null;
161 if(o)
162 onclick_li(o);
163 };
164 function checkfailure() {
165 if (document.getElementById("bt").innerHTML == "查看失败") {
166 document.getElementById("bt").innerHTML = "查看全部";
167 var trs = document.getElementsByTagName("table")[1].getElementsByTagName('tr');
168 for( var i = 1; i < trs.length; i++ ) {
169 var tr = trs[i];
170 if( "Failure" != tr.className )
171 tr.style.display = 'none';
172 }
173 }else if(document.getElementById("bt").innerHTML == "查看全部") {
174 document.getElementById("bt").innerHTML = "查看失败";
175 var trs = document.getElementsByTagName("table")[1].getElementsByTagName('tr');
176 for( var i = 1; i < trs.length; i++ ) {
177 var tr = trs[i];
178 if( "Failure" != tr.className )
179 tr.style.display = '';
180 }
181 }
182 };
183
184 ]]></script>
185 </head>
186 <body>
187 <h2>Summary</h2>
188 <table align="center" class="details" cellpadding="5" cellspacing="2" width="100%" >
189 <tr valign="top">
190 <th>执行总数</th>
191 <th>成功数</th>
192 <th>失败数</th>
193 <th>成功率</th>
194 <th>Average Time</th>
195 <th>Min Time</th>
196 <th>Max Time</th>
197 </tr>
198 <tr valign="top">
199 <xsl:variable name="allCount" select="count(/testResults/*)" />
200 <xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />
201 <xsl:variable name="allSuccessCount" select="count(/testResults/*[attribute::s='true'])" />
202 <xsl:variable name="allSuccessPercent" select="$allSuccessCount div $allCount" />
203 <xsl:variable name="allTotalTime" select="sum(/testResults/*/@t)" />
204 <xsl:variable name="allAverageTime" select="$allTotalTime div $allCount" />
205 <xsl:variable name="allMinTime">
206 <xsl:call-template name="min">
207 <xsl:with-param name="nodes" select="/testResults/*/@t" />
208 </xsl:call-template>
209 </xsl:variable>
210 <xsl:variable name="allMaxTime">
211 <xsl:call-template name="max">
212 <xsl:with-param name="nodes" select="/testResults/*/@t" />
213 </xsl:call-template>
214 </xsl:variable>
215 <xsl:attribute name="class">
216 <xsl:choose>
217 <xsl:when test="$allFailureCount > 0">Failure</xsl:when>
218 </xsl:choose>
219 </xsl:attribute>
220 <td align="center">
221 <xsl:value-of select="$allCount" />
222 </td>
223 <td align="center">
224 <xsl:value-of select="$allSuccessCount" />
225 </td>
226 <td align="center">
227 <xsl:value-of select="$allFailureCount" />
228 </td>
229 <td align="center">
230 <xsl:call-template name="display-percent">
231 <xsl:with-param name="value" select="$allSuccessPercent" />
232 </xsl:call-template>
233 </td>
234 <td align="center">
235 <xsl:call-template name="display-time">
236 <xsl:with-param name="value" select="$allAverageTime" />
237 </xsl:call-template>
238 </td>
239 <td align="center">
240 <xsl:call-template name="display-time">
241 <xsl:with-param name="value" select="$allMinTime" />
242 </xsl:call-template>
243 </td>
244 <td align="center">
245 <xsl:call-template name="display-time">
246 <xsl:with-param name="value" select="$allMaxTime" />
247 </xsl:call-template>
248 </td>
249 </tr>
250 </table>
251 <button class="button" id="bt" onclick="checkfailure()" style="float:right">查看失败</button>
252 <h2>Pages</h2>
253 <table align="center" class="details" cellpadding="5" cellspacing="2" width="100%">
254 <tr valign="top">
255 <th width="30%">URL</th>
256 <th>执行总数</th>
257 <th>失败</th>
258 <th>成功率</th>
259 <th>Average Time</th>
260 <th>Min Time</th>
261 <th>Max Time</th>
262 </tr>
263 <xsl:for-each select="/testResults/*[not(@lb = preceding::*/@lb)]">
264 <xsl:variable name="label" select="@lb" />
265 <xsl:variable name="count" select="count(../*[@lb = current()/@lb])" />
266 <xsl:variable name="failureCount" select="count(../*[@lb = current()/@lb][attribute::s='false'])" />
267 <xsl:variable name="successCount" select="count(../*[@lb = current()/@lb][attribute::s='true'])" />
268 <xsl:variable name="successPercent" select="$successCount div $count" />
269 <xsl:variable name="totalTime" select="sum(../*[@lb = current()/@lb]/@t)" />
270 <xsl:variable name="averageTime" select="$totalTime div $count" />
271 <xsl:variable name="minTime">
272 <xsl:call-template name="min">
273 <xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
274 </xsl:call-template>
275 </xsl:variable>
276 <xsl:variable name="maxTime">
277 <xsl:call-template name="max">
278 <xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
279 </xsl:call-template>
280 </xsl:variable>
281 <tr valign="top">
282 <xsl:attribute name="class">
283 <xsl:choose>
284 <xsl:when test="$failureCount > 0">Failure</xsl:when>
285 </xsl:choose>
286 </xsl:attribute>
287 <td align="left">
288 <xsl:value-of select="$label" />
289 </td>
290 <td align="center">
291 <xsl:value-of select="$count" />
292 </td>
293 <td align="center">
294 <xsl:value-of select="$failureCount" />
295 </td>
296 <td align="center">
297 <xsl:call-template name="display-percent">
298 <xsl:with-param name="value" select="$successPercent" />
299 </xsl:call-template>
300 </td>
301 <td align="center">
302 <xsl:call-template name="display-time">
303 <xsl:with-param name="value" select="$averageTime" />
304 </xsl:call-template>
305 </td>
306 <td align="center">
307 <xsl:call-template name="display-time">
308 <xsl:with-param name="value" select="$minTime" />
309 </xsl:call-template>
310 </td>
311 <td align="center">
312 <xsl:call-template name="display-time">
313 <xsl:with-param name="value" select="$maxTime" />
314 </xsl:call-template>
315 </td>
316 </tr>
317 </xsl:for-each>
318 </table>
319
320 <h2>ErrorDetail</h2>
321 <div id="panel-wrap">
322 <div id="left-panel">
323 <ol id="result-list">
324 <!-- 只把失败的生成html -->
325 <xsl:for-each select="*[attribute::s='false']">
326 <!-- group with the previous sibling -->
327 <xsl:if test="position() = 1 or @tn != preceding-sibling::*[1]/@tn">
328 <li class="navigation">Thread: <xsl:value-of select="@tn"/></li>
329 </xsl:if>
330 <li onclick="return onclick_li(this);">
331 <div>
332 <xsl:attribute name="class">
333 <xsl:choose>
334 <xsl:when test="@s = 'true'">success</xsl:when>
335 <xsl:otherwise>failure</xsl:otherwise>
336 </xsl:choose>
337 </xsl:attribute>
338 <xsl:value-of select="@lb"/>
339 </div><div class="detail">
340 <div class="group">Sampler</div>
341 <div class="zebra">
342 <table>
343 <tr><td class="data key">Timestamp</td><td class="data delimiter">:</td><td class="data"><span class="patch_timestamp"><xsl:value-of select="@ts"/></span></td></tr>
344 <tr><td class="data key">Time</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@t"/> ms</td></tr>
345 <tr><td class="data key">Latency</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@lt"/> ms</td></tr>
346 <tr><td class="data key">Sample Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@sc"/></td></tr>
347 <tr><td class="data key">Error Count</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@ec"/></td></tr>
348 <tr><td class="data key">Response Code</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rc"/></td></tr>
349 <tr><td class="data key">Response Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="@rm"/></td></tr>
350 </table>
351 </div>
352 <div class="trail"></div>
353 <xsl:if test="count(assertionResult) > 0">
354 <div class="group">Assertion</div>
355 <div class="zebra">
356 <table>
357 <xsl:for-each select="assertionResult">
358 <tbody>
359 <xsl:attribute name="class">
360 <xsl:choose>
361 <xsl:when test="failure = 'true'">failure</xsl:when>
362 <xsl:when test="error = 'true'">failure</xsl:when>
363 </xsl:choose>
364 </xsl:attribute>
365 <tr><td class="data assertion" colspan="3"><xsl:value-of select="name"/></td></tr>
366 <tr><td class="data key">Failure</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failure"/></td></tr>
367 <tr><td class="data key">Error</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="error"/></td></tr>
368 <tr><td class="data key">Failure Message</td><td class="data delimiter">:</td><td class="data"><xsl:value-of select="failureMessage"/></td></tr>
369 </tbody>
370 </xsl:for-each>
371 </table>
372 </div>
373 <div class="trail"></div>
374 </xsl:if>
375 <div class="group">Request</div>
376 <div class="zebra">
377 <table>
378 <tr><td class="data key">Method/Url</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="method"/><xsl:text> </xsl:text><xsl:value-of select="java.net.URL"/></pre></td></tr>
379 <tr><td class="data key">Query String</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="queryString"/></pre></td></tr>
380 </table>
381 </div>
382 <div class="trail"></div>
383 <div class="group">Response</div>
384 <div class="zebra">
385 <table>
386 <tr><td class="data key">Response Data</td><td class="data delimiter">:</td><td class="data"><pre class="data"><xsl:value-of select="responseData"/></pre></td></tr>
387 </table>
388 </div>
389 <div class="trail"></div>
390 </div>
391 </li>
392 </xsl:for-each>
393 </ol>
394 </div>
395 <div id="right-panel"></div>
396 </div>
397 </body>
398 </html>
399 </xsl:template>
400
401
402 <xsl:template name="min">
403 <xsl:param name="nodes" select="/.." />
404 <xsl:choose>
405 <xsl:when test="not($nodes)">NaN</xsl:when>
406 <xsl:otherwise>
407 <xsl:for-each select="$nodes">
408 <xsl:sort data-type="number" />
409 <xsl:if test="position() = 1">
410 <xsl:value-of select="number(.)" />
411 </xsl:if>
412 </xsl:for-each>
413 </xsl:otherwise>
414 </xsl:choose>
415 </xsl:template>
416
417 <xsl:template name="max">
418 <xsl:param name="nodes" select="/.." />
419 <xsl:choose>
420 <xsl:when test="not($nodes)">NaN</xsl:when>
421 <xsl:otherwise>
422 <xsl:for-each select="$nodes">
423 <xsl:sort data-type="number" order="descending" />
424 <xsl:if test="position() = 1">
425 <xsl:value-of select="number(.)" />
426 </xsl:if>
427 </xsl:for-each>
428 </xsl:otherwise>
429 </xsl:choose>
430 </xsl:template>
431
432 <xsl:template name="display-percent">
433 <xsl:param name="value" />
434 <xsl:value-of select="format-number($value,'0.00%')" />
435 </xsl:template>
436
437 <xsl:template name="display-time">
438 <xsl:param name="value" />
439 <xsl:value-of select="format-number($value,'0 ms')" />
440 </xsl:template>
441
442 </xsl:stylesheet>
可以将该段代码写入一个.xsl文件中,在build.xml文件中指向该样式。对了,还有一个需要注意的地方,就是,务必将Build.xml文件的编码格式和该样式的编码格式统一,不然,构建之后会出现中文乱码的情况。
看看生成的报告样式。
该模板完全适用于接口自动化框架中。