对于接口测试的方法,我们有多种选择,譬如:使用jmeter工具中的http请求,postman请求,和偏向代码层面的自动化接口测试框架。在分别使用这三者方法进行接口测试后,发现各有所优缺。
1、 postman比较适合单个接口测试请求,对于复杂的业务使用其进行自动化接口测试用例设计时比较繁琐。
2、 jmeter工具对于业务测试比较友好,可以通过前置条件和后置处理进行测试数据的构造,然后进行自动化接口测试用例的设计并执行,对于接口的测试结果输出比较直观。但是对于单个接口测试需要设计多个测试用例和测试数据,在后期的维护过程中不是很方便。
3、 自动化接口测试框架使用更偏向于单元测试层面,需要了解和懂得Java语言,对于测试结果的处理不是很友好,通过断言进行判断。在测试用例设计的过程中更全面一些,但测试结果没有jmeter工具那么直观。同时维护起来比较麻烦。
综上,在测试性能过程中了解了jmeter工具中的Java请求,如是尝试通过jmeter+Java+HttpclientUtil的使用进行接口测试。源码请下载:基于jmeter+Java+HttpclientUtil实现的接口测试工具
难点:
- 对于不同的系统接口比较多,所以需要针对每个接口设计一个类,并且类中需要继承JavaSampleClient类,类中需要包含getDefaultParameters(设置入参),setupTest(初始化方法),runTest(测试主体),teardownTest(测试结束)。每个类中含有大量重复数据并且每个方法的具体实现不同。因此,编写出的java请求比较冗余和且维护成本比较大。
- 测试响应结果的校验和断言需要针对每个测试用例进行设计,如果设计在代码里面维护比较麻烦而且场景覆盖比较低。如果借用jmeter工具的断言进行校验需要编写大量的BeanShell脚本。而且需要将接口响应结果直接打印。
- 接口参数每次改动都需要修改java请求的源码然后重新生成jar包增加了测试的工作量。相对于纯使用jmeter工具而言更复杂了。
针对以上的难点,研究了开发提供的单元测试后,针对上述问题进行了设计和解决。最终通过如下方法进行重新设计。
- 重写一个类OverWriteJavaSampleClient继承JavaSampleClient类,并将JavaSampleClient类中的方法重定义overGettDefaultParameters、overSetupTest、overRun、overRunTest方法,然后再其中实现JavaSampleClient的具体方法;对于接口中的参数定义成一个实体类,包括code(编号)、title(接口名称)、url(请求路径)、requestType(请求类型)、paramType(参数类型)、headerMsg(消息头参数)、paramsMag(接口参数)、assertMsg(断言)、description(接口描述)。每个接口名称对应一个实体类,且接口的参数通过map键值对存储在list中。接口参数数据通过Excel读取获取后存储为对应的格式。在方法中通过实体类中的参数判断接口类型,参数类型进行HttpClientUtil工具类调用发送http请求。
- 由于每个接口响应数据格式比较统一,只在代码中通过判断校验部分数据,具体的响应data里面的内容公共Jmeter工具进行校验。对于响应数据进行重新定义输出。
- 每个接口需要对于一个类,但是类里面的实现只需要通过Excel表中的title来进行参数回去。因此,类中的实现表简单,基本是统一的,减少了代码的冗余,同时参数可通过Excel进行维护。
针对上述设计思路,并开始了设计实现。包括两个部分,Excel的定义和java脚本的实现。
Excel的定义
:
代码实现:
OverWriteJavaSampleClient类实现方法
public abstract class OverWriteJavaSampleClient extends AbstractJavaSamplerClient {
Map<String,RemoteServerInfoEntity> setRemoteServerInfo;
{
try {
setRemoteServerInfo = setRemoteServerInfo();
} catch (Exception e) {
e.printStackTrace();
}
}
//自定义java请求方法入参,设置可用参数及默认值
public Arguments overGettDefaultParameters(String key){
Arguments params = new Arguments();
params.addArgument("httpHost","");
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getHeaderMsg().entrySet()) {
params.addArgument(stringStringEntry.getKey(),"");
}
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getParameterMsg().entrySet()) {
params.addArgument(stringStringEntry.getKey(),"");
}
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getAssertMsg().entrySet()) {
params.addArgument(stringStringEntry.getKey(),"");
}
return params;
}
public void overSetupTest(JavaSamplerContext arg0,String key) {
String httpHost = arg0.getParameter("httpHost");
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getHeaderMsg().entrySet()) {
stringStringEntry.setValue(arg0.getParameter(stringStringEntry.getKey()));
}
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getParameterMsg().entrySet()) {
stringStringEntry.setValue(arg0.getParameter(stringStringEntry.getKey()));
}
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getAssertMsg().entrySet()) {
stringStringEntry.setValue(arg0.getParameter(stringStringEntry.getKey()));
}
}
public String overRun(String httpHost, String key) {
String respJson = "";
StringBuilder urlBuff = new StringBuilder();
urlBuff.append(httpHost).append(setRemoteServerInfo.get(key).getUrl());
if(!setRemoteServerInfo.get(key).getHeaderMsg().isEmpty()) {
if (setRemoteServerInfo.get(key).getRequestType() == 1) {
respJson = HttpClientUtils.doGetRequest(urlBuff.toString(), setRemoteServerInfo.get(key).getHeaderMsg(), setRemoteServerInfo.get(key).getParameterMsg());
} else {
if (setRemoteServerInfo.get(key).getParamType() == 1) {
respJson = HttpClientUtils.doPostRequest(urlBuff.toString(), setRemoteServerInfo.get(key).getHeaderMsg(), setRemoteServerInfo.get(key).getParameterMsg(), null);
} else {
StringEntity entity = map2StringEntity((HashMap<String, String>) setRemoteServerInfo.get(key).getParameterMsg());
respJson = HttpClientUtils.doPostRequest(urlBuff.toString(), setRemoteServerInfo.get(key).getHeaderMsg(), null, entity);
}
}
}
else{
if (setRemoteServerInfo.get(key).getRequestType() == 1) {
respJson = HttpClientUtils.doGetRequest(urlBuff.toString(), null, setRemoteServerInfo.get(key).getParameterMsg());
} else {
if (setRemoteServerInfo.get(key).getParamType() == 1) {
respJson = HttpClientUtils.doPostRequestwithCookie(urlBuff.toString(), null, setRemoteServerInfo.get(key).getParameterMsg(), null);
} else {
StringEntity entity = map2StringEntity((HashMap<String, String>) setRemoteServerInfo.get(key).getParameterMsg());
respJson = HttpClientUtils.doPostRequestwithCookie(urlBuff.toString(), null, null, entity);
}
}
}
return respJson;
}
public SampleResult overRunTest(JavaSamplerContext arg0, String key){
String httpHost = arg0.getParameter("httpHost");
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getHeaderMsg().entrySet()) {
stringStringEntry.setValue(arg0.getParameter(stringStringEntry.getKey()));
}
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getParameterMsg().entrySet()) {
stringStringEntry.setValue(arg0.getParameter(stringStringEntry.getKey()));
}
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getAssertMsg().entrySet()) {
stringStringEntry.setValue(arg0.getParameter(stringStringEntry.getKey()));
}
//定义请求和响应相关信息,输出至生成结果树
//生成结果树中请求消息和响应消息内容
String title = " "+"\n"+"\t"+
" 接口:"+setRemoteServerInfo.get(key).getDescription()+" "+"\n"+"\t"+
" "+"\n";
String httpUrl = "HttpUrl:" + httpHost + setRemoteServerInfo.get(key).getUrl()+"\n";
String requestBody = "";
String assertMsg = "断言字段期望值:";
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getParameterMsg().entrySet()) {
stringStringEntry.setValue(arg0.getParameter(stringStringEntry.getKey()));
requestBody += "\n"+"\t"+"\""+ stringStringEntry.getKey()+"\""+":" + stringStringEntry.getValue() + ", ";
}
for (Map.Entry<String, String> stringStringEntry : setRemoteServerInfo.get(key).getAssertMsg().entrySet()) {
stringStringEntry.setValue(arg0.getParameter(stringStringEntry.getKey()));
assertMsg += "\""+ stringStringEntry.getKey()+"\""+":" + stringStringEntry.getValue() + ", ";
}
String requestJson = "RequestJson:"+"{" +requestBody+"\n" + "}"+"\n"+assertMsg;
Map<String, String> assertResult =new HashMap<>();
SampleResult sr = new SampleResult();
String request_msg = "";
request_msg = request_msg + title+httpUrl + requestJson ;//+ assertMsg;
String response_msg = "";
response_msg = response_msg+title;
try {
sr.sampleStart();
String runMap = this.overRun(httpHost,key);
if(setRemoteServerInfo.get(key).getAssertMsg().containsKey("exp_code")){
assertResult = AssertDeal.AssertDealofCodeMessage(runMap,setRemoteServerInfo.get(key).getAssertMsg());
response_msg += assertResult.get("assertResult") ;
sr.setSuccessful(Boolean.parseBoolean(assertResult.get("Bool")));
}
else if(setRemoteServerInfo.get(key).getAssertMsg().containsKey("exp_total")){
assertResult = AssertDeal.AssertDealofTotalResult(runMap,setRemoteServerInfo.get(key).getAssertMsg());
response_msg += assertResult.get("assertResult") ;
sr.setSuccessful(Boolean.parseBoolean(assertResult.get("Bool")));
}
else {
if(!runMap.equals(null)) {
response_msg += runMap;
sr.setSuccessful(Boolean.parseBoolean(assertResult.get(true)));
}
else {
response_msg += runMap;
sr.setSuccessful(false);
}
}
}catch (Throwable e){
sr.setSuccessful(false);
e.printStackTrace();
}finally {
sr.sampleEnd();
}
System.out.println(request_msg);
sr.setRequestHeaders(request_msg);
sr.setResponseData(response_msg,"utf-8");
System.out.println(response_msg);
return sr;
}
// 结束方法,实际运行时每个线程仅执行一次,在测试方法运行结束后执行
public void overTeardownTest(JavaSamplerContext arg0) {
}
public Map<String,RemoteServerInfoEntity> setRemoteServerInfo() throws Exception {
List<Map<String, String>> line = (List<Map<String, String>>) getLine("E:\\JmeterTest\\file\\interfaceParam.xls");
int lineSize = line.size();
Map<String,RemoteServerInfoEntity> paramsSet = new HashMap<>();
try {
for(int i=0;i<lineSize;i++) {
Integer getCode = Integer.parseInt(line.get(i).get("code"));
String getUrl = line.get(i).get("url");
int getRequestType = Integer.parseInt(line.get(i).get("requestType"));
int getParamType = Integer.parseInt(line.get(i).get("paramType"));
String getTitle = line.get(i).get("title");
Map<String, String> getHeader = string2Map(line.get(i).get("headerMsg"));
Map<String, String> getParams = string2Map(line.get(i).get("paramsMsg"));
Map<String, String> getAssert = string2Map(line.get(i).get("assertMsg"));
String getDescription = line.get(i).get("description");
RemoteServerInfoEntity param = new RemoteServerInfoEntity();
param.setCode(getCode);
param.setUrl(getUrl);
param.setParamType(getParamType);
param.setRequestType(getRequestType);
param.setTitle(getTitle);
param.setHeaderMsg(getHeader);
param.setParameterMsg(getParams);
param.setAssertMsg(getAssert);
param.setDescription(getDescription);
paramsSet.put(getTitle, param);
}
} catch (Exception e) {
e.printStackTrace();
}
return paramsSet;
}
}
举个栗子
登录接口java请求脚本
public class Login extends OverWriteJavaSampleClient {
String key = "login";
//自定义java请求方法入参,设置可用参数及默认值
public Arguments getDefaultParameters(){
Arguments params =overGettDefaultParameters(key);
return params;
}
// 初始化方法,实际运行时每个线程仅执行一次,在测试方法运行前执行
// 每个线程测试前执行一次,做一些初始化工作;
public void setupTest(JavaSamplerContext arg0){
overSetupTest(arg0,key);
}
@Override
public SampleResult runTest(JavaSamplerContext arg0) {
SampleResult sr = overRunTest(arg0,key);
return sr;
}
// 结束方法,实际运行时每个线程仅执行一次,在测试方法运行结束后执行
public void teardownTest(JavaSamplerContext arg0) {
overTeardownTest(arg0);
}
代码层执行结果: