前言
大家好,我是洋子。先祝大家端午节安康,记得吃粽子哦
在之前的文章《详解接口测试(1)-常见的网络通信协议》当中,我们介绍了接口的概念以及各种类型的接口用到的网络通信协议。鉴于HTTP网络协议使用最为广泛,本文将会基于HTTP协议的接口对其讲解如何进行接口测试,内容包含以下部分:
- 接口测试是什么
- HTTP接口的测试用例设计
- HTTP接口的测试方法
- 接口的测试策略
接口测试是什么
接口测试是对测试系统组件间接口进行测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系
调用一个接口A后,通常会触发下面的业务逻辑
- (*)在
数据库
、Redis
、ES
(Elasticsearch)、或者Kafka
里面查询数据、写入新的数据,或者更新历史老的数据 - 接口A继续调用下游的另外一个接口B,将接口B的数据作为接口A的返回值
- 数据处理完毕后,把数据格式化成
Json
或者HTML
等返回给前端 - 进行其余业务逻辑处理
从上面我们可以得知,调用一个接口后,数据的流动情况。以上述业务逻辑(*)为例,有两条最基本的数据流,分别是写入数据流和查询数据流
- 写入数据流:从外部参数传入调用接口后,经过业务处理,数据最终会存储到
数据库
或者缓存
当中 - 查询数据流:从外部参数传入调用接口后,经过业务处理,查询
数据库
或者缓存
,将查询到的数据从接口返回
所以在接口测试时,涉及数据库相关的逻辑,我们都需要去检查数据库或者缓存里的数据是否符合预期,这是一个很重要的测试点,测试点可以转换成接口测试用例,在下面会谈到如何设计接口测试用例
细心的小伙伴可能会问,光看接口文档,我怎么知道一个接口调用后背后都做了些什么?
想要彻底了解清楚调用接口后触发的业务逻辑,更好的进行质量保障,需要测试人员直接去看懂开发写的代码,并进行白盒测试
,这对测试人员代码code review要求比较高,所以更多企业退而求其次,要求测试人员进行接口测试
如果看不到开发写的代码,只进行接口测试
仍然是属于黑盒测试
的范畴
不过对于互联网大厂的服务端测试来说,测试一个业务需求一般是code review 接口测试 白盒测试 结合起来进行,这样能更好的保障所测需求的质量
HTTP接口的测试用例设计
和功能测试一样,在进行接口测试前,需要进行接口的测试用例设计,用例设计也不是凭空想象,我们需要以相关文档为依据
- 功能测试用例:一般是根据
产品需求文档PRD
或者交互设计文档
来进行设计 - 接口测试用例:一般是依据
接口文档
和技术方案
来进行设计
一个合格的HTTP接口文档,至少应具备如下信息,即请求方式(一般为GET或者POST)、请求URL、请求参数、预期返回结果
,举个接口文档的例子
请求方式
Get
请求URL
/test/api/getlist
请求参数
参数名 | 是否必传 | 数据类型 | 说明
source_type Y int 来源:百度1;好看2;贴吧3
page Y int 页码
per_page Y int 每页数量
word N string 关键词
预期返回结果
{
"errno": 0,
"status": 0,
"msg": "success",
"time": 1637156975,
"duration": 0.01242208480835,
"requestid": 2975372392
}
开发提供的接口文档可能存在不规范或者压根没有接口文档的情况,这时候需要推动开发补齐信息,不然对测试来说没有接口文档就无法提前设计用例,并且进行测试也比较困难
如果实在没有接口文档,那么测试只能通过Charles
、Fiddler
等抓包工具,通过线上环境或者测试环境抓包获取接口信息
有了接口文档以后,我们就可以开始接口测试用例的设计,而接口测试用例设计的重点,在于功能性的业务逻辑检查
和参数检查
。可以从输入(请求)参数、接口业务逻辑处理、接口输出(返回结果) 三个方面来进行考虑
另外对于客户端功能测试的用例设计方法,如等价类和边界值,在接口测试用例设计对于设计输入参数的部分同样适用,如对于请求参数的有大小限制,运用边界值设计,我们可以分别取空
、最小值
、最小值-1
、最大值
、最大值 1
来测试
接口业务逻辑处理的接口用例设计是重中之重,对于这方面的用例设计,除了仔细看接口文档,还需要我们详细阅读开发写的技术方案,在技术方案当中一般有程序流程图
,数据流图
或者时序图
如以下时序图,我们可以清晰的知道每个接口做了哪些操作,关键参数传递,数据如何存放、消费等,从而方便检查当调用接口后数据的落库情况,以及调用接口后业务逻辑是否符合预期
用例除了正常的接口功能流程,接口业务逻辑异常场景也需要考虑,常见的异常场景是数据库连接超时、没有满足前置条件,如:某个接口需要先登录获取 sesssion,如果直接调用该接口应该给出相应提示
接口用例设计小结
如果输入参数、接口业务逻辑处理(正常与异常流程)、返回结果
这三个方面考虑全面的话,接口的功能测试用例基本可覆盖以下三点:
- 主流程测试用例:正常的主流程业务需求校验
- 分支流程测试用例:正常的分支流程需求校验
- 异常流程测试用例:异常业务场景的
容错
校验
代码覆盖率是用来量化接口测试用例的覆盖程度,想要了解的朋友可以看下我之前的文章《什么是代码覆盖率》
HTTP接口的测试方法
接口测试用例设计完毕后,我们就可以开始正式进行接口测试了,一般接口测试都是对单个接口按照接口用例逐一进行测试
而接口测试方法,最开始可以使用Postman
等工具进行手工测试,然后我们可以用Python
或者JAVA
编写代码进行自动化测试,自动化测试的代码可以逐步完善形成我们的测试框架,在回归测试时解放双手,提高测试效率
手工测试
可以用来做接口测试的工具非常多,Postman
、 PostWoman
、 IntelliJ IDEA 的 HTTP Client
、 JMeter
等等
还是Postman来演示接口手工测试吧,由于我们这里没有接口文档,先使用Charles抓包获取某个接口信息(正常的接口测试流程,开发会提供接口文档)
我们设置好Charles的代理后(还不知道怎么使用Charles可以看我以前的文章《Charles从入门到精通》),打开B站网页版就可以开始抓包
经过我的一番观察,看到了B站的接口地址,我们就用ranking/region
这个接口来演示,在Charles的界面,我们可以看到这个接口URL,请求方式是GET,请求参数有两个(分别是rid和day),Cookies等信息
HTTP请求Header里面,有几个比较重要的属性,这里统一讲解一下
- Cookie:Cookie里面传递的参数一般都是用来确认用户身份、鉴定角色权限等,一些依赖登陆校验的接口,必须传入合法的Cookie,才能正常返回结果
- Content-Type:该字段出现在请求Header时,在它被用来告诉服务端如何处理请求的数据,出现在响应Header时,会告诉客户端(一般是浏览器)如何解析响应的数据。Content-Type常见有4种类型,application/x-www-form-urlencoded(键值对),application/json(json格式),text/xml(xml格式),multipart/form-data(字节流二进制格式)
- Content-Length:用来指明发送给接受方的消息主体的大小
- HOST:它表示访问的服务器域名,也可以是ip地址。如果发送HTTP请求时,携带的该字段域名无法正常解析出IP,找不到服务器资源,就会报404
- Connection:值为keep-alive,表示需要持久连接(长连接)
- User-Agent:它说明请求是从什么浏览器发出去,我们程序当中可以模拟UA,前端或者后端会根据UA来判定用户是来自于PC浏览器,还是手机APP,来返回不同的页面
- Accept:表示客户端可以接收的类型,如Json
- Accept-Encoding:设置为gzip、deflate、br,表示可以支持的Web服务器返回内容压缩编码类型
- Accept-Language:表示接受的语言
- Sec-Fetch-Site和Sec-Fetch-Mode:JavaScript中对跨域的相关设置
抓取到接口信息后,除了手动填写接口信息到Postman当中,我们还可以通过Charles拷贝CURL功能,直接导入到Postman,省去手动填写的时间
最后一步,在Postman点击Send按钮,我们就成功用Postman模拟浏览器发送了一个HTTP请求用于手工测试,接口的返回值我们可以和开发给的接口文档进行对比,当发现和预期不符时,排除环境等因素,我们就可以跟开发报Bug了
再安利一个Postman的实用功能,可以看到上图我们的域名是写死的,在日常测试当中,如果我们接口用例非常多,在线下测试、上线回归都会用到不同的域名或者ip地址,这时候如果写死域名,就需要一个个手动修改非常麻烦,我们可以设置环境变量,直接修改环境变量就可以修改域名地址
切换环境变量,把接口域名替换成环境变量就OK了,格式为{{环境变量名}}
自动化测试
当接口手工测试完毕后,我们就可以着手进行接口自动化测试用例以及相关代码的编写,使用 Python 或者 Java等语言结合自动化测试框架,编写自动化测试脚本,实现接口自动化测试、结合持续集成自动执行及自动发送测试报告等环节
接口自动化测试框架,我们可以自己造轮子,也可以使用成熟的框架,一个好的接口自动化测试框架应该涵盖以下几点:
- 流程方面:在回归阶段加强接口各种场景的覆盖度,并逐步向系统测试, 冒烟测试阶段延伸,最终达到全流程自动化。
- 结果展示:更加丰富的结果展示、趋势分析,质量统计和分析等
- 问题定位:报错信息、日志更精准,方便问题复现与定位
- 结果校验:加强自动化校验能力,如数据库信息校验
先说一下自己造轮子,我这里用Python给大家例子,下面这段代码就可以实现发送HTTP请求,但接口URL域名都是写死的,我们需要改写,把公共的部分抽象出来
代码语言:javascript复制# Python代码中引入requests库,引入后才可以在你的代码中使用对应的类以及成员函数
import requests
# 建立url_index的变量,存储战场的首页
url_index='http://127.0.0.1:12356/'
# 调用requests类的get方法,也就是HTTP的GET请求方式,访问了url_index存储的首页URL,返回结果存到了response_index中
response_index = requests.get(url_index)
# 存储返回的response_index对象的text属性存储了访问主页的response信息,通过下面打印出来
print('Response内容:' response_index.text)
抽象出一个Common类
代码语言:javascript复制# 定义一个common的类,它的父类是object
class Common(object):
# common的构造函数
def __init__(self,url_root):
# 被测系统的跟路由
self.url_root = url_root
# 封装你自己的get请求,uri是访问路由,params是get请求的参数,如果没有默认为空
def get(self, uri, params=''):
# 拼凑访问地址
url = self.url_root uri params
# 通过get请求访问对应地址
res = requests.get(url)
# 返回request的Response结果,类型为requests的Response类型
return res
# 封装你自己的post方法,uri是访问路由,params是post请求需要传递的参数,如果没有参数这里为空
def post(self, uri, params=''):
# 拼凑访问地址
url = self.url_root uri
if len(params) > 0:
# 如果有参数,那么通过post方式访问对应的url,并将参数赋值给requests.post默认参数data
# 返回request的Response结果,类型为requests的Response类型
res = requests.post(url, data=params)
else:
# 如果无参数,访问方式如下
# 返回request的Response结果,类型为requests的Response类型
res = requests.post(url)
return res
最后看看如何调用
代码语言:javascript复制# Python代码中引入requests库,引入后才可以在你的代码中使用对应的类以及成员函数
from common import Common
# 建立uri_index的变量,存储战场的首页路由
uri_index = '/'
# 实例化自己的Common
comm = Common('http://127.0.0.1:12356')
#调用你自己在Common封装的get方法 ,返回结果存到了response_index中
response_index = comm.get(uri_index)
# 存储返回的response_index对象的text属性存储了访问主页的response信息,通过下面打印出来
print('Response内容:' response_index.text)
# uri_login存储用户接口路径
uri_login = '/login'
# username变量存储用户名参数
username = '123456'
# password变量存储密码参数
password = '123456'
# 拼凑body的参数
payload = 'username=' username '&password=' password
response_login = comm.post(uri_login,params=payload)
print('Response内容:' response_login.text)
完成到这里,我们自己只是完成了接口的发送通用化,如何进行用例管理,如何发送测试报告我们都还没有做,往往还是要跟开源的测试框架结合,比如Java的JUnit、Python的Unittest等等
所以洋子还是不太建议自己去手动造轮子,因为成熟的接口自动化框架有非常多,已经非常完善,如果有定制化需求,我们也可以基于这些框架进行二次开发修改,如果用Python,我们可以使用unittest/pytest/robotframewok/httprunner/nose等等。如果用Java,可以使用Junit/TestNG/REST-assured等框架
我自己之前已经写过robotframewok框架的使用教程,感兴趣可以查看文章《在培训机构也学不到的Robot Framework自动化测试企业级实战教程》
接口测试策略
前面我们所谈到的接口测试,都是对接口的功能层面进行测试。如果接口需要承载的QPS非常高,我们还需要进行接口的性能测试。如果该接口涉及交易等重要场景,还需要进行接口安全测试
结束语
本文跟大家介绍了HTTP接口的测试用例设计以及测试方法,记得要多加练习,我们才能掌握接口测试