一、接口分类
接口成为api,分为内部接口、外部接口。
- 内部接口:开发一个系统,此系统提供了一些接口给本系统使用,对于安全性要求没那么高
- 外部接口:系统对外提供的接口。例如美团提供接口给外卖商家配送服务接口等。对于安全性要求会比较高
系统调用外部的接口:例如调用微信支付、支付宝支付等,这类接口只需要测正常用例即可
二、为什么要做接口测试
- 集成阶段
- 前后端分离、子系统分离
- 基于安全考虑:例如有些接口是可以绕过前端的若是页面功能测试是无法发现接口可能存在的问题的
- 测试前移:在开发阶段/前端页面未完成时测试就可以做接口mock测试
三、目前市面上的主流接口架构
基于restful的接口架构,默认是使用的http协议,默认使用json传输数据 对于同样的接口地址不同的请求方式得到的结果是不一样的。例如 get(查询用户)、post(增加用户)、put(修改用户)、delete(删除用户)
四、http协议
http协议是超文本传输协议,主要是用于浏览器和服务器之间交互数据,交互分为请求和响应两部分。请求包括请求行、请求头、请求正文数据。响应包括响应行、响应头、响应正文数据。
响应码:
- 1xx:请求已被接受,等待进一步处理
- 2xx:请求成功
- 3xx:重定向
- 4xx:客户端错误
- 5xx:服务器错误
有些网站中有cookie信息
- 清除cookie的方法:清空本地的cookie数据,然后shif+f5刷新
- cookie它不是缓存,他是保存在客户端的一小段文本信息,格式是dict格式的
- cookie的原理:当客户第一次访问服务器的时候,服务器就会生成cookie信息,这个cookie信息会通过响应头里面的set-cookie传输到客户端,从第2-n次的请求,只要访问当前的域名和路径则客户端会在请求的cookie里面自动的带上客户端的cookie信息
五、接口返回的数据格式
- json格式
- html格式
- xml格式 json是一种常用的数据格式,他是由键值对和列表组成 键值对:{key:values} 列表:[arrary1,arrary2] 接口开发的规则返回数据如下:
六、接口测试流程
- 1.熟悉接口api文档、熟练接口业务、接口地址、接口鉴权、接口入参、接口出参、错误码等。凡是有数据交互的地方就有接口
- 2.编写接口测试计划和方案 编写测试用例思路:
- 正向用例:输入正常的入参、接口成功返回(正向用例是一定要的)
- 反向用例:
- 鉴权反例:鉴权码为空、错误的鉴权码、鉴权码过期...
- 参数反例:参数为空、参数类型异常、参数长度异常、错误码异常等
- 其他场景:接口黑名单、接口调用次数限制、接口分页等根据业务而定
- 3.编写接口测试用例
- 4.使用接口测试工具执行接口测试
- 5.输出接口测试报告(html格式等)
七、市面上接口测试工具
- postman+newman+git+jenkins(newman主要用于和jenkins持续集成)
- jmeter+ant+git+jenkins
- 还有其他工具:如fiddler等
八、postman的安装和使用
安装:官网地址www.postman.com/
请求部分:
响应部分:
九、postman接口关联(接口依赖的处理)
下一个接口的参数使用的是上一个接口的返回值的处理。常用的有json及正则提取的方式
- json提取器(从接口返回值中取值)
- 正则表达式提取器(从接口返回值中取值)
- 从cookie中提取
- 从响应头中取值
//javascript脚本,var定义变量--json提取方式
//先打印出responseBody返回值,可以在console中查看
//console.log(responseBody);
//使用json提取器把responseBody返回值转换成一个字典,jsonData只是一个变量名
var jsonData = JSON.parse(responseBody);
//提取access_token
var access_token = jsonData.data.access_token;
//设置access_token为全局变量(全局变量就是在任何接口请求都可以访问的变量)
pm.globals.set("access_token",access_token);
//打印出提取出来的access_token值查看是否正确
console.log(jsonData.data.access_token)
//正则表达式提取
var token = responseBody.match(new RegExp('"access_token":"(.*?)"'));
//正则匹配到的是一个列表,取第一个值.并打印出来查看取值是否正确
console.log(token[1])
pm.globals.set("access_token",token[1])
//从响应头中提取值
var types = postman.getResponseHeader("Content-Type")
console.log(types)
//cookies提取值。下面以提取cookies中的csrf_token为例
var csrf_token = postman.getResponseHeader("csrf_token");
console.log(csrf_token.value)
十、postman动态参数
接口测试中常常会出现接口的参数不是固定的,必须使用随机数来实现的时候就需要使用到动态参数
- 内置的动态参数
- {{$timestamp}}:时间戳
- {{$randomint}}:随机的0-1000的整数
- {{$guid}}:随机很长的字符串
- 自定义动态参数
//自定义的时间戳
var times = Date.now();
//设置为全局变量
pm.globals.set("times",times);
//让接口请求停留3s,相当于python中的time.sleep
const sleep = (milliseconds) => {
const start = Date.now();
while (Date.now() <= start+milliseconds){}
};
sleep(3000);
十一、postman的全局变量和环境变量
全局变量:就是在所有接口请求里面都可以访问的变量 环境变量:就是全局变量(开发环境、测试环境、生产环境)
十二、postman的断言
断言判断一般是状态码的断言+业务断言
注意:
- 在postman中内置的动态参数无法做断言,必须使用自定义的动态参数
- 在tests里面不能使用{{}}的方法取全局变量,必须使用以下的方法
- pm.globals.get('test1')
- globals[''times1']
- globals.times1
十三、必须带请求头的接口如何测试
如果不知道接口需要带上哪些请求头并且接口文档中也没有标注时,只能把所有请求头带上再一个个排除
十四、postman+newman+jenkins实现自动生成报告并持续集成
步骤:
- 安装
- 安装node.js:node.js是newman的运行环境 node.js在官方网站下载后安装即可。 验证是否安装成功:cmd命令中输入node。有版本号就代表安装成功
- 安装npm。打开cmd输入npm install --global --production windows-build-tools,等待安装完成即可(一般安装成功node.js之后就会有npm,所以安装npm这个步骤可以省略),npm是一个依赖库
- 安装newman。cmd命令输入npm install -g newman 验证是否安装成功:cmd命令中输入newman -v出现版本信息就表示安装成功。安装成功Newman之后再安装报告:npm install newman-reporter-html进行安装(如果未安装可能后面生成报告时会提示未安装newman的报告器)
- 导出postman的测试用例、环境变量、全局变量(postman中对应的位置导出即可)
- newman run "测试用例导出存放的地址" -e "环境变量导出的存放位置" -g "全局变量导出存放的地址" -r cli,html,json,junit --reporter-html-export "导出报告输出的路径"
- 例如:newman run "D:\桌面\New Collection.postman_collection.json" -e "D:\桌面\postman_environment.json" -g "D:\桌面\workspace.postman_globals.json" -r html --reporter-html-export "D:\result\test_result.html"
3. 与jenkins持续集成
新建项目--构建执行window批处理命令--构建后选择publish html reports
十五、接口鉴权(鉴定是否有访问接口的权限)
-
cookies、session、token鉴权
- cookies鉴权:是服务器产生的,保存在浏览器,主要是因为http 协议无连接,无状态 cookie通过键值对的方式来保存记录。原理:第一次访问服务器的时候,服务器就会产生cookie并且在响应头的set-cookie里面发送给浏览器,浏览器保存,在第2-n次请求时会带上cookie信息 cookie一般包括信息有:name,value,domain,path,size,expries
- session鉴权:session它是服务器产生的,保存在服务器的内存,它可以通过cookie来传sessionid。原理:登录时,服务器会生成session,通过cookie把sessionid传给客户端,后面的所有请求里面都会自动的带上sessionid,,然后和服务器内存中的sessionid对比判断是否是同一个客户端。保存在服务器的内容存里面的失效时间为30分钟(这个时间是可以改的)
- token鉴权:token是服务器产生的,保存在服务器的文件或者数据库,一般情况下接口测试通过一个专门获取token的接口或者是登录接口来获取token,获取后每次请求其他接口时都必须带上token
-
cookie、session、token的相同点和不同点
- 相同点:都是在服务器端产生的,都是用于鉴权,只是保存在不同的位置
- 不同点:cookie保存在客户端,不安全。一般情况下用cookie保存一些非重要的数据。session会保存一些重要的数据(session是保存在服务器内存中)。token是独立的鉴权,它跟session、cookie无关
-
postman的鉴权方式
十六、接口mock servers
- mock测试就是测试过程中对于一些不容易创建或者是不容易获取的对象,用一个模拟的对象去替代
- mock是为了解决单元之间的耦合依赖关系。(桩服务)
- 例如:可以用于前后端联调、单元测试。第三方接口依赖等。实际mock服务就相当于是依赖服务的替身。并不需要去构造实现完整的服务逻辑,比如现在需要测试服务a,但是服务a依赖于服务b,则可以通过mock来替换b服务(mock一个假的b服务替身,mock不需要实现服务b真实的逻辑等,只需要按照处理逻辑返回给a对应的返回数据即可)
十七、接口加解密
接口加密:把传输数据按照一定的方式加密成密文再传输
接口解密:获取密文后按照解密方式还原成原始数据
目前的加密方式:
-
对称加密(私钥加密):des、aes、base64 -
非对称加密(双玥加密):RSA(公钥和私钥) -
只加密不解密:MD5/SHA1/sha3....
一个好用的加解密网站:bejson.com
//Md5加密的方式,tostring转换成字符串,touppercase换成大写(若是接口要求的是大写则要转成大写)
var user = CryptoJS.MD5("admin").toString().toUpperCase();
var password = CryptoJS.MD5("123456").toString().toUpperCase();
//加密后把账号密码设置为全局变量
pm.globals.set("user",user);
pm.globals.set("password",password);
十八、接口签名sign
对于安全性要求比较高的都会使用到接口签名,也是鉴权的一种。一般用于银行项目,支付相关
什么是接口签名
接口签名就是使用appid,appsecret(秘钥),nonce(流水号),timestamp(时间戳),以及请求参数(params/data)按照一定的规则(ASCII排序)组成字符串,再进行加密,这个经过加密之后的字符串就是sign签名
- appid和appsecret在线下针对不同接口调用方提供的
- 流水号一般是一串10位以上的随机一组数字或者随机的一组字符串。数字+字符串(guid)
- timestamp时间戳,一般10分钟内有效
为什么要做接口签名
- 防止伪装攻击(只需要提供签名,不需要提供鉴权码,从而防止鉴权码泄露)
- 防止数据篡改(原理:签名针对的是所有的请求数据,只要有一个数字改动了,那么sign就会变,导致请求失败)
- 防止重放攻击(接口被重复提交),nonce是唯一的,并且只有10分钟有效
接口签名的规则有很多,每个公司的都会有区别,但是大部分规则都相似。如图
整个签名过程(每一个过程都要打印出来看一下值是否是正确的)
//1.获取到appid和appsecret.这个是第三方给开发者明确身份的标识(对方会提供的)
var appid = "test";
var appsecret = "123456";
//2.获得nonce流水号
var nonce = getnonce(1000000000,9999999999);
console.log(nonce);
//3.获得时间戳
var timestamp = new Date().getTime();
console.log("timestamp",timestamp);
//4.获取到params中的参数
var params_data = pm.request.url.query.members;
console.log("params_data",params_data);
//5.获取到body里面的参数(JSON.parase加载成对象,JSON.stringify加载成字符串)
//request.data打印出来是个对象,需要加载成字符串打印出来
var body_data = request.data;
console.log("body_data1",JSON.stringify(body_data));
//6.把params跟body参数组成一个变量
for (var i=0;i<params_data.lenth;i++){
body_data[params_data[i].key]=params_data[i].value;
}
console.log("body_data2",JSON.stringify(body_data));
//这个可以把params跟body两个参数合并并组成一个变量(不知道数据格式是不是需要这样的,后面的都先用com而没有用body_data)
var com = {params_data,body_data}
//7.把组合的数据按照key升序
body_data = objectsort(com);
console.log("77body_data排序",JSON.stringify(com));
//8.把字典格式的参数转换成key=value&key=value的格式
var new_string = "";
for (var key in com){
new_string += key +"="+com[key]+"&";
}
console.log("new_string"+JSON.stringify(new_string));
//9.在字符串前面加上appid和appsecret,在字符串后面加上nonce,timestamp
new_string = "appid="+appid+"&"+"appsecret="+appsecret+"&"+new_string+"nonce"+nonce+"&"+"timestamp"+timestamp;
console.log("222",new_string);
//10.对上述字符做md5加密后并大写形成sign签名,然后把sign保存为全局变量
var sign = CryptoJS.MD5(new_string).toString().toUpperCase();
console.log("sign",sign);
//...........................................................................
//获得任意长度的随机数字
//Math.random()返回0-1的伪随机浮点数,math.floor向下取整
function getnonce(min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}
//把对象的key升值排序
function objectsort(obj){
var new_key = Object.keys(obj).sort();
console.log(new_key);
var arr = {};
for (var i=0;i<new_key.lenth;i++){
arr[new_key[i]] =obj[new_key[i]];
}
return arr;
}