jsonpath是使用一种简单的方法提取给定的json文档的部分内容,我们做接口测试时,目前主要流行的数据结构是json,遇到复杂的json格式,使用jsonpath提取数据
操作 | 说明 |
---|---|
$ | 查询根元素, |
@ | 当前接口由过滤词处理 |
* | 通配符, |
. . | 深度扫描 |
. | 表示子节点 |
[“(,”)] | 括号表示子项 |
[(,)] | 数组索引或者索引 |
[start:end] | 数组切片 |
[?()] | 过滤表达式,表达式要求值为一个布尔值 |
2、过滤器运算符
过滤器是筛选数组的逻辑表达式,一个典型的过滤器是[[email protected]>18)],其中@正在处理的当前节点,可以使用逻辑运算符&&和||创建更复杂的过滤器,字符串文字必须用单引号或者双引号括起来,([email protected]==‘blue’)]或者[[email protected]==‘blue’)])
在线调试jsonpath:http://www.e123456.com/aaaphp/online/jsonpath/?
token = jsonpath.jsonpath(res.json(),'$.data')[0]
#1、第一个参数表示要解析的对象
#2、第二个参数你要提取的数据对应的jsonpath表达式
#3、如果表达式能够取到值,不管是1个还是多个,返回的都是列表,所以我们在后面加了[0]
#4、如果表达式匹配不到数据,那么它返回的值是False
按照一定的维度进行分类,每个分类可以当作一个sheet工作表
变量名称 | 变量值 |
---|---|
host | http://82.156.74.26:9099 |
username | 18866668888 |
password | 123456 |
data类型:
{
"data":{
"xxx":"xxxxxxx"
}
}
params类型
{
"params":{
"xxx":"xxxxxxx"
}
}
json类型:
{
"json":{
"xxx":"xxxxxxx"
}
}
混合参数(既有data,也有params,有json
{
"data":{
"xxx":"xxxxxxx"
}
"json":{
"xxx":"xxxxxxx"
}
"params":{
"xxx":"xxxxxxx"
}
}
接口名称 | 默认参数 |
---|---|
登录 | { “data”: { “username”: “ u s e r n a m e " , " p a s s w o r d " : " {username}", "password": " username","password":"{password}” }} |
新增客户 | { “json”: { “entity”: { “customer_name”: “沙陌001”, “mobile”: “18729399607”, “telephone”: “01028375678”, “website”: “http://mtongxue.com/”, “next_time”: “2022-05-12 00:00:00”, “remark”: “这是备注”, “address”: “北京市,北京城区,昌平区”, “detailAddress”: “霍营地铁口”, “location”: “”, “lng”: “”, “lat”: “” } }} |
新建联系人 | { “json”: { “entity”: { “name”: “沙陌001联系人”, “customer_id”: “${customerId}”, “mobile”: “18729399607”, “telephone”: “01028378782”, “email”: “[email protected]”, “post”: “采购部员工”, “address”: “这是地址”, “next_time”: “2022-05-10 00:00:00”, “remark”: “这是备注” } }} |
测试集合名称 | 是否被执行 |
---|---|
新增客户接口测试集合 | y |
新增联系人接口集合 | y |
依赖于设计去创建项目结构
在common这个package下新建excel_util.py文件
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2022/6/16 16:40
@Author : liudan
@File : excel_util.py
@Desc :
"""
import openpyxl
#读取全局变量sheet工作表
def get_variables(wb):
sheet_data = wb['全局变量']
variables = {} #用来存储读取到的变量,名称是key,值是value
line_count = sheet_data.max_row #获取总行数
for l in range(2,line_count+1):
key = sheet_data.cell(l,1).value
value = sheet_data.cell(l,2).value
variables[key] = value
return variables
#读取接口默认参数
def get_api_default_params(wb):
sheet_data = wb['接口默认参数']
api_default_params = {} # 用来存储读取到的变量,名称是key,值是value
line_count = sheet_data.max_row # 获取总行数
for l in range(2, line_count + 1):
key = sheet_data.cell(l, 1).value
value = sheet_data.cell(l, 2).value
api_default_params[key] = value
return api_default_params
#读取要执行的测试集合
def get_casesuitename(wb):
sheet_data = wb['测试集合管理']
case_suite_name = [] # 用来存储读要执行的测试集合名称
line_count = sheet_data.max_row # 获取总行数
for l in range(2, line_count + 1):
flag = sheet_data.cell(l, 2).value
if flag == 'y':
suite_name = sheet_data.cell(l, 1).value
case_suite_name.append(suite_name)
return case_suite_name
#需要根据要执行的测试用例集合名称来读取对应的测试用例数据
def read_testcases(wb,suite_name):
sheet_data = wb[suite_name]
line_count = sheet_data.max_row #总行数
cols_count = sheet_data.max_column #总列数
"""
规定读取出来的测试数据存储结构如下:
{
“新增客户正确”:[
['apiname','接口地址','请求方式',...],
['apiname','接口地址','请求方式',...],
]
’新增客户失败“:[
['apiname','接口地址','请求方式',...],
]
'新增客户失败-手机号格式不正确‘[
['apiname','接口地址','请求方式',...],
]
}
"""
cases_info = {} #用来存储当前测试集合中的所有用例的信息
for l in range(2,line_count+1):
case_name = sheet_data.cell(l,2).value #测试用例名称
lines = [] #用来存储当前行的测试数据
for c in range(3,cols_count+1):
cell = sheet_data.cell(l,c).value #单元格数据
if cell == None:
cell = ''
lines.append(cell)
#判断当前用例名称是否已存在与case_info中
#如果不存在,那就直接赋值
#如果存在,在原来的基础上赋值
if case_name not in cases_info:
cases_info[case_name] = [lines]
else:
cases_info[case_name].append(lines)
return cases_info
#整合所有要执行的测试用例数据,将其转换成pytest参数化需要的数据个够格式
def get_all_testcases(wb):
"""
整合后的数据结构是
[
['新增客户接口测试集合','新增客户正确',[[],[]]]
['新增客户接口测试集合','新增客户失败-用户名为空',[[],[]]]
]
:param wb:
:return:
"""
test_data = [] #用来存储所有测试数据
#获取所有要执行的测试用例集合名称
case_suite_name = get_casesuitename(wb)
for suite_name in case_suite_name:
#遍历读取每个要执行的测试集合sheet工作表中的测试用例数据
cur_cases_info = read_testcases(wb,suite_name) #是字典
for key,value in cur_cases_info.items():
#key是测试用例名称,value是测试用例多行数据信息
case_info = [suite_name,key,value]
test_data.append(case_info)
return test_data
if __name__ == '__main__':
wb = openpyxl.load_workbook(r'../testcases/CRM系统接口测试用例.xlsx')
print(get_variables(wb))
print(get_api_default_params(wb))
print(get_casesuitename(wb))
print(read_testcases(wb, '新建联系人接口测试集合'))
print('-------------------------------------')
print(get_all_testcases(wb))
在common这个package下新建requests_util.py文件
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2022/6/16 18:55
@Author : liudan
@File : requests_util.py
@Desc :
"""
import requests
import jsonpath
session = requests.session()
class RequestsClient:
def send(self,url,method,**kwargs):
try:
self.resp = session.request(url=url,method=method,**kwargs)
except BaseException as e:
raise BaseException(f'接口发起异常:{e}')
return self.resp
#针对jsonpath的数据提取
#第一个参数指的是匹配的数据jsonpath表达式
#第二个参数指的是想要返回匹配到的第几个,默认是0,表示第一个
def extract_resp(self,json_path,index=0):
#注意有的接口是没有返回信息的,返回信息为空
text = self.resp.text #获取返回信息的字符串形式
if text != '':
resp_json = self.resp.json() #获取响应信息的json格式
#如果能匹配到值,那么res就是个列表
#如果匹配不到res就返回False
res = jsonpath.jsonpath(resp_json,json_path)
if res:
#如果index<0,我认为你想要匹配所有的结果
if index < 0:
return res
else:
return res[index]
else:
print("没有匹配到任何数据")
else:
raise BaseException('接口返回信息为空,无法提取')
if __name__ == '__main__':
client = RequestsClient()
client.send(
url = 'http://82.156.74.26:9099/login',
method = 'post',
data = {
"username":"18866668888",
"password":123456
},)
print(client.extract_resp('$.Admin-Token'))
在我们测试的时候,有的参数并不能够写死,所以这个时候我们希望某些参数在每次执行的时都是动态变化的,那么就需要我们封装一些辅助随机函数来帮助我们完成数据的动态变化
在common下新建一个 util_func.py文件,在其中写上我们所需要的辅助该函数
随机数生成可以用第三方库:faker
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2022/6/17 10:59
@Author : liudan
@File : util_func.py
@Desc :
"""
import hashlib
import time
from faker import Faker
fake = Faker(locale='zh_CN')
def rdm_phone_number():
return fake.phone_number()
def cur_timestamp(): #到毫秒级的时间戳
return int(time.time()*1000)
def cur_date(): #2022-12-25
return fake.date_between_dates()
def cur_date_time(): #2022-12-25 10:4:2
return fake.date_time_between_dates()
def rdm_date(pattern="%Y-%m-%d"):
return fake.date(pattern=pattern)
def rdm_date_time():
return fake.date_time()
def rdm_future_date_time(end_date): #未来30天
return fake.future_datetime(end_date=end_date)
def md5(data):
data = str(data)
return hashlib.md5(data.encode('UTF-8')).hexdigest()
if __name__ == '__main__':
print(rdm_phone_number())
print(cur_date())
print(cur_date_time())
print(rdm_date())
print(rdm_date_time())
print(rdm_future_date_time('+60d'))
print(md5('admin'))
在excel中需要动态函数的时候,调用规则是 m d 5 ( a d m i n ) , {{md5(admin)}}, md5(admin),{{rdm_future_date_time(‘+60d’)}}
元字符 | 描述 |
---|---|
\ | 转义字符 |
* | 匹配前面的子表达式任意次,0~n |
+ | 匹配前面的子表达式一次或多次(大于等于1次) |
? | 匹配前面的子表达式零次或一次 {0,1} |
{n} | 匹配确定的n次 |
{n,m} | 最少匹配n次且最多匹配m次 |
.点 | 匹配除“\n”和"\r"之外的任何单个字符 |
? | 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少地匹配“o”,得到结果 [‘o’, ‘o’, ‘o’, ‘o’] |
\d | 匹配一个数字字符。等价于[0-9] |
[a-z] | 字符范围。匹配指定范围内的任意字符 |