一、接口响应断言
(一)接口断言使用场景
- 问题:
- 如何确保请求可以发送成功。
- 如何保证符合业务需求。
- 解决方案:
- 通过获取响应信息,验证接口请求是否成功,是否符合业务需求。
Requests 中的响应结果对象
import requests
from requests import Response
# Response就是一个响应对象
r: Response = requests.get('http://www.example.com')
响应结果类型
属性 | 含义 |
---|---|
r |
响应 Response 对象(可以使用任意的变量名) |
r.status_code |
HTTP 响应状态码 |
r.headers |
返回一个字典,包含响应头的所有信息。 |
r.text |
返回响应的内容,是一个字符串。 |
r.url |
编码之后的请求的 url |
r.content |
返回响应的内容,是一个字节流。 |
r.raw |
响应的原始内容 |
r.json() |
如果响应的内容是 JSON 格式,可以使用该方法将其解析成 Python 对象。 |
# 导入依赖
import requests
def test_res_assert():
# 定义接口的 url 和 json 格式请求体
url = "https://httpbin.ceshiren.com/get"
# 发出 GET 请求,r 接收接口响应
r = requests.post(url)
响应状态码断言
- 基础断言:
r.status_code
import requests
def test_req():
r = requests.get("https://httpbin.ceshiren.com/get")
assert r.status_code == 200
二、JSON 响应体断言
1、什么是 JSON 响应体
- JSON格式的响应体指的是HTTP响应中的消息体(message body),它是以JSON格式编码的数据。
{
"name": "John",
"age": 30,
"city": "New York"
}
2、断言 JSON 格式响应体使用场景
- 验证API接口的返回结果是否符合预期。
- 业务场景上是否符合预期。
-
格式是否符合文档规范。
3、断言 JSON 格式响应体
-
r.json()
:返回 python 字典。
import requests
def test_res_json():
r = requests.get("https://httpbin.ceshiren.com/get")
assert r.status_code == 200
assert r.json()["url"] == "https://httpbin.ceshiren.com/get"
若碰到复杂断言应该如何处理?
- 多层嵌套的数据提取与断言: JSONPath
- 整体结构响应断言: JSONSchema
- 自行编写解析算法
三、整体结构响应断言
1、响应信息数据极为庞大
2、针对于“大响应数据”如何断言
- 针对主要且少量的业务字段断言。
- 其他字段不做数据正确性断言,只做类型与整体结构的校验。
- 与前面的版本进行 diff,对比差异化的地方。
3、JSONSchema 简介
- 使用 JSON 格式编写的
- 可以用来定义校验 JSON 数据的结构
- 可以用来校验 JSON 数据的一致性
- 可以用来校验 API 接口请求和响应
4、JSONSchema 整体结构响应断言
- 预先生成对应结构的 Schema。
- 将实际获取到的响应与生成的 Schema 进行对比。
5、JSONSchema 的生成
- 通过界面工具生成。
- 通过第三方库生成。
- 通过命令行工具生成。
6、JSONSchema 的生成效果
// # 预期的 JSON 文档结构
{
"name": "Hogwarts",
"Courses": ["Mock", "Docker"]
}
// jsonschema
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$ref": "#/definitions/Welcome",
"definitions": {
"Welcome": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"Courses": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["Courses", "name"],
"title": "Welcome"
}
}
}
7、界面工具生成
- 复制 JSON 数据
- 粘贴到在线生成工具中
- 自动生成 JSON Schema 数据
JSON Schema 在线生成工具:https://app.quicktype.io
8、第三方库生成(Python)
- 安装:
pip install genson
。 - 调用方法生成对应的 JSONSchema 数据结构。
from genson import SchemaBuilder
def generate_jsonschema(obj):
# 实例化jsonschem
builder = SchemaBuilder()
# 传入被转换的对象
builder.add_object(obj)
# 转换成 schema 数据
return builder.to_schema()
9、JSONSchema 验证(Python)
- 安装:
pip install jsonschema
。 - 调用
validate()
进行验证。
def schema_validate(obj, schema):
'''
对比 python 对象与生成的 JSONSchame 的结构是否一致
'''
try:
validate(instance=obj, schema=schema)
return True
except Exception as e:
return False
10、JSONSchema 二次封装
- 生成JSONSchema
- 验证JSONSchema
class JSONSchemaUtils:
@classmethod
def generate_schema(cls, obj):
# 实例化jsonschem
builder = SchemaBuilder()
# 传入被转换的对象
builder.add_object(obj)
# 转换成 schema 数据
return builder.to_schema()
@classmethod
def schema_validate(cls, obj, schema):
'''
对比 python 对象与生成的 json schame 的结构是否一致
'''
try:
validate(instance=obj, schema=schema)
return True
except Exception as e:
return False
四、数据库操作与断言
1、接口测试响应验证
如何在测试过程中验证接口没有 Bug?
- 通过接口响应值
- 通过查询数据库信息辅助验证
2、接口测试数据清理
自动化测试过程中,会产生大量的脏数据,如何处理?
- 通过 Delete 接口删除
- 自动化测试使用干净的测试环境,每次自动化测试执行完成之前或之后做数据还原。
3、数据库操作注意事项
直接对数据库做查询之外的操作是非常危险的行为
- 权限管理严格的公司数据库权限给的非常低
- 表结构复杂,随便删除数据会影响测试,甚至会导致系统出现异常
4、接口自动化测试常用的数据库操作
- 连接与配置
- 查询数据与断言
Python技术栈:章节《常用第三方库pymsql》;Java技术栈:《常用标准库:数据库操作-JDBC》
实战目标
- 第一次全流程实战的断言通过数据库验证
数据库信息
- 主机: litemall.hogwarts.ceshiren.com
- 端口: 13306
- 用户名: test
- 密码: test123456
注意:只有查询权限
数据库封装(Python)
- 封装数据库配置
- 封装 sql 查询操作
- 调用方法执行 sql 语句
import pymysql
# 封装建立连接的对象
def get_conn():
conn = pymysql.connect(
host="litemall.hogwarts.ceshiren.com",
port=13306,
user="test",
password="test123456",
database="litemall",
charset="utf8mb4"
)
return conn
# 执行sql语句
def execute_sql(sql):
connect = get_conn()
cursor = connect.cursor()
cursor.execute(sql) # 执行SQL
record = cursor.fetchone() # 查询记录
return record
if __name__ == '__main__':
# 执行sql语句查询user123这个用户的购物车有一个名称为 hogwarts1 的商品
execute_sql("select * from litemall_cart where "
"user_id=1 and deleted=0 and "
"goods_name='hogwarts1'")
查询数据与数据库断言(Python)
- 查询数据,添加查询条件
- 断言结果不为 None
# 查询查询user123这个用户的购物车有一个名称为 hogwarts1 的商品
sql_res = execute_sql("select * from litemall_cart where "
"user_id=1 and deleted=0 and "
"goods_name='hogwarts1'")
assert sql_res
五、接口鉴权的多种情况与解决方案
1、接口鉴权是什么
-
身份认证
接口鉴权通用的解决方案
- 认证信息的获取
- 认证信息的携带
@startuml
scale 800
if (登录成功?) then
#pink:响应错误;
detach
endif
#palegreen:响应认证信息;
#palegreen:携带认证信息发起其他请求;
@enduml
后端接口鉴权常用方法
@startmindmap
* 常用方式
** cookie
*** 1\. 携带身份信息请求认证
*** 2\. 之后的每次请求都携带cookie信息,cookie记录在请求头中
** token
*** 1\. 携带身份信息请求认证
*** 2\. 之后的每次请求都携带token认证信息
*** 3\. 可能记录在请求头,可能记录在url参数中
** auth
*** 每次请求携带用户的username和password,并对其信息加密
** oauth2(选修)
*** 1\. 携带身份信息请求认证
*** 2\. 服务端向指定回调地址回传code
*** 3\. 通过code获取token
*** 4\. 之后的请求信息都携带token。
*** 典型产品 微信自动化测试
@endmindmap
cookie 鉴权
- cookie 的获取(根据接口文档获取)
- 发送携带 cookie 的请求
- 直接通过 cookies 参数
- 通过
Session()
对象
import requests
class TestVerify:
def setup_class(self):
self.proxy = {"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"}
def test_cookies_by_write(self):
# 简单场景,直接写入cookie
url = "https://httpbin.ceshiren.com/cookies"
requests.get(url, proxies=self.proxy, verify=False, cookies={"hogwarts": "ad"})
def test_cookies(self):
# 获取session 的实例,需要通过Session()保持会话,
# 即为认证之后,之后所有的实例都会携带cookie
# 可以模仿用户在浏览器的操作
req = requests.Session()
# 第一次登陆,植入cookie
set_url = "https://httpbin.ceshiren.com/cookies/set/hogwarts/ad"
req.get(set_url, proxies=self.proxy, verify=False)
# 第二次请求的时候即可携带cookie信息
url = "https://httpbin.ceshiren.com/cookies"
req.get(url, proxies=self.proxy, verify=False)
token 鉴权
- token 的获取(根据接口文档获取)
- 发送携带 token 的请求(根据接口文档获取)
class TestVerify:
def setup_class(self):
self.proxy = {"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"}
def test_token(self):
# 1\. 获取token
url = "http://litemall.hogwarts.ceshiren.com/admin/auth/login"
user_data = {"username": "admin123", "password": "admin123", "code": ""}
r = requests.post(url, json=user_data, proxies=self.proxy, verify=False, )
self.token = r.json()["data"]["token"]
# 2\. 之后的请求均携带token
goods_list_url = "http://litemall.hogwarts.ceshiren.com/admin/goods/list"
goods_data = {"name": "hogwarts", "order": "desc", "sort": "add_time"}
r = requests.get(goods_list_url, params=goods_data,
headers={"X-Litemall-Admin-Token": self.token},
proxies=self.proxy, verify=False)
auth 鉴权(了解即可)
- 在基本 HTTP 身份验证中,请求包含格式为 的标头字段Authorization: Basic<credentials style="box-sizing: inherit;"></credentials>
- 其中credentials是 ID 和密码的Base64编码,由单个冒号连接:。
[图片上传失败...(image-7c678-1694090124455)]
auth 鉴权-代码示例
import requests
from requests.auth import HTTPBasicAuth
class TestVerify:
def setup_class(self):
self.proxy = {"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"}
def test_basic_auth(self):
# 表示访问一个需要BasicAuth认证的路径
# username=用户名,password=密码
# 如果不使用basic auth 则会失败
r = requests.get("https://httpbin.ceshiren.com/basic-auth/username/password",
proxies=self.proxy, verify=False,
auth=HTTPBasicAuth("username", "password"))