接口自动化测试 - requests 库
目录
1. 接口自动化的流程
1.1 挑选自动化接口
1.2 编辑接口测试用例
1.2.1 根据接口设计
1.2.2 结合业务逻辑设计
2. code
2.1 创建 Python 项目
2.2. 安装 requests 库
2.3 发送 http 请求
2.3.1 Response 对象
2.3.2 requests 提供的请求方法
2.3.2.1 请求方法参数
接口分为两种:
- 程序内部的接口: 所谓程序内部接口, 就是程序内部的方法与方法之间, 模块与模块之间, 互相调用的接口. 比如: 我们使用一个未登录的账号在 CSDN 点击发布博客时, 就会弹出登录界面, 要求我们必须登录后, 才可以发布博客, 此时, 就是 “发布博客接口, 调用了用户登录接口”.
- 系统对外的接口: 提供给用户使用的接口(比如我要在一个博客系统上查看一个博客, 服务器不可能直接把数据库给我们, 而是提供给用户一个查询博客的接口)
1. 接口自动化的流程
进行接口自动化的流程如下:
- 需求分析
- 挑选自动化接口
- 编写接口测试用例.
- 搭建自动化测试环境
- 选择编程语言 和 开发环境(IDE)
- 安装所需库.(Python: requests)
- 设计自动化框架(哪些包 [测试包/通用工具包/测试报告] /不同包放哪些代码)
- 编写代码.
- 执行测试用例(用例都转化成了代码)
- 输出测试报告.
1.1 挑选自动化接口
一个项目的接口是非常多的, 我们是无法保证覆盖所有的接口的.
我们可以从以下几个角度挑选自动化接口:
- 功能复杂度: 优选挑选功能复杂, 逻辑分支多的接口. 如: 涉及多种支付方式/订单状态的转换
- 高风险功能: 优先挑选风险高的接口. 如: 涉及资金的支付接口.
- 重复性高: 自动化的一个重要目的, 就是为了解决重复的测试功能, 因此挑选一些重复性高的测试接口. 如: 登录接口.
- 不易改变的接口: 避免对自动化代码进行频繁的维护.
1.2 编辑接口测试用例
对挑选好的接口, 设计接口测试用例.
设计接口测试用例, 需要从两方面去考虑:
- 通过接口设计测试用例
- 结合业务逻辑设计测试用例.
1.2.1 根据接口设计
通过接口设计测试用例, 我们可以从以下几个方面去考虑:
- 通过性验证(正向测试): 传入正确的参数, 返回的结果是否符合接口文档.
- 参数组合:
- 必选参数与可选参数组合:只传必选参数;必选参数+部分可选参数;必选参数+全部可选参数
- 不同参数值的组合:例如一个接口有“状态”和“类型”两个参数,需要测试不同状态和不同类型的组合情况,看筛选结果是否正确
- 边界值组合:如果参数有范围(如年龄、数量),需要测试其有效范围的边界值组合
- 安全性验证:
- 是否存在 SQL 注入: ' or 1 = 1 --
- 是否存在饶过问题: 购买一个商品, 付款时通过工具把价格修改为 0/负数.(后端没有进行饶过校验)
- 是否存在越权问题:
- 垂直越权: 普通用户是否可以进行管理员的操作.
- 水平越权: 普通用户 A 是否能够看到普通用户 B 的信息.
- 是否有强制用户登录操作(Cookie-session/token): 如果用户没有登录, 一些功能是无法使用的.
- 参数是否经过加密(https): 若使用 http, 参数是明文传输的, 别人抓个包就能看到.
- 密码是否有复杂性校验
- 异常验证: 不按照接口文档的要求传入正确的参数, 而是故意传入错误数据. 如: 必传参数就不传/int 传 String/传 null
1.2.2 结合业务逻辑设计
这就需要结合公司具体的业务了.
比如:
一个博客系统, 用户连续 5 次登录失败, 就需要等待 15min, 才能再一次登录.
新用户注册完 5 天后, 才能进行发帖操作.
用户发的帖子抄过 n 个人点赞, 可以获得 x 积分.
2. code
2.1 创建 Python 项目
2.2. 安装 requests 库
安装 requests 库(第三方库, 用于发送 http 请求):
pip install requests==2.31.0
查看是否安装成功: pip list (查看当前所安装库)
2.3 发送 http 请求
安装好 requests 库后, 我们就可以调用 requests.get/requests.post 向后端服务器发送 http GET/POST 请求.
requests.get / requests.post 返回的是一个 Response 对象, 这个 Response 对象包含了响应中的信息:
import requests# 调用 get 方法向百度首页发送 GET 请求.
# requests.get 返回的是一个 Response 对象, 这个对象包含了响应中的信息.
response = requests.get("https://www.baidu.com")# 打印 Response 对象
print(response)
# 打印 URL
print(response.url)
# 打印响应中的文本信息
print(response.text)
2.3.1 Response 对象
通过 requests 发送请求, 会返回一个 Response 对象, 这个 Response 对象中包含了响应中的信息.
常用的 Response 方法如下:
属性/方法 | 描述 |
r.status_code | 响应状态码 |
r.content | 字节方式的响应体, 会自动解码 gzip 和 deflate 压缩 |
r.headers | 以字典对象存储服务器响应头, 若键不存在则返回 None |
r.json() | Requests 中内置的 JSON 解析方法, 将响应体解析为 JSON 格式 |
r.url | 获取实际请求的 URL |
r.encoding | 编码格式, 根据响应头部的字符编码确定 |
r.cookies | 获取服务器设置的 cookies |
r.raw | 返回原始响应体, 不进行任何处理 |
r.text | 字符串方式的响应体, 会自动根据响应头部的字符编码进行解码 |
r.raise_for_status() | 失败请求 (非200响应) 抛出异常 |
需要注意的是:
如果响应的 body 是 Json 格式的, 那么必须使用 r.json() 来打印正文信息.
如果响应的 body 是 HTML 格式的, 那么必须使用 r.text 来打印正文信息.
否则报错:
2.3.2 requests 提供的请求方法
- requests.get(url, params=None, **kwargs)
- requests.post(url, data=None, json=None, **kwargs)
- requests.request(method, url, **kwargs)
其中, requests.get 发送 get 形式的 http 请求. requests.post 发送 post 形式的 http 请求.
而 requests.request 可以发送所有方式的 http 请求, 在 method 传入指定的方式即可.
# GET 请求.
get = requests.get("https://www.baidu.com")
# POST 请求.
post = requests.post("https://www.baidu.com")
# GET 请求, "GET" 大写或小写都可以, 推荐大写.
req_get = requests.request("GET", "https://www.baidu.com")
# POST 请求, "POST" 大写或小写都可以, 推荐大写.
req_post = requests.request("POST", "https://www.baidu.com")
2.3.2.1 请求方法参数
上面说到, 常用的 requests 方法有:
- requests.get(url, params=None, **kwargs)
- requests.post(url, data=None, json=None, **kwargs)
- requests.request(method, url, **kwargs)
其中, **kwargs 表示可以传更多的参数:
这些参数的含义如下:
参数名 | 描述 |
url | 请求的接口 |
headers(请求头) | 一个字典,包含要发送的HTTP头。 |
cookies(请求头) | 一个字典、列表或者 RequestsCookieJar 对象,包含要发送的cookies。 |
files | 一个字典,包含要上传的文件。 |
data(请求参数, post 请求, form 表单格式的参数) | 一个字典、列表或者字节串,包含要发送的请求体数据。 |
json(请求参数, post 请求, json 格式的参数) | 一个字典,将被转换为JSON格式并发送。 |
params(请求参数, get 请求的参数) | 一个字典、列表或者字节串,将作为查询字符串附加到URL上。 |
auth | 一个元组,包含用户名和密码,用于HTTP认证。 |
timeout | 一个浮点数或元组,指定请求的超时时间。 |
proxies | 一个字典,包含代理服务器的信息。 |
verify | 一个布尔值或字符串,指定是否验证SSL证书。 |
注意: 当我们使用 requests 发送一个携带 json/form 参数的 post 请求时(即将一个 Python 字典传递给 post/request 方法的 json/data 参数时), requests 会自动帮我们把请求中的 Content-Type 设置为 application/json 或者 application/x-www-form-urlencoded, 无需我们手动去 header 中设置, 非常方便.
你使用的参数 你的数据格式 requests 自动设置的 Content-Type json={'key': 'value'} JSON 数据 application/json data={'key': 'value'} 表单数据 application/x-www-form-urlencoded files={'file': ...} 文件上传(表单) multipart/form-data
举个例子:
测试博客登录接口:
博客登录接口是一个 post 请求, 那我们必须将登录的参数信息, 传入 request.post/request 方法的 json 形参上:
测试博客详情页接口:
由于 博客详情页接口是 get 请求, 因此参数既可以写在 URL 上, 也可以通过 requests.request 方法传入.
# 请求博客详情页接口.# 1. 方式一: 参数直接写在 url 中. url = 'http://47.93.87.16:8080/blog/getBlogDetail?blogId=31' header = { # 身份验证信息.'user_token': 'eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjJjYjM4MTI5NTYxYTQzOTRiZGI0NTUxYmQyNDIyNjhlNTkyMzRjMzJhODg1OTlkNTFhNDZkNWJmYjc0Nzk3MjkiLCJpZCI6MiwidXNlck5hbWUiOiJsaXNpIn0.3u3ujG1kKR6s7IrFBMSEEkCw-ee5J9R6cdQj7F2KyPE' } res = requests.request(method='GET', url=url, headers=header) print('方式一: ', res.json())# 2. 方式二: 参数通过方法参数传入. url = 'http://47.93.87.16:8080/blog/getBlogDetail' param = {'blogId': 32 } header = { # 身份验证信息.'user_token': 'eyJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6IjJjYjM4MTI5NTYxYTQzOTRiZGI0NTUxYmQyNDIyNjhlNTkyMzRjMzJhODg1OTlkNTFhNDZkNWJmYjc0Nzk3MjkiLCJpZCI6MiwidXNlck5hbWUiOiJsaXNpIn0.3u3ujG1kKR6s7IrFBMSEEkCw-ee5J9R6cdQj7F2KyPE' } res = requests.request(method='GET', url=url, headers=header, params=param) print('方式二: ', res.json())
END