根据接口文档中的入参,生成自动化测试用例中的异常测试用例,包含用例描述,用例数据
不想当将军的小兵,不是好的小兵;不想做开发的测试,不是好的测试;
不管你信不信,我是信了...
一直以来,内心总有些迷茫的时候,迷茫的是作为测试既然要学那么多编程,为什么不直接去干开发呢?
看了这句话,才发现自己钻进了牛角尖,没有站在更高的高度来思考测试这个岗位,而仅仅是作为一个测试员,或者说是抱着急功近利的心态在想问题,从而忽略了测试的本质。
测试的本质是尽可能的发现软件的缺陷,更高级的应该是用更少的时间,更少的人员来找出更多的缺陷。那么不管更好的测试流程,更高效的测试方法,更好的测试工具,这些都应该是我们追求的东西。
流程,方法,工具,这三者中我认为更重要的应该是方法,最难提升的也是方法。
编码啊工具之类的可以用到哪些提升哪些,
说到底,不管是开发中的编码,还是测试中的编码,都是很初级的工作,都不是这个职业链中的高端,高端应该是对设计对方法的研究与创新,甚至引发自己所处职业的一种潮流。
做好测试不是靠编程,而是靠的是严禁的作风,慎密的逻辑思维,适合的测试流程。从用户角度,到系统角度,到黑产角度,既保证给普通用户很好的用户体验,也保证系统运行平顺流畅,也保证不能应为hacker的攻击而功亏一篑
进入正题,最近在研究API接口中,异常测试用例的自动化问题,这边考虑的自动化,是通过接口说明文档,自动生成用例描述,用例的入参数据,用例的断言数据,当然测试的级别不是最高的,可以设置为2左右
比如一个注册接口,异常的测试用例,是不是需要判断,每一个必传入参是否传入,那这边就有2N条测试用例
1.请求传入的必传参数var1未传,验证返回var1....
2. 请求传入的必传参数var1传入的为空字符串(String类型)/null(..), 验证返回var1.....
异常测试用例还需要判断入参的长度,是不是又有了
3. 请求传入的参数var1超过最大长度限制,验证返回var1
有些入参有要求,比如用list,异常测试用例又有了
4.请求传入的参数Var1不是xx类型,验证返回
入参最好还需要判断是否将入参首位的空格去掉,异常测试用例又有了
5. 请求传入的入参首尾含有空格,验证存入DB中不含空格【要求代码对入参的首尾空格做trim()操作】
对接口的异常测试用例,弥漫着满满的套路气息,每次都进行编写测试用例,创建测试用例数据和断言等操作,入参多的时候,还是很费时间的,不利于提升工作效率。
更好的办法是:编写代码,自动生成测试用例描述,组装测试用例的数据,自动生成断言等
以前,我们一直用Jmeter创建的脚本来自动生成上述内容,
Jmeter生成jmx脚本,脚本中指定了入参个数、类型,长度等,之后生成用例描述,用例数据和断言
好处就是复用方便,更改下url直接测试
缺点就是,不利于动态生成用例,入参变化多了之后要重新创建新的脚本
还有一个缺点就是low,你若是说你用Jmeter做接口自动化测试的,大厂中厂都会嫌弃你low,因为人家是用Python做的,Java做的。
【low不low!!who cares?!!不是一样能完成任务吗,不一样能持续化集成并发送测试报告吗?你们一个组就100多号测试,也不就是完成这写接口的自动化测试吗,我们刚上手的实习生分分钟都能讲API测试用到飞】
感觉中国人,很喜欢用工具和方式来judge others,我们兄弟公司的人,大多是麻省理工学院的,api测试自动化还是用postman实现的呢,ui测试也只不过是protractor+jasmine等,人家不是low,够用就好,有时候已经不提倡测试,直接leader 做code review
现在动态实现接口异常测试用例的生成,用一个简单的例子
我们常见的接口说明文档,是不是长这样?
我页面还没开发完,大意就是讲入参,和比天性,类型,最大长度,等信息填入表中。
CREATE TABLE [DBO].[QA_APIVAR]( [APIVAR_ID] [INT] IDENTITY(1,1) NOT NULL PRIMARY KEY, [API_ID] [INT] NOT NULL, [APIVAR_NAME] [VARCHAR](100) NULL, [APIVAR_INPUTFLAG] [BIT] NOT NULL DEFAULT 1, [APIVAR_INPUTDATATYPE] [VARCHAR](100) NULL, [APIVAR_INPUTDATATYPEID] INT NOT NULL, [APIVAR_INPUTLENGTH] INT NOT NULL, [APIVAR_INPUTSCOPE] [VARCHAR](100) NULL, [APIVAR_INPUTNOTE] [VARCHAR](200) NULL, [APIVAR_DELETED_FLAG] [BIT] NOT NULL DEFAULT 0, [APIVAR_CREATEDT] DATETIME NOT NULL, [APIVAR_UPDATEDT] DATETIME NOT NULL )
代码单元测试部分,判断必传入参的未传,生成测试用例描述,测试数据body【这边以json格式为sample】,断言暂未写
package com.example.demo.api; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; import java.util.Random; @Controller public class ApiVar2Case { String ALLCHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; @Autowired private ApiVarService apiVarService; @Autowired private ApiCaseService apiCaseService; @RequestMapping("/CheckMustVar") //根据必传参数,依次生成"请求传入的xx参数未传,验证返回" public void CheckMustVar() { List<ApiVarBean> list = apiVarService.CheckMustVar(1); System.out.println(list); System.out.println(list.size()); int max = 0; String body = null; for (int i = 0; i < list.size(); i++) { String var = list.get(i).getVal(); var = "请求中未传入" + var + "验证返回" + var + "~~更多断言"; System.out.println(var); StringBuffer sb = new StringBuffer(); body = "{ \n"; for (int j = 0; j < list.size(); j++) { if (list.get(i).getVal() != list.get(j).getVal()) { Random random = new Random(); max = list.get(j).getInputLenght(); sb = new StringBuffer(""); for (int k = 0; k < max; k++) { sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length()))); } body = body + "\"" + list.get(j).getVal() + "\":\"" + sb + "\", \n"; } } body = body.substring(0, (body.length() - 3)) + " \n }"; System.out.println(body); ApiCaseBean apiCaseBean = new ApiCaseBean(); apiCaseBean.setApiCase_taskid(1); apiCaseBean.setApiCase_name(var); apiCaseBean.setApiCase_body(body); apiCaseBean.setApiCase_asseertion(null); apiCaseBean.setApiCase_priorityid(3); int count = apiCaseService.save(apiCaseBean); System.out.println(count); } } //根据必传参数,依次生成"请求传入的XX参数为空字符串" @RequestMapping("/CheckMustVarEmpty") public void CheckMustVarEmpty() { List<ApiVarBean> list = apiVarService.CheckMustVar(1); int max = 0; String body = null; for (int i = 0; i < list.size(); i++) { String var = list.get(i).getVal(); var = "请求中传入" + var + "为空字符串验证返回" + var + "~~更多断言"; System.out.println(var); StringBuffer sb = new StringBuffer(); body = "{ \n"; for (int j = 0; j < list.size(); j++) { if (list.get(i).getVal() != list.get(j).getVal()) { Random random = new Random(); max = list.get(j).getInputLenght(); sb = new StringBuffer(""); for (int k = 0; k < max; k++) { sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length()))); } body = body + "\"" + list.get(j).getVal() + "\":\"" + sb + "\", \n"; } else { body = body + "\"" + list.get(j).getVal() + "\":\"" + "\", \n"; } } body = body.substring(0, (body.length() - 3)) + " \n }"; ApiCaseBean apiCaseBean = new ApiCaseBean(); apiCaseBean.setApiCase_taskid(1); apiCaseBean.setApiCase_name(var); apiCaseBean.setApiCase_body(body); apiCaseBean.setApiCase_asseertion(null); apiCaseBean.setApiCase_priorityid(3); int count = apiCaseService.save(apiCaseBean); System.out.println(count); } } //根据接口中参数,依次生成"请求传入的XX参数为最大长度+1,即超过最大长度" @RequestMapping("/CheckVarOverLength") public void CheckVarOverLength() { List<ApiVarBean> list = apiVarService.CheckMustVar(1); int max = 0; String body = null; for (int i = 0; i < list.size(); i++) { String var = list.get(i).getVal(); int length = list.get(i).getInputLenght(); var = "请求中传入" + var + "的长度为" + length + "实际传入的长度为" + (length + 1) + "验证返回" + var + "~~更多断言"; System.out.println(var); StringBuffer sb = new StringBuffer(); body = "{ \n"; for (int j = 0; j < list.size(); j++) { Random random = new Random(); max = list.get(j).getInputLenght(); sb = new StringBuffer(""); if (list.get(i).getVal() != list.get(j).getVal()) { for (int k = 0; k < max; k++) { sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length()))); } } else { for (int k = 0; k <= max; k++) { sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length()))); } } body = body + "\"" + list.get(j).getVal() + "\":\"" + sb + "\", \n"; } body = body.substring(0, (body.length() - 3)) + " \n }"; ApiCaseBean apiCaseBean = new ApiCaseBean(); apiCaseBean.setApiCase_taskid(1); apiCaseBean.setApiCase_name(var); apiCaseBean.setApiCase_body(body); apiCaseBean.setApiCase_asseertion(null); apiCaseBean.setApiCase_priorityid(3); int count = apiCaseService.save(apiCaseBean); System.out.println(count); } } }
以下是生成的数据,我当然可以用个对象来接收,并存到数据库中去
[ApiCaseBean{num=1, val='name', InputFlag=1, InputDataType='String', InputTypeID=1, InputLenght=5, InputNote='null'}, ApiCaseBean{num=2, val='ID', InputFlag=1, InputDataType='String', InputTypeID=1, InputLenght=5, InputNote='null'}, ApiCaseBean{num=3, val='mobile', InputFlag=1, InputDataType='String', InputTypeID=1, InputLenght=5, InputNote='null'}, ApiCaseBean{num=6, val='apple', InputFlag=1, InputDataType='String', InputTypeID=1, InputLenght=6, InputNote='null'}, ApiCaseBean{num=7, val='orange', InputFlag=1, InputDataType='String', InputTypeID=1, InputLenght=6, InputNote='null'}, ApiCaseBean{num=8, val='pear', InputFlag=1, InputDataType='String', InputTypeID=1, InputLenght=6, InputNote='null'}]
6
请求中未传入name验证返回name~~更多断言
{
"ID":"zHUX3",
"mobile":"0J9jR",
"apple":"BTOfiV",
"orange":"ZUz1DB",
"pear":"8vHDVB"}
请求中未传入ID验证返回ID~~更多断言
{
"name":"8gJfa",
"mobile":"5pKjY",
"apple":"7vZVSJ",
"orange":"45cTEN",
"pear":"v9FliS"}
请求中未传入mobile验证返回mobile~~更多断言
{
"name":"Bi5Xm",
"ID":"OmYSR",
"apple":"IEYHCO",
"orange":"1zPaB6",
"pear":"Jw0d84"}
请求中未传入apple验证返回apple~~更多断言
{
"name":"gesB1",
"ID":"fSCeE",
"mobile":"HmY6j",
"orange":"SKInay",
"pear":"kDeurf"}
请求中未传入orange验证返回orange~~更多断言
{
"name":"KnSwW",
"ID":"9VKd2",
"mobile":"thAXp",
"apple":"apjji8",
"pear":"J3UkLR"}
请求中未传入pear验证返回pear~~更多断言
{
"name":"OTKPo",
"ID":"5WgEh",
"mobile":"gfJZA",
"apple":"aXksSk",
"orange":"JCCGZp"}
生成测试用例的表
CREATE TABLE [DBO].[QA_APICASE]( [APICASE_ID] [INT] IDENTITY(1,1) NOT NULL PRIMARY KEY, [API_ID] [INT] NOT NULL, [APICASE_NAME] [VARCHAR](100) NULL, [APICASE_BODY] [NVARCHAR](2000) NULL, [APICASE_ASSERCTION] [VARCHAR](100) NULL, [APICASE_PRIORITYID] INT , [APICASE_PASSID] INT, [APICASE_DELETED_FLAG] [BIT] NOT NULL DEFAULT 0, [APICASE_MEMO] [NVARCHAR](2000) NULL, [APICASE_CREATEDT] DATETIME NOT NULL, [APICASE_UPDATEDT] DATETIME NOT NULL )
有了用例描述和数据后,不管用什么接口测试工具,还是自已用Java/Python写的框架,导入数据即可
比如 https://www.cnblogs.com/qianjinyan/p/9951465.html
![](https://images.cnblogs.com/cnblogs_com/qianjinyan/910455/o_jasmine%20love.gif)