大家好,欢迎来到IT知识分享网。
本文为霍格沃兹测试开发学社学员学习笔记分享
原文链接:接口自动化测试-L1 – 学习笔记 – 测试人社区
一、 接口自动化测试框架介绍
1、 接口测试场景
2、 自动化测试场景
3、 接口自动化测试与 Web/App 自动化测试区别
- 接口关注数据无法触达用户体验。
4、 接口测试工具类型
测试类型 |
工具 |
价值 |
接口抓包 |
Charles、Postman |
接口抓包工具,可以抓取 App 的数据包 |
接口测试 |
Postman |
接口调试工具,接口手工测试工具,学习成本低,直接安装即可使用 |
接口自动化测试 |
Requests、RestAssured |
|
性能测试 |
JMeter |
性能测试工具 |
5、 Requests
- 是由 Python 实现的 API 测试框架。
- 支持发起 POST, GET, PUT, DELETE 等请求。
- 可以用来验证和校对响应信息。
- 官网地址: requests.readthedocs.io/en/latest/
6、 Requests 优势
- 功能全面:HTTP/HTTPS 支持全面。
- 使用简单:简单易用,不用关心底层细节。
- 定制性高:结合测试框架完成二次封装,比如 HttpRunner。
7、 Requests 环境准备
- 安装命令:pip install requests
二、接口请求方法
1、 常见 HTTP 请求方法构造
方法 |
说明 |
requests.request() |
构造一个请求,支撑以下各方法的基础方法。 |
requests.get() |
构造 HTTP 协议中的 GET 请求。 |
requests.post() |
构造 HTTP 协议中的 POST 请求。 |
requests.put() |
构造 HTTP 协议中的 PUT 请求。 |
requests.delete() |
构造 HTTP 协议中的 DELETE 请求。 |
2、 底层设计
3、 HTTP 协议知识
- URL 结构
- HTTP 请求
- HTTP 响应
3、 构造 GET 请求
- requests.get(url, params=None, **kwargs) url: 接口 url。 params:拼接在 url 中的请求参数,非必填,不填默认为None。 **kwargs:更多底层支持的参数。
# 导入依赖import requestsdef test_get(): # 定义接口的 url 和拼接在 url 中的请求参数 url = "https://httpbin.ceshiren.com/get" # 发出 GET 请求,r 接收接口响应 r = requests.get(url) # 打印接口响应 logger.info(f"接口响应为 {r}")
4、 构造 POST 请求
- requests.post(url, data=None, json=None, **kwargs) url: 接口 url。 data:表单格式请求体。 json:JSON 格式请求体。 **kwargs:更多底层支持的参数。
# 导入依赖import requestsdef test_post(): # 定义接口的 url url = "https://httpbin.ceshiren.com/post" # 发出 POST 请求,r 接收接口响应 r = requests.post(url) # 打印接口响应 logger.info(f"接口响应为 {r}")
5、 构造 PUT 请求
- requests.put(url, data=None, **kwargs) url: 接口 url。 data:表单格式请求体。 **kwargs:更多底层支持的参数。
# 导入依赖import requestsdef test_put(): # 定义接口的 url url = "https://httpbin.ceshiren.com/put" # 发出 POST 请求,r 接收接口响应 r = requests.put(url) # 打印接口响应 logger.info(f"接口响应为 {r}")
6、 构造 DELETE 请求
- requests.delete(url, **kwargs) url: 接口 url。 **kwargs:更多底层支持的参数。
# 导入依赖import requestsdef test_delete(): # 定义接口的 url 和表单格式请求体 url = "https://httpbin.ceshiren.com/delete" # 发出 POST 请求,r 接收接口响应 r = requests.delete(url) # 打印接口响应 logger.info(f"接口响应为 {r}")
实战代码
import requests# 发起get请求def test_res_get(): url = "https://httpbin.ceshiren.com/get" # 发起get请求, 并返回一个响应对象 r = requests.get(url) # 把响应对象以json的形式输出 print(r.json())# 发起post请求def test_res_post(): url = "https://httpbin.ceshiren.com/post" # 发起get请求, 并返回一个响应对象 r = requests.post(url) # 打印文本格式的响应信息,如果出现method not allowed,代表请求方法使用错误 print(r.text)# 发起put请求def test_res_put(): url = "https://httpbin.ceshiren.com/put" # 发起get请求, 并返回一个响应对象 r = requests.put(url) # 打印文本格式的响应信息,如果出现method not allowed,代表请求方法使用错误 print(r.text)# 发起delete请求def test_res_delete(): url = "https://httpbin.ceshiren.com/delete" # 发起get请求, 并返回一个响应对象 r = requests.delete(url) # 打印文本格式的响应信息,如果出现method not allowed,代表请求方法使用错误 print(r.text)
7、 构造请求方法
requests.request(method, url, **kwargs)
- method: 请求方法。 GET,OPTIONS,HEAD,POST,PUT,PATCH,DELETE。
- url: 接口 url。
- **kwargs:更多底层支持的参
8、 底层参数说明
参数 |
应用场景 |
method |
请求方法 |
url |
请求 URL |
params |
请求中携带 URL 参数 |
data |
请求中携带请求体(默认为表单请求) |
json |
请求中携带 json 格式的请求体 |
headers |
请求中携带头信息 |
cookies |
请求中携带 cookies |
files |
请求中携带文件格式的请求体 |
auth |
请求中携带认证信息 |
timeout |
设置请求超时时间 |
allow_redirects |
请求是否允许重定向 |
proxies |
设置请求代理 |
verify |
请求是否要认证 |
cert |
请求中携带 ssl 证书 |
三、接口请求参数
1、 请求参数简介
- 接口请求中携带的,会在路径之后使用?代表客户端向服务端传递的参数。
- 使用 key=value 形式拼接在 URL 中。
- 如果有多个,则使用&分隔
- 例如:
2、 携带请求参数的方式
- 常用两种方式: 直接在 URL 中拼接:?username=Hogwarts&id=666。 通过 params 参数传递:requests.get(url, params)
3、 携带请求参数的 GET 请求
import requests# 发起get请求def test_get_url(): # url中携带参数信息 url = "https://httpbin.ceshiren.com/get?name=ad&age=18" r = requests.get(url) print(r.text)def test_get_params(): url = "https://httpbin.ceshiren.com/get" # 定义一个字典格式的变量信息 req_params = {"name": "ad", "age": 18} # 通过params进行传参 r = requests.get(url, params=req_params) print(r.text)
4、 携带请求参数的 POST 请求
import requests# ===其他类型的请求也可以携带url参数信息# 发起get请求def test_post_url(): # url中携带参数信息 url = "https://httpbin.ceshiren.com/post?name=ad1&age=19" r = requests.post(url) print(r.text)def test_post_params(): # url不携带参数信息 url = "https://httpbin.ceshiren.com/post" # 定义一个字典格式的变量信息 req_params = {"name": "ad1", "age": 19} # 通过params进行传参 r = requests.post(url, params=req_params) print(r.text)
四、接口请求头
1、 请求头信息的使用场景
- 身份认证
- 指定数据类型
例如:飞书接口文档
2、 请求头信息
- HTTP 请求头是在 HTTP 请求消息中包含的元数据信息,用于描述请求或响应的一些属性和特征。
- 实际工作过程中具体要关注的头信息字段需要和研发沟通。
- 常见的头信息(下面表格):
内容 |
含义 |
Authorization |
表示客户端请求的身份验证信息 |
Cookie |
表示客户端的状态信息,通常用于身份验证和会话管理 |
Content-Type |
表示请求消息体的 MIME 类型 |
User-Agent |
发送请求的客户端软件信息 |
3、 构造头信息
- 使用 headers 参数传入。
- 通常使用字典格式。
headers = {'user-agent': 'my-app/0.0.1'}
r = requests.get(url, headers=headers)
import requests
def test_res_get():
url = "https://httpbin.ceshiren.com/get"
# 通过字典格式定义一个头信息
header = {"name": "ad", "User-Agent": "apple", "Content-type": "application/json"}
r = requests.get(url, headers=header)
print(r.text)
五、接口请求体-json
1、 接口请求体简介
- 进行HTTP请求时,发送给服务器的数据。
- 数据格式类型可以是JSON、XML、文本、图像等格式。
- 请求体的格式和内容取决于服务器端API的设计和开发人员的要求。
- 例如:飞书接口文档
2、常用接口请求体
类型 |
介绍 |
Content-type |
JSON(JavaScript Object Notation) |
轻量级的数据交换格式,最常见的一种类型。 |
application/json |
表单数据(Form Data) |
以键值对的形式提交数据,例如通过 HTML 表单提交数据。 |
application/x-www-form-urlencoded |
XML(eXtensible Markup Language) |
常用的标记语言,通常用于传递配置文件等数据。 |
application/xml |
text/xml |
||
文件(File) |
可以通过请求体上传文件数据,例如上传图片、视频等文件。 |
上传文件的 MIME 类型,例如 image/jpeg |
multipart/form-data |
||
纯文本(Text) |
纯文本数据,例如发送邮件、发送短信等场景 |
text/plain |
其他格式 |
二进制数据、protobuf 等格式 |
3、 JSON 简介
- JavaScript Object Notation 的缩写。
- 是一种轻量级的数据交换格式。
- 是理想的接口数据交换语言。
- Content-Type 为 application/json。
4、构造 JSON 格式请求体
- 定义为字典格式。
- 使用 json 参数传入。
import requestsdef test_body_json(): # httpbin.ceshiren.com是请求什么返回什么 url = "https://httpbin.ceshiren.com/post" # 定义变量,存放请求体 req_body = { "name": "holy", "age": 19 } # 通过json关键字传递请求体信息 r = requests.post(url, json=req_body) print(r.text)
六、接口响应断言
1、 接口断言使用场景
- 问题: 如何确保请求可以发送成功。 如何保证符合业务需求。
- 解决方案: 通过获取响应信息,验证接口请求是否成功,是否符合业务需求。
2、 Requests 中的响应结果对象
import requestsfrom requests import Response# Response就是一个响应对象r: Response = requests.get('http://www.example.com')
3、 响应结果类型
属性 |
含义 |
r |
响应 Response 对象(可以使用任意的变量名) |
r.status_code |
HTTP 响应状态码 |
r.headers |
返回一个字典,包含响应头的所有信息。 |
r.text |
返回响应的内容,是一个字符串。 |
r.url |
编码之后的请求的 url |
r.content |
返回响应的内容,是一个字节流。 |
r.raw |
响应的原始内容 |
r.json() |
如果响应的内容是 JSON 格式,可以使用该方法将其解析成 Python 对象。 |
4、 响应状态码断言
- 基础断言: r.status_code
import requests
def test_res():
url = "https://httpbin.ceshiren.com/get"
# 返回值为一个Response对象
r = requests.get(url)
# 断言,确定响应状态码为200
assert r.status_code == 200
# 断言url
assert r.url == "https://httpbin.ceshiren.com/get"
# 打印响应状态码信息
print(r.status_code)
print("-----------")
# 打印响应头信息
print(r.headers)
print("-----------")
# 打印响应体信息
print(r.text)
print("-----------")
# 打印编码之后的请求的 url
print(r.url)
七、json响应体断言
1、 JSON 响应体
- JSON格式的响应体指的是HTTP响应中的消息体(message body),它是以JSON格式编码的数据。
{
"name": "John",
"age": 30,
"city": "New York"
}
2、断言 JSON 格式响应体使用场景
- 验证API接口的返回结果是否符合预期。 业务场景上是否符合预期。 格式是否符合文档规范。
3、 断言 JSON 格式响应体
- r.json():返回 python 字典。
4、 若碰到复杂断言应该如何处理?
- 多层嵌套的数据提取与断言: JSONPath
- 整体结构响应断言: JSONSchema
- 自行编写解析算法
import requestsdef test_res_json(): url = "https://httpbin.ceshiren.com/get" # 如果响应体是非json的场景,就不能使用r.json()的方式,否则会报raise RequestsJSONDecodeError异常,可以使用r.text r = requests.get(url) print(r.json()) # 断言响应状态码。只能判断请求是否成功发送 assert r.status_code == 200 # r.json()将json格式转换为字典,断言外层的某个字段数据,和业务场景相关 assert r.json()["url"] == url # 断言多层的场景 assert r.json()["headers"]["Host"] == "httpbin.ceshiren.com"
宠物商店接口自动化测试实战
实战思路
自动化测试脚本思路
"""
完成宠物商城宠物查询功能接口自动化测试。
编写自动化测试脚本。
完成断言。
"""
import requests
class TestPetStoreSearch:
"""
宠物商店查询单接口测试
"""
def setup_class(self):
# 定义请求url
self.base_url = "https://petstore.swagger.io/v2/pet"
# 拼接宠物查询接口url
self.search_url = self.base_url + "/findByStatus"
def test_search_pet(self):
# 宠物查询接口请求参数
pet_status = {
"status": "available"
}
# 发出请求
r = requests.get(self.search_url, params=pet_status)
# 打印接口响应信息
print(r.text)
# 状态断言
assert r.status_code == 200
# 业务断言,r.json()转换为python对象,不是空列表
assert r.json() != []
# 响应返回的是一个数组,数组中每一个对象都应该包含id字段
assert "id" in r.json()[0]
日志配置:log_utils.py
# 配置日志
import logging
import os
from logging.handlers import RotatingFileHandler
# 绑定绑定句柄到logger对象
logger = logging.getLogger(__name__)
# 获取当前工具文件所在的路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 拼接当前要输出日志的路径
log_dir_path = os.sep.join([root_path, '..', f'/logs'])
if not os.path.isdir(log_dir_path):
os.mkdir(log_dir_path)
# 创建日志记录器,指明日志保存路径,每个日志的大小,保存日志的上限
file_log_handler = RotatingFileHandler(os.sep.join([log_dir_path, 'log.log']), maxBytes=1024 * 1024, backupCount=10)
# 设置日志的格式
date_string = '%Y-%m-%d %H:%M:%S'
formatter = logging.Formatter(
'[%(asctime)s] [%(levelname)s] [%(filename)s]/[line: %(lineno)d]/[%(funcName)s] %(message)s ', date_string)
# 日志输出到控制台的句柄
stream_handler = logging.StreamHandler()
# 将日志记录器指定日志的格式
file_log_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
# 为全局的日志工具对象添加日志记录器
# 绑定绑定句柄到logger对象
logger.addHandler(stream_handler)
logger.addHandler(file_log_handler)
# 设置日志输出级别
logger.setLevel(level=logging.INFO)
代码优化
- 新建日志配置。
- 在用例中使用配置好的日志实例。
- 使用 pytest parametrize 装饰器实现宠物状态的参数化。
- 测试报告添加标题
"""
完成宠物商城宠物查询功能接口自动化测试。
编写自动化测试脚本。
完成断言。
"""
import allure
import pytest
import requests
from interface.utils.log_utils import logger
@allure.feature("宠物查询接口")
class TestPetStoreSearch:
"""
宠物商店查询单接口测试
"""
def setup_class(self):
# 定义请求url
self.base_url = "https://petstore.swagger.io/v2/pet"
# 拼接宠物查询接口url
self.search_url = self.base_url + "/findByStatus"
# 正常场景用例
# 参数化,第一个为需要传的参数,第二个为传的值,可以使用列表或元组,ids给用例命名
@pytest.mark.parametrize(
"status",
["available", "pending", "sold"],
ids=["available_pets", "pending_pets", "sold_pets"]
)
@allure.story("宠物查询接口冒烟用例")
def test_search_pet(self, status):
# 宠物查询接口请求参数
pet_status = {
"status": status
}
# 发出请求
r = requests.get(self.search_url, params=pet_status)
# 添加日志,打印接口响应信息
logger.info(r.text)
# 状态断言
assert r.status_code == 200
# 业务断言,r.json()转换为python对象,不是空列表
assert r.json() != []
# 响应返回的是一个数组,数组中每一个对象都应该包含id字段
assert "id" in r.json()[0]
# 异常场景,参数化
@pytest.mark.parametrize(
"status",
["petstatus", 123456, ""],
ids=["wrong_value", "number", "none_str"]
)
@allure.story("status传入错误的值")
def test_search_abnormal(self, status):
"""
宠物查询接口异常场景
:return:
"""
# 宠物查询接口请求参数
pet_status = {
"status": status
}
# 发出请求
r = requests.get(self.search_url, params=pet_status)
# 添加日志,打印接口响应信息
logger.info(r.text)
# 状态断言
assert r.status_code == 200
# 业务断言,r.json()转换为python对象,不是空列表
assert r.json() == []
# 不传参
@allure.story("不传status")
def test_search_none_params(self):
# 发出请求
r = requests.get(self.search_url)
# 添加日志,打印接口响应信息
logger.info(r.text)
# 状态断言
assert r.status_code == 200
# 业务断言,r.json()转换为python对象,不是空列表
assert r.json() == []
# 传非status的参数
@allure.story("传入非status参数")
def test_search_wrong_params(self):
pet_status = {
"key": "available "
}
# 发出请求
r = requests.get(self.search_url, params=pet_status)
# 添加日志,打印接口响应信息
logger.info(r.text)
# 状态断言
assert r.status_code == 200
# 业务断言,r.json()转换为python对象,不是空列表
assert r.json() == []
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/55742.html