目录:
import requests
# key值需要替换为文件名
# value 对应为文件二进制流
def test_file():
r = requests.post("https://httpbin.ceshiren.com/post",
# files={"request_file": open("./datas/1.txt", "rb")},
files={"request_file": ("hello.txt", open("./datas/1.txt", "rb"))},
verify=False
)
print(r.text)
抓包看请求体:(学会使用charles或者fiddler抓包,看请求和响应)
import requests
def test_form():
data = {"hello": "form"}
r = requests.post("https://httpbin.ceshiren.com/post", verify=False, data=data)
print(r.text)
def test_json():
data = {"hello": "form"}
r = requests.post("https://httpbin.ceshiren.com/post", verify=False, json=data)
print(r.text)
import requests
import xml.etree.ElementTree as ET
def test_xml():
# 读取XML文件
tree = ET.parse('./datas/test.xml')
root = tree.getroot()
# 创建一个空列表来保存数据
datas = []
# 遍历XML文件的每个元素
for elem in root.iter():
# 将元素的标签和文本内容添加到列表中
datas.append((elem.tag, elem.text))
data = datas
r = requests.post("https://httpbin.ceshiren.com/post", verify=False, data=data)
print(r.text)
运行抓包:
from requests_xml import XMLSession
def test_xml():
session = XMLSession()
r = session.get('https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss')
print(r.text)
print(r.xml.links)
print(r.raw_xml)
print(r.xml.text)
from requests_xml import XMLSession
session = XMLSession()
r = session.get('https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss')
r.xml.links
item = r.xml.xpath('//item', first=True)
print(item.text)
from requests_xml import XMLSession
import xml.etree.ElementTree as ET
def test_xml():
session = XMLSession()
r = session.get('https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss')
print(r.text)
print(r.xml.links)
print(r.xml.raw_xml)
print(r.xml.text)
def test_xpath_assert():
session = XMLSession()
r = session.get('https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss')
item = r.xml.xpath('//link')
result = []
for i in item:
print(i.text)
result.append(i.text)
print(result)
assert "https://www.nasa.gov/image-of-the-day/" in result
import xml.etree.ElementTree as ET
root = ET.fromstring(countrydata)
root.findall(".")
root.findall("./country/neighbor")
root.findall(".//year/..[@name='Singapore']")
root.findall(".//*[@name='Singapore']/year")
root.findall(".//neighbor[2]")
from requests_xml import XMLSession
import xml.etree.ElementTree as ET
def test_xml():
session = XMLSession()
r = session.get('https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss')
print(r.text)
print(r.xml.links)
print(r.xml.raw_xml)
print(r.xml.text)
def test_xpath_assert():
session = XMLSession()
r = session.get('https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss')
item = r.xml.xpath('//link')
result = []
for i in item:
print(i.text)
result.append(i.text)
print(result)
assert "https://www.nasa.gov/image-of-the-day/" in result
def test_xml_assert():
session = XMLSession()
r = session.get('https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss')
root = ET.fromstring(r.text)
item = root.findall(".")
print(item)
items = root.findall(".//link")
print(items)
for i in items:
print(i.text)
Cookie使用场景,在接口测试过程中,很多情况下,需要发送的请求附带cookies,才会得到正常的响应的结果。所以使用python+requests进行接口自动化测试也是同理,需要在构造接口测试用例时加入cookie。传递Cookie的两种方式
import requests
def test_cookie1():
url = "https://httpbin.testing-studio.com/cookies"
header = {"Cookie": "LiMing=teacher", "User-Agent": "hdc"}
r = requests.get(url=url, headers=header, verify=False)
print(r.request.headers)
def test_cookie2():
url = "https://httpbin.testing-studio.com/cookies"
header = {"User-Agent": "hdc"}
cookie_data = {"today": "20231013"}
r = requests.get(url=url, headers=header, verify=False, cookies=cookie_data)
print(r.request.headers)
不设置请求超时:
设置请求超时:
import requests
class TestReq:
def test_timeout(self):
r = requests.get('http://github.com', timeout = 0.01)
代码示例:
import requests
class TestReq:
def test_timeout(self):
r = requests.get('http://github.com', timeout=0.01)
# r = requests.get('http://github.com', timeout=5000)
通过 proxies 参数,监听请求与响应信息
import requests
def test_proxy():
proxy = {
"http": "http://127.0.0.1:6666",
"https": "http://127.0.0.1:6666"
}
url = "https://httpbin.testing-studio.com/post"
r = requests.post(url=url, proxies=proxy, verify=False)
print(r.text)
// - 层级多。
// - 嵌套关系复杂。
{
"errcode": 0,
"errmsg": "ok",
"userid": "zhangsan",
"name": "张三",
"department": [1, 2],
"order": [1, 2],
"position": "后台工程师",
"mobile": "13800000000",
"gender": "1",
"email": "[email protected]",
"biz_mail": "[email protected]",
"is_leader_in_dept": [1, 0],
"direct_leader": ["lisi", "wangwu"],
"avatar": "http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0",
"thumb_avatar": "http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/100",
"telephone": "020-123456",
"alias": "jackzhang",
"address": "广州市海珠区新港中路",
"open_userid": "xxxxxx",
"main_department": 1,
"extattr": {
"attrs": [
{
"type": 0,
"name": "文本名称",
"text": {
"value": "文本"
}
},
{
"type": 1,
"name": "网页名称",
"web": {
"url": "http://www.test.com",
"title": "标题"
}
}
]
},
"status": 1,
"qr_code": "https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=xxx",
"external_position": "产品经理",
"external_profile": {
"external_corp_name": "企业简称",
"wechat_channels": {
"nickname": "视频号名称",
"status": 1
},
"external_attr": [
{
"type": 0,
"name": "文本名称",
"text": {
"value": "文本"
}
},
{
"type": 1,
"name": "网页名称",
"web": {
"url": "http://www.test.com",
"title": "标题"
}
},
{
"type": 2,
"name": "测试app",
"miniprogram": {
"appid": "wx8bd80126147dFAKE",
"pagepath": "/index",
"title": "my miniprogram"
}
}
]
}
}
场景 | 方式 |
---|---|
提取 errcode 对应的值 | res["errcode"] |
提取 title 对应的值 | res["extattr"]["external_profile"]["external_attr"][1]["web"]["title"] |
提取 type 为 0 的 name | 编码实现 |
提取 attrs 下的所有的 name | 编码实现 |
场景 | 对应实现 | JSONPath 实现 |
---|---|---|
提取 errcode 对应的值 | res["errcode"] |
$.errcode |
提取 title 对应的值 | res["extattr"]["external_profile"]["external_attr"][1]["web"]["title"] 等 |
$..title |
提取 type 为 0 的 name | 编码实现 | $..external_attr[?(@.type==0)].name |
提取 attrs 下的所有的 name | 编码实现 | $..attrs..name |
符号 | 描述 |
---|---|
$ | 查询的根节点对象,用于表示一个 json 数据,可以是数组或对象 |
@ | 过滤器(filter predicate)处理的当前节点对象 |
* |
通配符 |
. | 获取子节点 |
.. |
递归搜索,筛选所有符合条件的节点 |
?() | 过滤器表达式,筛选操作 |
[start:end] | 数组片段,区间为[start,end),不包含 end |
[A]或[A,B] | 迭代器下标,表示一个或多个数组下标 |
https://jsonpath.hogwarts.ceshiren.com/
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
需求 | JsonPath |
---|---|
所有书籍的作者 | $.store.book[*].author |
所有作者 | $..author |
store 下面的所有内容 | $.store. |
所有的价格 | $.store..price |
第三本书 | $..book[2] |
所有包含 isbn 的书籍 | $..book[?(@.isbn)] |
所有价格小于 10 的书 | $.store.book[?(@.price < 10)] |
所有书籍的数量 | $..book.length |
pip install jsonpath
# 具体的使用。
jsonpath.jsonpath(源数据对象, jsonpath表达式)
代码示例:
import jsonpath
import requests
def test_jsonpath():
url = "https://httpbin.ceshiren.com/post"
req_body = {"teacher": "ad", "school": "hdc"}
r = requests.post(url, json=req_body, verify=False)
print(r.json()["headers"]["Content-Type"])
res = jsonpath.jsonpath(r.json(), "$..Content-Type")
print(res)
assert res[0] == r.json()["headers"]["Content-Type"]
形式 | 章节 | 描述 | |
---|---|---|---|
知识点 | 代理配置 | 利用代理分析测试脚本,排查请求错误 | |
知识点 | 多层嵌套响应断言 | 利用 jsonpath 进行多层嵌套的响应断言 |
import requests
class TestPetstorePetmanager:
def setup_class(self):
self.base_url = "https://petstore.swagger.io/v2/pet"
self.search_url = self.base_url + "/findByStatus"
self.pet_id = 9223372000001084222
self.delete_url = self.base_url + f'/{self.pet_id}'
self.pet_status = "available"
self.add_pet_info = {
"id": self.pet_id,
"category": {
"id": 1,
"name": "cat"
},
"name": "miao",
"photoUrls": [
"string"
],
"tags": [
{
"id": 5,
"name": "cute"
}
],
"status": self.pet_status
}
self.update_name = "miao-hdc"
self.update_pet_info = {
"id": self.pet_id,
"category": {
"id": 1,
"name": "cat"
},
"name": self.update_name,
"photoUrls": [
"string"
],
"tags": [
{
"id": 5,
"name": "cute"
}
],
"status": self.pet_status
}
self.search_param = {
"status": self.pet_status
}
def test_pet_manager(self):
# 新增宠物接口
add_r = requests.post(self.base_url, json=self.add_pet_info, verify=False)
# 查询宠物
search_r = requests.get(self.search_url, params=self.search_param, verify=False)
# 修改宠物
update_r = requests.put(self.base_url, json=self.update_pet_info, verify=False)
# 删除宠物
delete_r = requests.delete(self.delete_url, verify=False)
proxy = {
"http": "http://127.0.0.1:8888",
"https": "http://127.0.0.1:8888"
}
requests.post(url, json=pet_info, proxies=proxy, verify=False)
import logging
import os
from logging.handlers import RotatingFileHandler
# 绑定绑定句柄到logger对象
logger = logging.getLogger(__name__)
# 获取当前工具文件所在的路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 拼接当前要输出日志的路径
root_len = len(root_path)
strs = root_path[0:root_len-6]
log_dir_path = os.sep.join([strs,f'datas\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,
encoding="utf-8")
# 设置日志的格式
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)
def prit_path():
print(root_path)
print(log_dir_path)
prit_path()
jsonpath.jsonpath(r.json(), "$..id")
import jsonpath
import requests
from interface_automation_testing.接口自动化测试_L2.宠物商店接口自动化测试实战.utils.log_util import logger
class TestPetstorePetmanager:
def setup_class(self):
self.base_url = "https://petstore.swagger.io/v2/pet"
self.search_url = self.base_url + "/findByStatus"
self.pet_id = 9223372000001084222
self.delete_url = self.base_url + f'/{self.pet_id}'
self.pet_status = "available"
self.add_pet_info = {
"id": self.pet_id,
"category": {
"id": 1,
"name": "cat"
},
"name": "miao",
"photoUrls": [
"string"
],
"tags": [
{
"id": 5,
"name": "cute"
}
],
"status": self.pet_status
}
self.update_name = "miao-hdc"
self.update_pet_info = {
"id": self.pet_id,
"category": {
"id": 1,
"name": "cat"
},
"name": self.update_name,
"photoUrls": [
"string"
],
"tags": [
{
"id": 5,
"name": "cute"
}
],
"status": self.pet_status
}
self.search_param = {
"status": self.pet_status
}
def test_pet_manager(self):
# 新增宠物接口
add_r = requests.post(self.base_url, json=self.add_pet_info, verify=False)
logger.info(f"新增宠物接口响应为:{add_r.text}")
assert add_r.status_code == 200
# 查询宠物
search_r = requests.get(self.search_url, params=self.search_param, verify=False)
logger.info(f"查询宠物接口的响应为:{search_r.text}")
assert search_r.status_code == 200
assert self.pet_id in jsonpath.jsonpath(search_r.json(), "$..id")
# 修改宠物
update_r = requests.put(self.base_url, json=self.update_pet_info, verify=False)
logger.info(f"修改宠物接口的响应为:{update_r.text}")
assert update_r.status_code == 200
search_r = requests.get(self.search_url, params=self.search_param, verify=False)
assert search_r.status_code == 200
assert self.update_name in jsonpath.jsonpath(search_r.json(), "$..name")
# 删除宠物
delete_r = requests.delete(self.delete_url, verify=False)
logger.info(f"删除宠物接口的响应为:{delete_r.text}")
assert delete_r.status_code == 200
search_r = requests.get(self.search_url, params=self.search_param, verify=False)
assert search_r.status_code == 200
assert self.pet_id not in jsonpath.jsonpath(search_r.json(), "$..id")
# 生成报告信息
pytest --alluredir=./report
# 生成报告在线服务,查看报告
allure serve ./report/
pytest -vs .\test_petstore.py --alluredir=./datas/report --clean-alluredir
allure serve ./datas/report
运行结果:
https://gitee.com/coderPatrickStar/template/tree/master/python_software_testing