专栏地址:https://www.nowcoder.com/tutorial/10032/index
1. 什么是接口测试?
要谈接口测试,先要清楚什么是接口,接口即通常所说的API(Application Programming Interface,应用程序接口)是一些预先定义的函数,或指软件系统不同组成部分衔接的约定。 用来提供应用程序与开发人员基于某软件或硬件得以访问的一组例程,而又无需访问原码或理解内部工作机制的细节(接口定义来源于百度百科)。简单来说,接口就是将一些复杂的操作封装起来方便其他程序调用,调用的形式可以是本地函数,也可以是网络请求。
接口测试就是对上述接口进行的一种测试,主要用于测试系统与外部其他系统之间的接口,以及系统内部各个子模块之间的接口。测试的重点是要检查接口参数传递的正确性,接口功能实现的正确性,输出结果的正确性,以及对各种异常情况的容错处理的完整性和合理性,我们通常所讲的接口测试大都是基于http协议的接口,因此本文重点针对http协议的接口测试进行讲述。
接口测试通常分为三个步骤:
- 准备测试数据(非必需)
- 请求接口
- 验证请求后的响应结果
2. 接口请求示例
下图中的门禁,可以理解为接口,用户刷门禁卡的过程可以理解为请求接口的过程,假设用户持有两张卡,一张是“open卡”、一张是“close卡”,当用户刷“open卡”时,相当于给接口参数p传递参数值“open”,此时门禁控制开门,当用户刷“close卡”时,门禁控制关门。
3. 接口URL
相信通过上面的例子,你已经明白接口测试是怎么一回事了,接下来一起来理解HTTP的URL是怎么组成的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Od02KHqc-1610589527579)(https://uploadfiles.nowcoder.com/images/20200702/896606139_1593689226176_C41FCBD588985BD375CE7661BCE13E4E “接口url”)]
上图中包含5个主要部分,分别是请求协议、ip/域名、端口、接口路径、接口参数及参数值,下面分别说明:
❶请求协议:
- http — Hyper Text Transfer Protocol的缩写,是客户端浏览器或其他程序与Web服务器之间的应用层通信协议 ,其基于TCP/IP通信协议来传递数据
- https — Hyper Text Transfer Protocol over SecureSocket Layer的缩写,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性
❷请求IP/域名:
提供接口系统所部署的服务器ip或域名地址,生产环境一般只对外提供域名访问,一是相对安全,二是方便运维维护
❸请求端口:
主机用来监听http请求的端口号,端口号是跟在URL的IP或域名地址后面的,用“:”分隔开
- http默认端口:80
- https默认端口:443
❹接口路径:
端口后“/”和参数前“?”中间的一串,称之为接口路径,上面示例中的接口路径为“door”,是比较特殊的一种,其与接口名称一致,而实际业务场景中,接口路径中可能会包含多个“/”,形如“xx/yyy/door”
❺接口参数及参数值:
接口路径和参数之间用“?”来分隔,“?”后面就是参数和参数值组成的键值对,参数和参数值用“=”连接,多个参数之间用“&”来分隔,例如我们上述接口URL中增加1个参数user,参数值为Xiaoming,则完整URL为:
http://127.0.0.1:5000/door?p=open&user=Xiaoming
Tips:
有些请求的URL中并没有"?"及其后面的参数对,这有可能是下面两种情况:
- POST等类型的请求,参数对包含在body内
- 请求本身不带有参数信息
4. 接口测试实例
接下来我将用门禁实例,带你一步步做接口测试,对于门禁的接口测试,最基本的就是验证:p的值分别为“open”、“close”的时候,门是不是可以正常开门、关门。首先我们用python实现门禁接口的功能,代码如下(示例代码参见GitHub:https://github.com/8784285/nowcoder.git ,文件路径:2.1/2.1.py):
#coding:utf-8
__author__ = 'Mr. null'
from flask import Flask, request, jsonify
app = Flask(__name__)
# 定义door接口,参数p值为"open"时返回开门,为"close"时返回关门
@app.route('/door',methods= ["GET"])
def door():
p = request.args.get("p")
if p == 'open':
return jsonify(code=0,msg="开门")
elif p == 'close':
return jsonify(code=1,msg="关门")
if __name__ == '__main__':
app.run(host='0.0.0.0',debug= 'True')
Tips:
Flask:Flask是一个使用 Python 编写的轻量级 Web 应用框架,较其他同类型框架更为灵活、轻便、安全且容易上手,用它可以轻松的实现http api的开发。
有关Flask的详细教程可进一步参考:https://www.w3cschool.cn/flask/
运行上述代码,启动http服务器,然后我们用postman工具(有关postman工具的安装,参见附录-postman安装指南)来做接口测试
case1:开门请求
url栏中输入“http://127.0.0.1:5000/door?p=open”, 点击【Send】发送请求,得到了“开门”的预期结果,如下图:
Tips:
case:本专栏中所提及的case均为“测试用例”之意
case2:关门请求
如果p参数值改为“close”,同样得到了“关门”的预期结果,如下图:
5. 请求消息结构
上面我们在postman工具中向服务器发送了请求,这个请求的报文如下,它包括:请求行、请求头部(header)、空行和请求数据四个部分组成。
1)GET类型请求报文示例
GET /door?p=open HTTP/1.1 <!--[请求行]-->
User-Agent: PostmanRuntime/7.20.1 <!--[请求头部]-->
Accept: */* <!--[请求头部]-->
Cache-Control: no-cache <!--[请求头部]-->
Postman-Token: cd074d2a-aa96-4970-ad27-846988d80de0 <!--[请求头部]-->
Host: 127.0.0.1:5000 <!--[请求头部]-->
Accept-Encoding: gzip, deflate <!--[请求头部]-->
Connection: keep-alive <!--[请求头部]-->
<!--[空行]-->
<!--[请求数据,GET方法为空]-->
上述示例中,GET类型请求各部分对应的行如下:
- 请求行:第1行
- 请求头部:2-8行
- 空行:第9行
- 请求数据:GET方法为空
2)POST类型请求报文示例
POST /login HTTP/1.1 <!--[请求行]-->
Content-Type: application/json <!--[请求头部]-->
User-Agent: PostmanRuntime/7.20.1 <!--[请求头部]-->
Accept: */* <!--[请求头部]-->
Cache-Control: no-cache <!--[请求头部]-->
Postman-Token: 2a717a85-7f4c-4f2b-b22a-655d75afd976 <!--[请求头部]-->
Host: 127.0.0.1:5000 <!--[请求头部]-->
Accept-Encoding: gzip, deflate <!--[请求头部]-->
Content-Length: 78 <!--[请求头部]-->
Connection: keep-alive <!--[请求头部]-->
<!--[空行]-->
{ <!--[请求数据]-->
"username": "Mr.null", <!--[请求数据]-->
"password": "dc483e80a7a0bd9ef71d8cf973673924" <!--[请求数据]-->
} <!--[请求数据]-->
❶请求方法:
请求行中,很重要的一个信息是请求方法,http的请求方法有如下9种,其中序号1-3的方法为http1.0版本定义,序号4-9的6种方法为http1.1新增。
序号 | 方法 | 描述 |
---|---|---|
1 | GET | 请求指定的信息,并返回实体主体 |
2 | HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交数据(例如提交表单或者上传文件),数据包含在请求体中 |
4 | PUT | 从客户端向服务器传送的数据更新指定的内容 |
5 | DELETE | 请求服务器删除指定的内容 |
6 | CONNECT | 预留给能够将连接改为管道方式的代理服务器,如使用SSL |
7 | OPTIONS | 查看服务器的性能 |
8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断 |
9 | PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 |
不同的请求方法定义了数据或者资源的不同操作方式,通常,接口程序都应当遵守http协议定义的操作规范,以便增加程序的可读性以及可维护性。因此,验证接口程序是否按照http定义的操作方式实现对数据或者资源的操作,也是接口测试的一个重要内容。
❷请求头:
上面GET/POST类型请求报文中都包含请求头信息,实际应用中,请求头部的参数不止上述GET/POST类型请求报文示例中的7个和9个,测试经常用到或者需要关注的header头信息主要包括如下:
协议头 | 说明 | 示例 |
---|---|---|
Accept | 可接受的响应内容类型(Content-Types) | Accept: text/plain |
Accept-Charset | 可接受的字符集 | Accept-Charset: utf-8 |
Accept-Encoding | 可接受的响应内容的编码方式。 | Accept-Encoding: gzip, deflate |
Accept-Language | 可接受的响应内容语言列表。 | Accept-Language: zh-CN |
Authorization | 用于表示HTTP协议中需要认证资源的认证信息 | Authorization: Basic OSdjJGRpbjpvcGVuIANlc2SdDE== |
Cache-Control | 用来指定当前的请求/回复中的,是否使用缓存机制 | Cache-Control: max-age=0 |
Connection | 客户端(浏览器)想要优先使用的连接类型 | Connection: keep-alive |
Cookie | 由之前服务器通过Set-Cookie(见下文)设置的一个HTTP协议Cookie | Cookie: NOWCODERUID=24D03102EFBD820E19AB242B1BB1ECED |
Content-Length | 以8进制表示的请求体的长度 | Content-Length: 348 |
Content-Type | 请求体的MIME类型 (用于POST和PUT请求中) | Content-Type: application/json |
Date | 发送该消息的日期和时间(以RFC 7231中定义的"HTTP日期"格式来发送) | Date: Tue, 26 May 2020 21:30:00 GMT |
Referer | 表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接将浏览器带到了当前页面 | Referer: https://blog.nowcoder.net/detail/dd0c58608bc7421e8bcb88f75652746e |
User-Agent | 浏览器的身份标识字符串 | User-Agent: Mozilla/…… |
上文我们介绍了接口的请求示例、接口url组成、接口请求消息结构等内容,并用门禁卡的实例演示了两个接口测试的测试用例,如果进一步深究,对于请求url"http://127.0.0.1:5000/door?p=open", 还有如下问题需要考虑
- 少p参数,接口如何处理?
- p参数的参数值为push,与接口定义的“open”、“close”不一致,接口如何处理?
- p参数的参数值为1,那这个1到底是字符型还是数值型?
- p的参数值包含特殊字符或中文,url会怎么处理?
上述问题是很多api接口bug产生的根源,也是作为测试人员需要设计的基本测试用例内容,下一节我们继续探讨这些问题的答案,同时,分析接口测试中涉及到的异常场景测试点、安全测试点、性能测试点等关键测试点,形成完整、实用的接口测试点大纲,相信对你一定会有帮助。
另外,上述门禁功能,用户手里的门禁卡是非常简陋的,就是一块卡片上面印了“open”字样,只要别人看到这个信息,就可以自己拿张卡片上面打印“open”伪造出一个门禁卡,这个伪造的门禁卡同样也可以打开门,在实际应用中怎么解决这个问题呢?《第三章-接口测试进阶篇》中,我们继续用代码实例深入分析、探讨这个问题。
我的微信如下:
关注公众号,更多精彩