XXXweb系统的api测试(http request方式实现)
1.明确待测试api的来源
笔者工作中是项目api来源为jira-confluence,研发哥哥会把api接口的详细定义写到confluence上
在使用soapui测试工具编写测试api时,所有跟api有关的信息全部为confluence为准绳。
2 详细解析
2.1登录api
笔者这里就列举了三个常用登录测试case来测试登录api
2.1.1输入正确的登录信息
2.1.2断言
返回状态码断言:
返回信息头断言:(脚本断言)
脚本源码如下:(脚本语言选择groovy)
脚本代码如下:
def response = messageExchange.modelItem.testCase.testSteps["登录成功"].testRequest.response;
//获取服务器响应
def headers = response.responseHeaders;
//定义服务器响应头
def server = headers.get("Server").toString().replace("[","").replace("]","");
//获取服务器响应头中的server属性值,并且转成string数据类型,去掉“[”和“]”,否则校验通不过
def date = headers.get("Date").toString().replace("[","").replace("]","");
def contentlength = headers.get("Content-Length").toString().replace("[","").replace("]","");
def connection = headers.get("Connection").toString().replace("[","").replace("]","");
def SetCookie = headers.get("Set-Cookie").toString().replace("[","").replace("]","");
def expect_server = "nginx"
def expect_connection = "keep-alive"
def expect_contentlength = "0"
assert server == expect_server, "服务器返回server地址和预期地址不符合!"
//做断言,server值和预期值相等,否则报错“服务器返回server地址和预期地址不符合!”
assert connection == expect_connection, "服务器返回connection地址和预期地址不符合!"
assert contentlength == expect_contentlength, "服务器返回contentlength和预期不符合!"
2.1.3添加脚本step,设置工程全局变量ticket
脚本源码如下:
// 获取登录的ticket
def response = testRunner.testCase.getTestStepByName("登录成功").httpRequest.response;
def header = response.responseHeaders;
def cookies = header.get("Set-Cookie");
testRunner.testCase.testSuite.project.setPropertyValue("ticket", cookies.toString());
//添加project级变量ticket
2.1.4给测试工程下所有的api请求添加正确的http信息头
3.添加完成后选择作用范围
Target可以指定某个teststep,如图:
*如果要在工程中所有的api请求前都添加event handler默认target为空即可
脚本源码:
// 描述:处理请求头
def headers = request.requestHeaders;
log.info(context.expand( '${#Project#ticket}' ))
// 携带用户信息
// 获取方法
def method = request.getMethod().toString().toLowerCase();
// 获取提交的数据类型
def mediaType = request.getMediaType().toString().toLowerCase();
log.info(mediaType)
// API约定:不是表单提交,就是json格式提交
if (mediaType != 'multipart/form-data') {
request.setMediaType('application/json');
headers.remove("Content-Type");
headers.put("Content-Type", "application/json; charset=UTF-8");
headers.remove("Accept");
headers.put("Accept", "application/json");
}
headers.remove("Cookie");
headers.put("Cookie", context.expand( '${#Project#ticket}' ));
request.requestHeaders = headers;
2.2 Exercise相关api测试
2.2.1 add
1. 获取新增习题id
2.在groovy script step中右键
3.调试:使用log.info()方法
验证OK。
4.把exercise_id添加到project的Property中
5.添加JDBC链接:获取数据库中exercise表中最新的id值(id是自增长,最新id值和服务器返回的id值相等则通过验证)
笔者用的是MySQL数据库,配置如下:
注意:把第一章第一节中下载的mysql的jar包放在如下位置:
6.添加Assertion Step
配置如下:
2.2.2 view
1使用新增习题id参数化查看系统api的请求参数(将添加习题的id作为查看习题的请求id)
这样做同样有优缺点。
优点:后面的编辑和删除api接口可以同样调用该参数,这样可以重复对增删改查api接口进行重复测试,这是使用固定参数达不到的
缺点:api的测试有了耦合,如果新增接口失败会导致其他的接口跟着失败,跟实际情况不符
建议还是使用参数化方式,参数化方式更适合api的持续集成测试
2.返回值断言(脚本断言)共四步
第一步:定义预期结果
在定义预期结果前,我们先引入groovy中处理json解析器:JsonSlurper
源码如下:
//引入groovy中处理json解析器
import groovy.json.JsonSlurper;
//引用messageExchange对象包获取服务器返回的json字符串
def response=messageExchange.modelItem.testCase.testSteps["view"].testRequest.response.contentAsString;
//log.info(response);
//新建一个json解析器
def slurper=new JsonSlurper();
//把json结构解析成字符串的表达形式
def result=slurper.parseText(response);
OK我们完成了groovy中的json解析器的引入,开始预期结果的定义,源码如下:
def expect_exercise_id = context.expand( '${#Project#exercise_id}' )
def expect_exercise_content = "正常的单选题习题题干"
def expect_exercise_knowledge_point = "正常的单选题习题知识点"
def expect_exercise_analysis_content = "习题分析"
def expect_exercise_tags_id = 1
def except_exercise_tags_name = "企业职能"
def except_exercise_answers_content_1 = "选项A"
def except_exercise_answers_content_2 = "选项B"
def except_exercise_answers_content_3 = "选项C"
def except_exercise_answers_content_4 = "选项D"
def except_exercise_answers_exerciseId_1 = context.expand( '${#Project#exercise_id}' )
def except_exercise_answers_exerciseId_2= context.expand( '${#Project#exercise_id}' )
def except_exercise_answers_exerciseId_3 = context.expand( '${#Project#exercise_id}' )
def except_exercise_answers_exerciseId_4 = context.expand( '${#Project#exercise_id}' )
def except_exercise_answers_is_right_1 = "选项A"
def except_exercise_answers_is_right_2 = "选项B"
def except_exercise_answers_is_right_3 = "选项C"
def except_exercise_answers_is_right_4 = "选项D"
除了和id相关的预期结果定义为变量,其他的预期结果我们定义为常量
第二部:解析json字符串得到实际结果
//定义实际结果
def actual_exercise_id = result.id
def actual_exercise_content = result.content
def actual_exercise_knowledge_point = result.knowledge_point
def actual_exercise_analysis_content = result.analysis.content
def actual_exercise_tags_id = result.tags.id
def actual_exercise_tags_name =result.tags.name
def actual_exercise_answers_content_num = result.answers.content.size
def actual_exercise_answers_content_1 = result.answers.content[0]
def actual_exercise_answers_content_2 = result.answers.content[1]
def actual_exercise_answers_content_3 = result.answers.content[2]
def actual_exercise_answers_content_4 = result.answers.content[3]
def actual_exercise_answers_exerciseId_1 = result.answers.exerciseId[0]
def actual_exercise_answers_exerciseId_2= result.answers.exerciseId[1]
def actual_exercise_answers_exerciseId_3 = result.answers.exerciseId[2]
def actual_exercise_answers_exerciseId_4 = result.answers.exerciseId[3]
def actual_exercise_answers_is_right_1 = result.answers.is_right[0]
def actual_exercise_answers_is_right_2 = result.answers.is_right[1]
def actual_exercise_answers_is_right_3 = result.answers.is_right[2]
def actual_exercise_answers_is_right_4 = result.answers.is_right[3]
第三部:写断言
这里就不把所有的断言都写完了,例子如下:
assert expect_exercise_id == actual_exercise_id, "习题的id错误!"
assert expect_exercise_content == actual_exercise_content, "习题的content错误!"
assert expect_exercise_knowledge_point == actual_exercise_knowledge_point, "习题的knowledge_point错误!"
assert expect_exercise_analysis_content == actual_exercise_analysis_content,"答案解析内容错误!"
assert except_exercise_answers_content_num == actual_exercise_answers_content_num,"答案数量错误!"
第四部:调试脚本
运行测试脚本,结果如图:
处理方式1:把log.info(conents)注释掉,再运行,结果如下:
已经不是脚本本身的问题了,而是断言没有通过。
再看这个断言问题:
看上去这两个值好像是相等的,为什么断言还没有通过呢?
我们先看看这两个值的类型,是否都是integer类型
使用:
运行测试,结果如下:
expect_exercise_id是string类型,actual_exercise_id是integer类型,所以这两个值肯定是不相等的,就需要进行类型转换,我们这里把expect_exercise_id转成ingteger类型,再运行测试:
Id的断言测试已经通过了。
3 Jenkins持续集成
在第一章环境搭建中已经详细说明了jenkins的搭建过程,不再陈述,访问jenkins配置页面,进入研发人员创建的job(构建待测程序的job中),如图:
增加构建后步骤为:Build other projects,在要构建的项目中输入soapui的测试job:
选择在成功构建后触发soapui的测试job