一阁谆、項目場景
1、excel中存儲各個http請求的 類型愉老、url,參數(shù)场绿,頭部信息,檢查點嫉入,關(guān)聯(lián)參數(shù)
2焰盗、從excel中讀取并發(fā)送各類http請求,并校驗檢查點
3咒林、將各請求中關(guān)聯(lián)的參數(shù)熬拒,引入對應(yīng)需要關(guān)聯(lián)的請求鏈接中
二、實現(xiàn)以上需求垫竞,需要使用到框架
<!--用于讀取excel-->
<dependency>
<groupId>com.github.crab2died</groupId>
<artifactId>Excel4J</artifactId>
<version>2.1.2</version>
</dependency>
<!--用于讀取文件-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!--用于httpclient發(fā)送get,post請求-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.4</version>
</dependency>
<!--用于表達式判斷-->
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>3.2.0</version>
</dependency>
<!--用于獲取json數(shù)據(jù)-->
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
三澎粟、需要用到的Utils工具類
1、將字符串轉(zhuǎn)化成Map存儲欢瞪。
如:excel中的頭部信息活烙、檢查點有多個時,需要拆分
import java.util.HashMap;
import java.util.Map;
public class MapUtil {
public static Map StringToMap(String str, String regx){
Map<String,Object> map = null;
if(str!=null) {
String[] strs = str.split(regx);
map = new HashMap<String,Object>();
for (String s : strs) {
String[] ss = s.split("=");
map.put(ss[0], ss[1]);
}
}
return map;
}
public static Map StringToMap(String str){
return StringToMap(str,";");
}
}
2遣鼓、將參數(shù)存儲到Map中啸盏。
如:excel中的關(guān)聯(lián)參數(shù),若有多個時需要拆分譬正,且需要得到具體關(guān)聯(lián)的值宫补。
import com.jayway.jsonpath.JsonPath;
import java.util.HashMap;
import java.util.Map;
public class SaveParamsUtil {
private static Map<String,Object> paramsMap = new HashMap<String,Object>();
public static void saveParamsToMap(String json,String params){
Map<String,Object> map = MapUtil.StringToMap(params);
if(map!=null) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
String val = entry.getValue().toString();
//這里需要用到JsonPath表達式獲取具體json中的值
Object obj = JsonPath.read(json, val);
paramsMap.put(key, obj.toString());
}
}
}
public static Object get(String key){
return paramsMap.get(key);
}
}
3、正則表達式匹配工具類
如:excel中第三個請求url中用到關(guān)聯(lián)的參數(shù)
import com.sc.bean.TestCase;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternUtil {
private static Pattern patternRule = Pattern.compile("\\$\\{(.*?)\\}");//${id}
public static void matcherParams(TestCase testCase){
Matcher matcher = patternRule.matcher(testCase.getUrl());
while (matcher.find()){
System.out.println(matcher.group());
String key = matcher.group(1);
String value = SaveParamsUtil.get(key).toString();
String url = testCase.getUrl().replace(matcher.group(),value);
testCase.setUrl(url);
System.out.println("newurl--->"+testCase.getUrl());
}
}
}
4曾我、實際json返回的結(jié)果與excel中檢查點是否一致工具類
如:excel中 $.code的實際值與期望值 1的比較
import com.googlecode.aviator.AviatorEvaluator;
import com.jayway.jsonpath.JsonPath;
import java.util.HashMap;
import java.util.Map;
public class CheckPointUtil {
public static Boolean checkPoint(String json,String checkParam){
if(checkParam!=null && !"".equals(checkParam) && !"null".equals(checkParam)){
Map<String,Object> map = new HashMap<String,Object>();
String[] cParams = checkParam.split(";");
for(String params:cParams){
String[] pars = params.split("=|>|<|>=|<=|==");
checkParam = checkParam.replace(pars[0],"data");
Object obj = JsonPath.read(json,pars[0]);
if(obj instanceof String){
checkParam = checkParam.replace(pars[1],StrToAviatorString(pars[1]));
checkParam = checkParam.replace("=","==");
}
map.put("data",obj);
Boolean bool = (Boolean) AviatorEvaluator.execute(checkParam,map);
return bool;
}
}
return true;
}
public static String StrToAviatorString(String str){
return "'"+str+"'";
}
}
5、HttpClient發(fā)送get健民、post請求工具類
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
public class HttpUtils {
private static CloseableHttpClient httpclient;
static {
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
manager.setMaxTotal(200); //連接池最大并發(fā)連接數(shù)
manager.setDefaultMaxPerRoute(200);//單路由最大并發(fā)數(shù),路由是對maxTotal的細(xì)分
httpclient = HttpClients.custom().setConnectionManager(manager).build();
}
/* ConnectionRequestTimeout httpclient使用連接池來管理連接抒巢,這個時間就是從連接池獲取連接的超時時間,可以想象下數(shù)據(jù)庫連接池
ConnectTimeout 建立連接最大時間
SocketTimeout 數(shù)據(jù)傳輸過程中數(shù)據(jù)包之間間隔的最大時間
HttpHost 代理
*/
private static RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
.setSocketTimeout(10000)
.setConnectTimeout(5000)
.setConnectionRequestTimeout(100).build();
//.setProxy(new HttpHost("127.0.0.1", 8888, "http")).build();
public static String doGet(String url) throws HttpClientException {
return doGet(url, null);
}
public static String doGet(String url, Map<String, Object> header) throws HttpClientException {
String ret = "";
HttpGet get = new HttpGet(url);
get.setConfig(config);
get.addHeader(HTTP.CONTENT_ENCODING, "UTF-8");
CloseableHttpResponse closeableHttpResponse = null;
try {
if (header != null) {
for (Map.Entry<String, Object> entry : header.entrySet()) {
get.setHeader(entry.getKey(), entry.getValue().toString());
}
}
closeableHttpResponse = httpclient.execute(get);
if (closeableHttpResponse.getStatusLine().getStatusCode() == 200) {
ret = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
} else {
throw new HttpClientException(
"System level error, Code=[" + closeableHttpResponse.getStatusLine().getStatusCode() + "].");
}
} catch (ClientProtocolException e) {
throw new HttpClientException("HttpClient error," + e.getMessage());
} catch (IOException e) {
throw new HttpClientException("IO error," + e.getMessage());
} finally {
if (closeableHttpResponse != null) {
try {
closeableHttpResponse.close();
} catch (IOException e) {
}
}
}
return ret;
}
public static String doPostString(String url, String params, String regx) throws HttpClientException {
Map<String, Object> paramsMp =null;
if(params!=null) {
paramsMp = new HashMap<String, Object>();
String[] strp = params.split(regx);
for (int i = 0; i < strp.length; i++) {
String singleparms = strp[i];
String[] key_values = singleparms.split("=");
paramsMp.put(key_values[0], key_values[1]);
}
}
return doPost(url, paramsMp);
}
public static String doPost(String url, Map<String, Object> params) throws HttpClientException {
return doPost(url, params, null);
}
public static String doPost(String url, Map<String, Object> params, Map<String, Object> header)
throws HttpClientException {
String ret = "";
HttpPost post = new HttpPost(url);
post.setConfig(config);
post.addHeader(HTTP.CONTENT_ENCODING, "UTF-8");
CloseableHttpResponse closeableHttpResponse = null;
HttpEntity postEntity = null;
try {
if (params != null) {
List<NameValuePair> list = new ArrayList<NameValuePair>();
for (Map.Entry<String, Object> entry : params.entrySet()) {
list.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
}
postEntity = new UrlEncodedFormEntity(list,"UTF-8");
post.setEntity(postEntity);
}
if (header != null) {
for (Map.Entry<String, Object> entry : header.entrySet()) {
post.setHeader(entry.getKey(), entry.getValue().toString());
}
}
closeableHttpResponse = httpclient.execute(post);
if (closeableHttpResponse.getStatusLine().getStatusCode() == 200) {
ret = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
} else {
throw new HttpClientException(
"System level error, Code=[" + closeableHttpResponse.getStatusLine().getStatusCode() + "].");
}
} catch (ClientProtocolException e) {
throw new HttpClientException("HttpClient error," + e.getMessage());
} catch (IOException e) {
throw new HttpClientException("IO error," + e.getMessage());
} finally {
if (postEntity != null) {
try {
EntityUtils.consume(postEntity);
} catch (IOException e) {
}
}
if (closeableHttpResponse != null) {
try {
closeableHttpResponse.close();
} catch (IOException e) {
}
}
}
return ret;
}
public static String doPostJson(String url, String jsonParam) throws HttpClientException {
return doPostJson(url, jsonParam, null);
}
public static String doPostJson(String url, String jsonParam, Map<String, Object> header)
throws HttpClientException {
String ret = "";
HttpPost post = new HttpPost(url);
post.setConfig(config);
post.addHeader(HTTP.CONTENT_ENCODING, "UTF-8");
CloseableHttpResponse closeableHttpResponse = null;
StringEntity postEntity = null;
try {
if (jsonParam != null) {
postEntity = new StringEntity(jsonParam, "utf-8");
postEntity.setContentEncoding("UTF-8");
postEntity.setContentType("application/json");
post.setEntity(postEntity);
}
if (header != null) {
for (Map.Entry<String, Object> entry : header.entrySet()) {
post.setHeader(entry.getKey(), entry.getValue().toString());
}
}
closeableHttpResponse = httpclient.execute(post);
if (closeableHttpResponse.getStatusLine().getStatusCode() == 200) {
ret = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
} else {
throw new HttpClientException(
"System level error, Code=[" + closeableHttpResponse.getStatusLine().getStatusCode() + "].");
}
} catch (ClientProtocolException e) {
throw new HttpClientException("HttpClient error," + e.getMessage());
} catch (IOException e) {
throw new HttpClientException("IO error," + e.getMessage());
} finally {
if (postEntity != null) {
try {
EntityUtils.consume(postEntity);
} catch (IOException e) {
}
}
if (closeableHttpResponse != null) {
try {
closeableHttpResponse.close();
} catch (IOException e) {
}
}
}
return ret;
}
}
定義自己的異常類:
public class HttpClientException extends Exception{
private static final long serialVersionUID = 2206587260745140347L;
public HttpClientException(String msg) {
super(msg);
}
}
四秉犹、excel中的各參數(shù)轉(zhuǎn)化成javabean
import com.github.crab2died.annotation.ExcelField;
public class TestCase {
@ExcelField(title = "類型")
private String type;
@ExcelField(title = "地址")
private String url;
@ExcelField(title = "參數(shù)")
private String params;
@ExcelField(title = "頭部",order=2)
private String header;
@ExcelField(title = "測試結(jié)果", order=1)
private String result;
@ExcelField(title = "檢查點")
private String checkP;
@ExcelField(title = "關(guān)聯(lián)")
private String correlation;
public String getCheckP() {
return checkP;
}
public void setCheckP(String checkP) {
this.checkP = checkP;
}
public String getCorrelation() {
return correlation;
}
public void setCorrelation(String correlation) {
this.correlation = correlation;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
@Override
public String toString() {
return "TestCase{" +
"type='" + type + '\'' +
", url='" + url + '\'' +
", params='" + params + '\'' +
", header='" + header + '\'' +
", result='" + result + '\'' +
", checkP='" + checkP + '\'' +
", correlation='" + correlation + '\'' +
'}';
}
}
五蛉谜、測試類
1、通過github.Excel4J包中的ExcelUtils 讀取excel崇堵,返回List
遍歷List集合型诚,集合中的每一個對象都是 TestCase bean
2、遍歷List的過程中鸳劳,判斷讀取excel中type請求是get還是post
如果是get,就調(diào)用已封裝好的HttpUtils中的doGet方法狰贯,需要傳get請求需要的url,頭部信息
如果是post,就調(diào)用已封裝好的HttpUtils中的doPost方法,需要傳get請求需要的url,參數(shù)涵紊,頭部信息
因為在Excel中參數(shù)傍妒、頭部信息都是以字符串的形式存儲的,所以需要將參數(shù)摸柄,頭部信息字符串轉(zhuǎn)化成Map對象才行颤练。
所以,這里封裝了字符串轉(zhuǎn)Map的工具類MapUtil驱负,方法名為:StringToMap,這里需要傳一個分割符的參數(shù)嗦玖,為了方便后續(xù)使用,重載StringToMap方法跃脊,傳遞了默認(rèn)的分割符“;”宇挫。
3、將get/post的請求返回結(jié)果匾乓,存儲在某個變量中responseJson捞稿。
4、檢查responseJson返回的code的值拼缝,是否與excel中檢查點中code的值是否一致娱局。
excel中“{(.*?)}作為一個匹配的規(guī)則敛纲,這里同jmeter中正則表達式匹配
在excel的url中通過上述匹配規(guī)則循環(huán)查看是否有需要匹配的url
如果有匹配喂击,則返回一個group,這里group得到的是${id}
通過group(n)得到具體要關(guān)聯(lián)的參數(shù)為id
將excel中關(guān)聯(lián)的參數(shù)id的值已轉(zhuǎn)存到map中
這里直接可調(diào)用該map淤翔,key為group(n)翰绊,即可得到對應(yīng)的id的value
將url中的參數(shù)替換成該id對應(yīng)的value,得到一個新的url
重新為TestCase設(shè)置url,后續(xù)http發(fā)送請求時旁壮,即可得到一個完整url
import com.github.crab2died.ExcelUtils;
import com.sc.bean.TestCase;
import com.sc.http.HttpUtils;
import com.sc.util.CheckPointUtil;
import com.sc.util.MapUtil;
import com.sc.util.PatternUtil;
import com.sc.util.SaveParamsUtil;
import java.io.File;
import java.util.List;
public class ApiTest {
public static void main(String[] args) throws Exception {
String path = System.getProperty("user.dir")+ File.separator+"data"+File.separator+"apitest.xlsx";
//通過github.Excel4J包中的ExcelUtils 讀取excel监嗜,返回List
List<TestCase> list = ExcelUtils.getInstance().readExcel2Objects(path,TestCase.class);
if(list!=null){
for(TestCase testCase : list){
PatternUtil.matcherParams(testCase);
String responseJson =null;
if("get".equals(testCase.getType())){
responseJson = HttpUtils.doGet(testCase.getUrl(), MapUtil.StringToMap(testCase.getHeader()));
}else if("post".equals(testCase.getType())){
responseJson = HttpUtils.doPost(testCase.getUrl(), MapUtil.StringToMap(testCase.getParams(),"&"),MapUtil.StringToMap(testCase.getHeader()));
}
Boolean checkFlag = CheckPointUtil.checkPoint(responseJson,testCase.getCheckP());
System.out.println("checkFlag="+checkFlag);
if(checkFlag){
SaveParamsUtil.saveParamsToMap(responseJson,testCase.getCorrelation());
}
System.out.println(responseJson);
}
}
}
}
六、輸出結(jié)果
checkFlag=true
{"msg":"登錄成功","uid":"E2BBEDC09FAA4406B8D85C96DF6281CF","code":"1"}
checkFlag=true
{"msg":"登錄成功","uid":"E2BBEDC09FAA4406B8D85C96DF6281CF","code":"1"}
${id}
newurl--->http://www.baidu.com?pid=E2BBEDC09FAA4406B8D85C96DF6281CF
checkFlag=true
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta ......