接口测试平台:Http请求的简单执行(旧版已废弃)
本文是上文《接口测试平台:接口内容的设计》的具体执行部分。
涉及jar包如下:
- mybatis-spring-boot-starter 1.3.0
- lombok 1.16.16
- okhttp 3.10.0
- json-path 2.4.0
大概逻辑如下:
前端选择执行
- 可以在列表页进行多选,执行
- 单接口的调试执行
- 可以在列表页进行多选,执行
后端在数据库逐读取所有case的具体信息,逐条执行case。
- 将结果进行储存和反馈给前端。
- 前端对数据进行处理,如果是列表的批量执行,只会刷新case的最后执行状态,如果是单条case的调试执行,会渲染最新的result(接口返回结果)
具体实现思路如下:
1.前端选择执行
前端在点击执行时,会进行一次请求,像后端传一个id的list(id即为case在数据库中的存储id)
2.后端在数据库逐读取所有case的具体信息,逐条执行case。
service层
@Override
public ResponseVo excuteRequest(Integer[] ids) {ResponseVo responseVo = new ResponseVo();
// 获取当前需执行的所有case
List<TestCase> caseList = apiTestCaseMapper.selectTestCaseListByIds(ids);
// 遍历caseList,进行http请求
for (TestCase testCase:caseList){
// 保存响应结果
String result = "";
Response response = ApiTestUtils.doRequest(testCase, GLOBAL_COLLECTION_ID);
try {
result = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
// 保存变量, 调试执行时可以忽略此方法
ApiTestUtils.saveVariable(result, testCase, GLOBAL_COLLECTION_ID);
// 判断是否通过了所有校验条件
if (ApiTestUtils.verifyResult(result, testCase, GLOBAL_COLLECTION_ID)){
testCase.setStatus(Boolean.TRUE);
} else {
testCase.setStatus(Boolean.FALSE);
}
testCase.setResult(result);
// 更新数据库保存的信息
setJsonValue(testCase);
apiTestCaseMapper.updateCase(testCase);
// 将执行后的结果返回给前端
responseVo.setIsSuccess(Boolean.TRUE);
responseVo.setResult(testCase);
}
return responseVo;
}
mapper.xml
ApiTestUtils
这里有一个collectionId(集合id),用于后面的集合执行,此处全部默认为0即可。@Slf4j
public class ApiTestUtils {
/** * @param testCase 传入完整的case数据 * @param collectionId 集合id,如果传入0则表示为接口调试执行 * @return Response返回结果 */
public static Response doRequest(TestCase testCase, Integer collectionId){
String url = "http://" + getVariable(testCase.getApiUrl(), collectionId);
if (null != testCase.getApiPort()){
url += ":" + String.valueOf(testCase.getApiPort());
}
if (!testCase.getApiPath().isEmpty()){
url += getVariable(testCase.getApiPath(),collectionId);
}
// 请求方式: POST/GET
String apiMethod = testCase.getApiMethod();
// 请求类型:1.json 2.url form
int bodyType = testCase.getBodyType();
String body = "";
// 连接超时、写入超时、读取超时,均设置为30s,这里不做展开讨论,有兴趣的同学可以搜一下okhttp的用法
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(Config.connectTimeout, TimeUnit.SECONDS)
.writeTimeout(Config.writeTimeout,TimeUnit.SECONDS)
.readTimeout(Config.readTimeout, TimeUnit.SECONDS)
.build();
Request.Builder builder = new Request.Builder();
// 设置header
List<RequestHeaders> headersList = JSON.parseArray(testCase.getHeaderValue(), RequestHeaders.class);
for (RequestHeaders headers:headersList){
builder.header(headers.getHeaderKey(), getVariable(headers.getHeaderValue(), collectionId));
}
// 设置body、mediaType
String mediaTypeValue = "";
if (bodyType == 1){
mediaTypeValue = "application/json;charset=UTF-8";
body = testCase.getJsonValue();
} else if (bodyType == 2){
mediaTypeValue = "application/x-www-form-urlencoded;charset=utf-8";
//TODO urlfrom的body处理
}
// 如果前端没有传body,讲body设置为空string
if (body == null){
body = "";
} else {
body = getVariable(body, collectionId);
}
Request request = null;
if (apiMethod.equals("POST")){
RequestBody requestBody =RequestBody.create(MediaType.parse(mediaTypeValue), body);
request = builder.url(url).post(requestBody).build();
} else if(apiMethod.equals("GET")){
request = builder.url(url).get().build();
}
Response response = null;
try {
response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
/** * * @param string 变量名 * @param collectionId 集合id,:为0时只读全局变量,非0时读了全局变量再读集合内变量 * @return 变量值 */
private static String getVariable(String string,Integer collectionId){
HashMap<String,String> variableMap = ApiTestConfig.globalVariableMap;
// 如果为集合执行,会再次获取集合内变量
if (collectionId != 0){
variableMap.putAll(ApiTestConfig.collectionVariableMap);
}
// 查询string中是否有${KEY}格式的数据,如果有则将其替换为VALUE
if (!string.isEmpty()){
String reg = "\\$\\{.*?}";
Pattern p = Pattern.compile(reg);
Matcher m = p.matcher(string);
// 遍历替换所有的变量
while (m.find()){
String key = m.group().replace("${","").replace("}","");
if (variableMap.containsKey(key)){
string = string.replace(m.group(),variableMap.get(key));
}
}
return string;
} else {
return "";
}
}
/** 保存变量 */
public static void saveVariable(String result, TestCase testCase, Integer collectionId) {
// 调试执行时可以忽略此方法
}
/** * 遍历ExpectedList,只要有校验不通过的条件测抛FALSE */
public static Boolean verifyResult(String responseResult, TestCase testCase, Integer collectionId){
List<Expected> ExpectedList = JSON.parseArray(testCase.getExpectedListValue(), Expected.class);
if (ExpectedList.size() == 0){
if (collectionId != 0){
Assert.assertTrue(Boolean.TRUE);
}
return Boolean.TRUE;
}
Boolean bool = Boolean.FALSE;
for (Expected exp:ExpectedList){
// 提取方式:1.jsonPath 2.正则表达式
int extractMethod = exp.getExtractMethod();
String extractRule = exp.getExtractRule();
// 校验方式:1.equasl 2.contains
int compareType = exp.getCompareType();
String expectedValue = exp.getExpectedValue();
// 实际获取结果
String actualRes = "";
if (extractMethod == 1){
try {
// 这里取到的值如果是bool时,强转会抛错,所以需要用object接一下,再转String
Object o = JsonPath.read(responseResult, extractRule);
actualRes = String.valueOf(o);
} catch (Exception e){
if (collectionId != 0){
Reporter.log("接口返回结果为:" + responseResult);
Assert.fail("预期值取值失败");
}
return Boolean.FALSE;
}
} else if (extractMethod == 2){
try {
Pattern p = Pattern.compile(extractRule);
Matcher m = p.matcher(responseResult);
if (m.find()){
actualRes = m.group();
}
} catch (Exception e){
if (collectionId != 0){
Assert.fail("预期值取值异常");
}
return Boolean.FALSE;
}
}
// 对比预期结果
if (compareType == 1){
if (collectionId != 0){
Assert.assertEquals(actualRes, expectedValue);
}
if (actualRes.equals(expectedValue)){
bool = Boolean.TRUE;
}
} else if (compareType == 2){
if (actualRes.contains(expectedValue)){
bool = Boolean.TRUE;
} else {
if (collectionId != 0){
Assert.fail("预期值:" + expectedValue + "不存在与期望值" + actualRes + "中");
}
}
}
}
return bool;
}
}
3.将结果进行储存和反馈给前端。
service层这四行代码,进行了处理
// 自己封装的方法,给header,formValue,variableList,expectedList等字段赋值
setJsonValue(testCase);
// 更新数据库保存的信息
apiTestCaseMapper.updateCase(testCase);
// 将执行后的结果返回给前端
responseVo.setIsSuccess(Boolean.TRUE);
responseVo.setResult(testCase);
这里只返回了最后一条case的信息
4.前端对数据进行处理
在列表进行批量执行时,拿到后端返回的isSuccess=true时,则执行刷新列表,获取最新的每个接口的执行状态。
在进行单接口调试时,从result中的testCase信息获取接口响应结果及校验结果,重新渲染页面。
上述过程,将前端传数据然后读sql的过程转变为xml驱动testng进行执行,可以替换成一个接口测试框架。
下篇文章会讲解在多接口测试中,如何控制接口与接口间的数据依赖。有疑问的小伙伴欢迎在文章下方留言,我会根据问题不断优化文章内容!
还没有评论,来说两句吧...