最近在整理性能测试的一些入门文章,给同事们分享,介绍API 接口自动化和性能测试入门。
下面将以百度天气预报查询API 服务为例,创建Java API 请求范例。
1. API 服务信息
参考文档:https://blog.csdn.net/younghaiqing/article/details/54799303
接口示例:http://api.map.baidu.com/telematics/v3/weather?location=北京&output=json&ak=amMXVSEUGt6yU95x4DQOC2Um
百度ak申请地址:http://lbsyun.baidu.com/apiconsole/key
接口参数说明:
返回结果:
2. 创建Java工程
2.1 分析API 服务接口
接口示例如下:
http://api.map.baidu.com/telematics/v3/weather?location=北京&output=json&ak=yourkey
输出的数据格式,默认为xml格式;当output设置为'json'时,输出的为json格式的数据。
通过观察接口示例,可以判断HTTP请求方式为GET——GET方法向URL添加数据。
2.2 写出API 服务请求
package com.ane56.tester; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class BaiduWeather_originalV1 { /* * 根据城市名称查询天气 * @param city * * auther Jason Ma */ //接口示例 //http://api.map.baidu.com/telematics/v3/weather?location=北京&output=json&ak=yourkey //百度ak申请地址:http://lbsyun.baidu.com/apiconsole/key //输出的数据格式,默认为xml格式;当output设置为'json'时,输出的为json格式的数据 private static String MYKEY = "amMXVSEUGt6yU95x4DQOC2Um"; private static String ADDRESS = "http://api.map.baidu.com/telematics/v3/weather"; public static void main(String[] args) { String city = "青浦"; String httpArg = "location=" + city + "&output=" + "xml" + "&ak=" + MYKEY; String httpUrl = ADDRESS + "?" + httpArg; System.out.println("请求的地址是:" + httpUrl); BufferedReader reader = null; String result = null; StringBuffer sbf = new StringBuffer(); try { URL url = new URL(httpUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.connect(); InputStream is = connection.getInputStream(); reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); String strRead = null; while ((strRead = reader.readLine()) != null) { sbf.append(strRead); sbf.append(" "); } reader.close(); result = sbf.toString(); } catch (Exception e) { e.printStackTrace(); } System.out.println(result); } }
3. 转化为Jmeter JavaSampler
下面将利用Eclipse创建Maven工程的方式,完成JavaSampler的编码工作。
3.1 创建Maven工程
在Eclipse左侧的Package Explore点击右键,依次选择New -> Other -> Maven -> Maven Project:
这里要注意,之前创建的API 请求类,不是一定要基于Maven工程,普通的Java Project就可以;
创建Maven Project是为了开发和调试Jmeter JavaSampler。
3.2 添加依赖
打开Maven工程的POM.xml文件,添加依赖包。我们需要的依赖包如下:
- Junit:工程创建时已存在;
- Jmeter:核心,作为JavaSampler请求;有三个组件需要添加:ApacheJMeter_core、ApacheJMeter_java、ApacheJMeter_components;
- log4j-over-slf4j:为了解决调试阶段log4j红字报错,非必选,也可以用其他依赖包替代。
完成后的POM.xml文件如下:
<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> <groupId>com.ane56.tester</groupId> <artifactId>Jmeter_JavaSampler</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>Jmeter_JavaSampler</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.slf4j/log4j-over-slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_core</artifactId> <version>3.3</version> </dependency> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_java</artifactId> <version>3.3</version> </dependency> <dependency> <groupId>org.apache.jmeter</groupId> <artifactId>ApacheJMeter_components</artifactId> <version>3.3</version> </dependency> </dependencies> </project>
3.3 理解Java Sampler的套路
首先:要实现AbstractJavaSamplerClient类,才能被Jmeter加载;创建名为“BaiduWeather_JavaSamplerV1”的Java类,
添加对AbstractJavaSamplerClient类的实现;完成后如下:
public class BaiduWeather_JavaSamplerV1 extends AbstractJavaSamplerClient { …… }
其次:(对比LoadRunner脚本的结构:)
- 设置参数:getDefaultParameters,用于设置传入的参数;
- 初始化(init):setupTest,用于初始化性能测试时的每个线程;
- 执行迭代(action):runTest,为性能测试时的线程运行体;
- 完成退出(end):teardownTest,为测试结束方法,用于结束性能测试中的每个线程。
补充:调试脚本的main()方法。
3.4 详解getDefaultParameters()
在getDefaultParameters()代码段中,添加请求的参数;
作用:当Jmeter读取此JavaSampler后,能够读出请求的参数到列表:
注意:
在getDefaultParameters()代码段中,只需要添加参数项的名字,而不必要赋值。
通常我们会在Jmeter脚本中,对请求参数进行参数化。
读取参数的代码(在runTest方法内):
3.5 详解runTest()格式
- 创建sampleResult:private SampleResult sampleResult;
- 给请求贴标签:sampleresult.setSampleLabel("Java请求");
- 把请求的信息,写到Jmeter的【察看结果树 – 请求】页面:
sampleresult.setSamplerData(httpUrl);
sampleresult.setDataType(SampleResult.TEXT);
- 发送请求时,开始计时:sampleresult.sampleStart();
- 收到应答后,结束统计响应时间:sampleresult.sampleEnd();
- 标记获得成功的应答,对应http 200:sampleresult.setResponseCodeOK();
- 把收到的应答内容,写到Jmeter的【察看结果树 – 响应数据】页面:
sampleresult.setResponseData("结果是:"+ result, "utf-8");
sampleresult.setDataType(SampleResult.TEXT);
3.6 调试脚本的main()
1)首先要进行参数赋值,否则将直接使用getDefaultParameters()代码段中设置的参数默认值。建议赋值,与Jmeter的实际工作方式保持一致。
2)调用BaiduWeather_JavaSamplerV1,创建一次测试执行。
4. 导出Maven工程
4.1 导出依赖包
选中工程,点击右键,在菜单中选择Run As -> Run Configuration:
1) 首先在左侧的列表中,选择Maven Build,然后点击左上方的新建图标;
2) 输入操作的名字,在本例中是:Jmeter_JavaSampler_exportDependency;
3) 定位当前操作的工程路径;可以点击Workspace…的按钮来定位;
4) 输入操作的具体命令:在本例中是为了导出依赖:dependency:copy-dependencies;
5) 检查Maven Runtime的信息是否正确;
6) 点击Apply按钮完成保存;
7) 点击Run按钮、进行依赖包的导出。
等待完成后,到工程所在的路径 argetdependency目录,查看的依赖jar包:
4.2 导出测试类
导出测试类有两种方式:
1)Maven install命令:该命令会在工程所在的路径 arget文件夹下,生成与工程同名的jar文件,如Jmeter_JavaSampler-0.0.1-SNAPSHOT.jar。
不推荐这样的方式,因为这将导出工程中所有的类,不够精确。
2)直接导出单个接口测试类,注意是符合Jmeter JavaSampler的那个类。
具体方法是:在Eclipse左侧的Package Explore选中该类,点击右键,选择Export:
输入要导出JAR file的路径和名称,点击Finish按钮,完成导出操作。
5. 在Jmeter创建测试计划
5.1 准备工作
首先,要将导出的依赖包,拷贝到jmeterlib目录下。推荐将整个dependency文件夹拷过来,然后再引入到测试计划。
这样的好处在于大量的依赖包中存在与原有jmeterlib目录下的同名或不同版本的干扰。如下图:
然后,将单独导出的接口测试类的jar文件复制到jmeterlibext目录下,如下图:
这里的jar文件有个问题,先卖个关子,在后面分析解决思路。
完成后,进入下一环节。
5.2 创建测试计划
启动Jmeter,在测试计划的主窗口的靠下位置,添加依赖到classpath中;
通过点击浏览按钮,定位到依赖包的文件夹,全选所有的jar文件,点击打开按钮:
第一步完成后,添加线程组,并添加Java请求;
在类名称的下拉菜单中,找到我们开发的测试类BaiduWeather_JavaSamplerV1:
5.3 调试脚本
填写请求参数,填充原本为空的参数、缺省值可以替换也可保留:
添加一个监听器 – 察看结果树,以便查看脚本调试的结果:
点击刚刚添加的察看结果树,将主画面切换到结果树:
在调试前,需要保存脚本成.jmx文件。点击保存按钮,将脚本存放到合适的位置。
点击上方工具条内的启动按钮,查看测试结果:
Text区域,每执行一次请求,就自动增加一条记录到尾部。
右侧的区域,有三个Tab页,方便查看测试请求和响应。我们分别查看一下,注意判断响应数据是否为正确的。
请求页面:
没问题,确实与预期的一样。
再看响应数据页面:
显示error -3, No result available,出错了。
5.4 解决问题
解决步骤:
先将请求内容,放到浏览器中,查看是否能获得正确的天气预报信息:
成功了,说明API 服务没问题,请求内容也没问题。是哪里不对呢?
首先想到的,是否因为百度天气预报API对不是浏览器访问的Request做了屏蔽?类似与反爬虫策略。那就需要在请求时,补全HTTP Header信息。
那么HTTP Header信息哪里能查看?
这里可以利用Chrome浏览器的开发工具,按F12后,刷新一下,浏览器会自动录制整个请求和应答。
这样就能捕获到HTTP Header信息,将它们逐条复制下来。在Jmeter的线程组下面添加一个配置元件 - HTTP头信息管理,再逐条添加进去。
再次执行脚本,错误依旧如此。
思路错了吗?其实没有,方法是正确的,再想想。
正确的方法如下:
仔细查看一下Request RUL,如下图:
和浏览器的地址栏中的内容比较一下,“location=上海”在Request中,被替换成了“location=%E4%B8%8A%E6%B5%B7”。
这是汉字没有转码所导致的:我们从Jmeter发出的汉字不是URL编码的格式。
再想想,我们在Eclipse调试接口脚本的时候,应答信息和浏览器中显示的内容是一样的;这意味着从Eclipse发出的Request,已经被自动转码了,有点坑啊!
因此,我们需要对城市名称的汉字进行转码,而且要在接口请求的代码里完成。
为完成转码功能,需要再增加一个依赖:
import java.io.UnsupportedEncodingException;
runTest方法修改后,为如下图所示:
这里增加了一个转码的步骤,将接收到的城市名称从汉字转码为URL格式的。执行一下看看效果:
成功了!
重新将接口测试类导出成jar文件,删除先前存在于jmeterlibext下的同名文件;再拷贝一遍。
Jmeter也要重启才会刷新Java请求中的类列表。
重启后,打开之前保存的脚本,再次运行调试;执行成功。
以上就是从API 接口请求,转化为Jmeter的JavaSampler的编写方法。完整代码如下:
package com.ane56.tester; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient; import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext; import org.apache.jmeter.samplers.SampleResult; public class BaiduWeather_JavaSamplerV2 extends AbstractJavaSamplerClient { private SampleResult sampleResult; private String parameter; /** Holds the result data (shown as Response Data in the Tree display). */ private String resultData; private String httpArg_city; private String httpArg_outputType; SampleResult sampleresult = new SampleResult(); /** * JMeter界面中可手工输入参数,代码里面通过此方法获取 */ public Arguments getDefaultParameters() { // 设置参数,并赋予默认值 Arguments params = new Arguments(); params.addArgument("httpUrl", ""); params.addArgument("httpArg_city", "上海"); params.addArgument("httpArg_outputType", "xml"); params.addArgument("mykey", ""); //System.out.println("Default params list: " + params); return params; } /** * 执行runTest()方法前会调用此方法,可放一些初始化代码 */ @Override public void setupTest(JavaSamplerContext arg0) { //parameter = arg0.getParameter("parameter"); sampleresult.setDataEncoding("utf-8"); long start = System.currentTimeMillis(); } @Override public SampleResult runTest(JavaSamplerContext arg0) { sampleresult.setSampleLabel("Java请求"); String httpUrl = arg0.getParameter("httpUrl"); String location = arg0.getParameter("httpArg_city"); String outputType = arg0.getParameter("httpArg_outputType"); String mykey = arg0.getParameter("mykey"); //对汉字转码,否则在Jmeter测试时,获得错误响应 try { location = java.net.URLEncoder.encode(location, "utf-8"); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } String httpArg = "location=" + location + "&output=" + outputType + "&ak=" + mykey; httpUrl = httpUrl + "?" + httpArg; System.out.println("HttpURL is: " + httpUrl); BufferedReader reader = null; String result = null; StringBuffer sbf = new StringBuffer(); sampleresult.setSamplerData(httpUrl); sampleresult.setDataType(SampleResult.TEXT); try { sampleresult.sampleStart(); sampleresult.getStartTime(); URL url = new URL(httpUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.connect(); InputStream is = connection.getInputStream(); reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); String strRead = null; while ((strRead = reader.readLine()) != null) { sbf.append(strRead); sbf.append(" "); } reader.close(); result = sbf.toString(); System.out.println("结果是:"+ result); sampleresult.setResponseCodeOK(); sampleresult.setResponseMessage("Java Sampler Response is done. "); sampleresult.setResponseData("结果是:"+ result, "utf-8"); sampleresult.setDataType(SampleResult.TEXT); sampleresult.setSuccessful(true); } catch (Exception e) { sampleresult.setSuccessful(false); e.printStackTrace(); } finally { sampleresult.sampleEnd(); // jmeter 结束统计响应时间标记 sampleresult.getEndTime(); } return sampleresult; } /** * 执行runTest()方法后会调用此方法. */ @Override public void teardownTest(JavaSamplerContext arg0) { long end = System.currentTimeMillis(); } /** * 主函数;打jar包之前,记得注释掉main */ public static void main(String[] args) { Arguments params = new Arguments(); params.addArgument("httpUrl", "http://api.map.baidu.com/telematics/v3/weather"); params.addArgument("httpArg_city", "上海"); params.addArgument("httpArg_outputType", "json"); params.addArgument("mykey", "amMXVSEUGt6yU95x4DQOC2Um"); JavaSamplerContext arg0 = new JavaSamplerContext(params); BaiduWeather_JavaSamplerV2 test = new BaiduWeather_JavaSamplerV2(); test.setupTest(arg0); test.runTest(arg0); test.teardownTest(arg0); System.exit(0); } }