jsonpath和json数据(序列化eval()、反序列化loads())及断言处理(断言封装)

jsonpath:对json串进行搜索

安装jsonpath

安装:pip install jsonpath
导入: from jsonpath import jsonpath
jsonpath能通过简单的方式就能提取给定JSON中的字段。
jsonpath官方地址:https://goessner.net/articles/JsonPath/
在线检验jsonpath是否正确:https://www.jsonpath.cn/
结合2个网站,使用网站当中案例去练习。

jsonpath运算符 – 以$ 符号开头
在这里插入图片描述
$…price 获取json当中,key为price的所有值——》获取某个key的所有值,这个较为常用;=递归搜索
$.store.book[1].price——》一级一级去获取 更加精准
$…book[0] ——》 获取 多个值,可以用列表取值。
$…book[0:3] ——》可以使用列表的切片
$…book[0,1,2]——》获取第一个第二个 第三个
$…book[?(@.price > 10)]——》 ?过滤表达式== 价格大于10的显示出来
$…book. * ——》 匹配所有
Python的逻辑运算也可以在这里过滤表达式用:http://testingpai.com/article/1628759818085

"""
jsonpath(提取数据本身,jsonpath提取表达式$)注意: jsonpath表示结果是一个列表 ,如果结果里有个值 放在列表里。
- 如果获取的是里面具体的值,列表取值[0]要求: 数据必须是json格式数据。
"""import requests
from jsonpath import jsonpath# 1、登录
json = {"principal": "lemon_auto", "credentials": "lemon123456", "appType": 3, "loginType": 0}
url = 'http://shop.lemonban.com:8107/login'
res = requests.request(method="post",url=url, json=json)
print("登录的结果:", res.json())
access_token = jsonpath(res.json(),"$..access_token")[0] # jsonpath 提取数据
token_type =jsonpath(res.json(),"$..token_type")[0]
headers = {'Authorization': token_type + access_token}# 2、搜索商品
url = 'http://shop.lemonban.com:8107/search/searchProdPage'
param = {"prodName":"真皮圆筒包"}
res = requests.request(method="get",url=url,params=param)
# print("搜索商品结果:",res.json()) # 获取json格式 转化为字典格式
prodId = res.json()["records"][0]["prodId"]# 3、进入商品详情页
param = {"prodId":prodId}
url='http://shop.lemonban.com:8107/prod/prodInfo'
res = requests.request(method="get",url=url,params=param)
print("商品详情页的结果:",res.json())
skuId = res.json()["skuList"][0]["skuId"]
print(skuId)# 4、添加购物车
json = {"basketId": 0, "count": 1, "prodId": prodId, "shopId": 1, "skuId": skuId}
url='http://shop.lemonban.com:8107/p/shopCart/changeItem'
res = requests.request(method="post",url=url, headers=headers, json=json)
print("添加购物车的结果:", res.text)  # 这个接口返回值不是json格式 不能用json()方法转化字典,文本格式。# 5、购物车查询 - 获取basketId的值
json = []  # 参数 空列表
url = 'http://shop.lemonban.com:8107/p/shopCart/info'
res = requests.request(method="post",url=url, headers=headers, json=json)
print("购物车查询的结果:", res.json())
# 注意: 这个地方就没有办法通过字典取值,因为不是字典,是列表
basketId = jsonpath(res.json(),"$..basketId")
print(basketId)# 6、结算商品
json = {"addrId": 0, "basketIds": basketId, "couponIds": [], "isScorePay": 0, "userChangeCoupon": 0,"userUseScore": 0, "uuid": "05d2b445-c062-4690-8eca-b24bd028e40f"}
url = 'http://shop.lemonban.com:8107/p/order/confirm'
res = requests.request(method="post",url=url, headers=headers, json=json)
print("结算商品的结果:", res.text)

接口自动化测试用例设计

pytest测试框架去测试接口: 对应用测试用例。【登录,搜索,购物车等】

  • 针对每个模块设计测试用例 + 测试数据== 正常测试数据+ 异常测试数据,放在excel表格管理。

  • pytest的数据驱动 : 用例方法 执行所有数据。

这个大家做过测试,应该都知道接口用例的设计思路跟系统测试的用例设计是一样的。【等价类 边界值 场景法 错误推测法】
单接口场景 - 关注点是接口的各种参数组合

保障单接口的正确性,既要保证接口可以按照需求正确处理传入的参数,给出正确的返回;也要按照需求,正确的拒绝传入异常的参数,给出正确的拒绝性返回。

  • 正向场景:正常发送请求得到正常的响应数据
  • 异常场景:使用等价类和边界值的方法,设计测试用例,用不属于规定范围的数据去发送请求,检查服务器能否正常处理
    • 参数异常:长度异常,数据类型异常,为空,重复,业务异常参数【比如用户注册非正常运营商的号码】

多接口业务场景【冒烟测试】 - 优势:价值更高
按照用户实际使用场景梳理接口业务,通过多个接口的串联组合调用完成业务逻辑,更加关注于业务流程是否能跑通。

自动化测试用例设计注意事项

  • 每个表单一个模块,每个模块测试用例都可以单独运行,测试用例之间没有相互依赖;

  • 一个模块用例可以包含一个或多个测试步骤(一个接口请求即为一个测试步骤)

    • 一般单接口用例就是一个步骤

    • 业务场景用例包含多个步骤: 有些前置步骤需要执行

所以,我们来写一下登录的测试用例:
以后在公司开发没有给接口文档的时候,我们就自己去抓包获取这个接口的需要的信息;

登录通过抓包:登录需要的参数:{“principal”: “lemon_auto”, “credentials”: “lemon123456”, “appType”: 3, “loginType”: 0}

用户名: 账号为4~16位字母、数字或下划线

设计测试数据

正常: 符合长度和数据类型要求的 正确的用户名

异常: 小于4 大于16,不是数字字母和下划线字符,为空等

密码: 是否正确

APPtype和logintype 我们固定,就测试这种类型。

利用前面所写的excel封装,pytest参数化,来完成接口测试用例数据读取,并发起请求

  • 1、利用openpyxl库,从excel当中读取测试数据出来: 之前的接口数据都是手动写的,我们其实是在excel管理的,直接读取。

  • 2、使用pytest框架来写用例: 结合数据驱动来实现多条测试用例的运行

  • 3、在用例的步骤,发起http请求,然后获取响应结果

import json
import pytest
import requests
from tools.handle_excel import read_excel
from tools.handle_path import exc_path
# 第一步:调用excel操作哈拿书,读取excel数据
all_cases = read_excel(exc_path,"登录")
# 第二步: 使用pytest执行接口用例 + 发送接口请求
@pytest.mark.parametrize("data",all_cases)
def test_login_case(data):method = data['请求方法']url = data['接口地址']headers = data['请求头']params = data['请求参数']  # 字符串类型转换为字典
res = requests.request(method, url, json=params,headers=headers)

这个时候会成功么?-- 报错。: AttributeError: ‘str’ object has no attribute ‘items’。这是因为 从excel读取的数据看起来是字典,但是其实是字符串;但是接口发送的参数需要是字典的格式。所以需要数据格式的转化。

  • eval()
  • json.loads

json数据和序列化与反序列化

什么是json 【(JavaScript Object Notation, JavaScript 对象表示法】: json本质上是一种字符串,只是还要符合特殊格式的字符串

json - 表达数据。https://www.runoob.com/json/json-tutorial.html

JSON 数据的书写格式是: key : value ,看起来跟Python的字典很像,但是不是字典。

JSON语法规则:

  1. 数据在键值对中
  2. 多条数据由逗号分隔
  3. 花括号 {} 保存对象
    • JSON 值【value】可以是:
    • 数字(整数或浮点数)
    • 字符串(在双引号中)
    • 逻辑值(true 或 false)
    • 中括号 [] 保存数组: 类似于Python列表,索引取值 从0开始
{"hobby": ["游戏", "音乐", "电影"]
}
- null

json的语法要求和Python语法的区别:

  • 字符串:双引号括号来的。------> python当中单双引号都可以。

    • 因为很多其他的语言 双引号才能表示字符串 为了兼容其他语言,json里的字符串必须是双引号
    • 布尔值:true,false。----------> python当中是True,False.
    • null: 为空 ,------------> python当是None。
  • json不是字典,但是可以跟python的字典实现相互转化: 注意: 只能值字典类型 其他类型不可以

    • 序列化 - python数据类型–> 成json串。
    • 反序列化 - json字符串转成—> python数据类型。
  • json字符串与字典之间的转换:python的标准库是json,不需要安装 直接导入使用 处理json数据。

    • json.loads() – json字符串转换成python字典。-- 反序列化操作方法 ===== 重点掌握
    • json.dumps() – Python的字典转json字符串。–序列化的方法

evel和json转化各有优劣势 我们来对比一下:

json.loads():这个函数用于将JSON格式的字符串转换为字典。它只能处理符合JSON格式的字符串,如果字符串中包含其他类型的数据,可能会抛出异常。

eval():这个函数可以将字符串作为Python表达式进行求值,并返回结果。但是,由于它可能执行字符串中的任何代码,所以使用时需要特别小心,避免执行恶意代码。

参考文章:http://testingpai.com/article/1692240611928

json数据格式 : 特殊的字符串

a  = {"a":"1"}  #字典
print(type(a),a)
b = '{"a":"2"}'  # json串
print(type(b),b)c = "{'a':'1'}" # 普通的字符串 不是json数据 双引号
d = '{"a":true}'
e = '{"a":null}'

写自动化测试用例的数据设计为 json格式数据
– json.loads 统一转化。

  • 避免eval(): 不符合Python格式,转化失败 – null true false

如果不是一个标准的json格式的字符串,用反序列化失败,报错信息如下:
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

import jsonparams = "{'principal': 'lemon_py'}"
print(type(params),params)# # 反序列化的操作 转化为 Python的字典
# params = json.loads(params)
# print(type(params),params)# eval转化
params = eval(params)
print(type(params),params)

运行结果如下:

<class 'str'> {'principal': 'lemon_py'}
<class 'dict'> {'principal': 'lemon_py'}

断言 -测试用例的断言

测试用例执行步骤:

  • 1、利用excel库,从excel当中读取测试数据出来
  • 2、使用pytest框架来写用例
  • 3、在用例的步骤,发起http请求,然后对响应结果
  • 4、做断言。对响应状态码做断言。

思考:这个断言怎么做呢?

  • 1、首先,能用整个响应结果的文本做断言么?
    不可以,token是变化的 每次都不一样的
    可以采取: 取一些固定的字段 断言数据-- nickname

  • 2、登录成功和失败的结果不一样,用pytest框架数据驱动执行,需要断言是如何统一做断言?
    登录成功:响应结果是一个 json数据
    登录失败:响应结果是一个文本
    期望结果 用来做断言数据-- 格式统一化。

  • 1、在excel表格中把文本结果和json结果统一断言方式

  • 2、封装一个函数做断言,能处理不同的场景。

    • 第一步: 先把excel读取的数据反序列化为字典
    • 第二步: 拿到期望结果里的jsonpath表达式,取掉键值[k,v],v用来做断言,k用来取值。
      • 登录成功情况覆盖
      • 登录失败情况覆盖
    • 第三步: 可以做个判断,登录成功和登录失败分别做不同处理
      3、验证这个断言的方法是否适用于各个模块的用例。
"""
预期结果: 可能有以下两种形式:
{"$..nickName":"lemon_py"}
{"text":"Incorrect account or password"}处理步骤:
第一步: 反序列化操作: 转化为字典
第二步: 取到期望结果键值对: key 是jsonpath 表示式,value是断言的预期结果- 判断: key是$ 开头的json提取器,对login.json()去jsonpath提取- 或者是text,login.text 执行结果 vs value
第三步: key用来从login的结果里提取数据的 -- 执行结果
第四步: value vs 执行结果数据 断言比对"""
import json
from jsonpath import jsonpath# expected = '{"$..nickName":"lemon_py"}'  # 预期结果
expected = '{"text":"Incorrect account or password"}'
# login_result = {"access_token":"804d0382-b5b5-4950-bdcf-3f9376d84d27","token_type":"bearer","refresh_token":"29a0fc92-2578-4df2-8b00-3a6170fc480a",
#                 "expires_in":1295999,"pic":None,"userId":"8d0aa6ffd73a42a0acf5006817c0a564",
#                 "nickName":"lemon_py","enabled":True}  # login_res.json()
login_result = "Incorrect account or password" # login_res.text# 第一步: 反序列化操作: 转化为字典
expected = json.loads(expected)
# 第二步: 取到期望结果键值对: key 是jsonpath 表示式,value是断言的预期结果
for k,v in expected.items():if k.startswith("$"):# k是 "$..nickName",v 是lemon_pyactual_result = jsonpath(login_result,k)[0]assert actual_result == velif k == 'text':assert login_result == v

断言封装

"""
封装函数:一次封装 多次使用
步骤:
1、功能代码写出来
2、def 封装
3、参数化: 考虑数据是变化的 设置为参数
4、判断是否返回值 :不需要设置返回值"""
import json
from jsonpath import jsonpathdef response_asser(expected_data,login_resp):"""这是做响应断言的函数:param expected_data: 从excel表格里读取的预期结果表达式:param login_resp:登录的响应消息:return:"""# 第一步: 反序列化操作: 转化为字典expected = json.loads(expected_data)# 第二步: 取到期望结果键值对: key 是jsonpath 表示式,value是断言的预期结果for k,v in expected.items():if k.startswith("$"):# k是 "$..nickName",v 是lemon_pyactual_result = jsonpath(login_resp.json(),k)[0]assert actual_result == velif k == 'text':assert login_resp.text == v

requests库的二次封装

登录接口和搜索接口发送接口请求有太多雷同的代码,后面模块多了更多雷同;这种代码的冗余 我们需要解决一下,进行请求方法的二次封装。

  • 我们测试用例的脚本里尽量简洁的-- 功能测试人员进行测试用例模块维护,代码越少好。
  • 再次封装requests方法,把通用代码封装+考虑到各种请求情况 ,不需要写用例的改参数。 简化功能测试工作。
  • 希望这个统一的方法里能覆盖各种参数的传递方式
    • json参数
    • formdata参数
    • 文件参数等各种参数类型都能覆盖到
  • 希望这个统一的方法里能覆盖各种请求方法
    • get post put 等
  • 这样不管什么请求接口都可以直接调用同一个请求方法方法做处理。
"""
* 希望这个统一的方法里能覆盖各种参数的传递方式* json参数* formdata参数* 文件参数等各种参数类型都能覆盖到
* 希望这个统一的方法里能覆盖各种请求方法* get post put 等传参的方式:
1、get请求,params 接受
2、post请求:content-type的类型有关系。- application/json格式数据,json关键字接受参数,content-type的头部不需要传 默认就是json- application/x-www-form-urlencoded,data关键字接受参数,content-type的头部不需要传 默认就是form- multipart/form-data:支持文件/图片等传输 ,一般都是文件 图片上传接口- 发送请求的时候不能带上  'Content-Type': 'multipart/form-data'  删除之后才发送接口请求。步骤:
1、def 函数封装: 因为针对excel读取数据-- 头部 参数 url 方法等-- 参数数据参数化。
2、参数和头部 因为读取出来是字符串,所以都要做反序列的操作
3、接口请求可能是get  post  put等各种请求方法 分支判断
4、有些接口有头部 有些接口没有头部?-- 做判空处理的
5、post方法的数据格式- content-type有多种情况  所以都要覆盖。
6、当遇到文件类型接口的时候: 文件参数如何处理?-- 不能写死:- 文件名字 : 作为测试用例数据写在excel里,方便后续做数据维护  改excel  不需要改代码。- 文件路径处理: 统一的路径处理 handle_path"""
import json
import requests
from tools.handle_path import pic_pathdef requests_api(casedata):method = casedata["请求方法"]url = casedata["接口地址"]headers = casedata["请求头"]params = casedata["请求参数"]# 反序列操作: 结合判空处理,if headers is not None:headers = json.loads(headers)if params is not None:params = json.loads(params)#接口请求可能是get  post  put等各种请求方法 分支判断if method.lower() == "get":resp = requests.request(method=method, url=url, params=params,headers=headers)elif method.lower() == "post":# post请求:content-type的类型有关系。需要对每一种类型做处理 分支判断if headers["Content-Type"] == "application/json":resp = requests.request(method=method, url=url, json=params, headers=headers)if headers["Content-Type"] == "application/x-www-form-urlencoded":resp = requests.request(method=method, url=url, data=params, headers=headers)if headers["Content-Type"] == "multipart/form-data":# 发送请求的时候不能带上  'Content-Type': 'multipart/form-data'  删除之后才发送接口请求。headers.pop("Content-Type") # 字典删除元素filename = params["filename"]  # 文件名字 值file_obj = {"file": (filename, open(pic_path/filename, "rb"))}  # 文件参数resp = requests.request(method=method, url=url,headers=headers,files=file_obj)elif method.lower() == "put":passreturn resp

接口关联和前置的应用 --单接口测试

解决刚刚的遗留问题: 就是上传接口是需要先登录的,需要传一个authorization的token的头部信息;我们把他写死的;但是这个token 其实是会过期的,而且每个用户登录都是不一样的。所以,应该是要动态获取的。

  • 也就实现执行上传接口后,都需要先登录,获取token。
  • 那么这个可以把登录设置为上传接口的前置件。使用pytest的conftest前置。
    注意: 前置比较多的单接口测试 或者 业务流 冒烟测试 是不适合用conftest前置进行操作。

多个前置和业务流的自动化测试,需要用 【多接口的动态参数处理】
conftest.py文件如下

"""
执行一个接口执行之前 先要执行另外一个接口 获取数据【token】 给到下一个使用:
- pytest的夹具
- yield 返回值
- conftest 共享定义夹具 获取token"""
import pytest
import requests
from jsonpath import jsonpath@pytest.fixture()
def login_fixture():url_login = "http://shop.lemonban.com:8107/login"param = {"principal": "lemon_py", "credentials": "12345678", "appType": 3, "loginType": 0}resp = requests.request("post",url=url_login,json=param)access_token = jsonpath(resp.json(), "$..access_token")[0]  # jsonpath 提取数据token_type = jsonpath(resp.json(), "$..token_type")[0]token = token_type+access_tokenyield token  # 夹具的返回值

什么叫做多接口的动态参数处理?

  • 测试数据都放在excel中管理,每个接口如果有提取的数据就直接放在excel里写好,到时候直接读取出来做响应的提取操作即可。
  • 电商项目: 购物车 token proId skuID等 需要执行多个接口 获取返回值;
  • 业务流: 前面过很多步骤 【中间步骤都是单独的一个接口的请求】
    以上情况 都不太适合用conftest夹具测试使用。因为前置很多 处理比较麻烦。

提取出来的数据存放在哪里?以及怎么传给下个接口使用?
参考postman的处理方法。

  • 先执行前置登录-- 正常接口测试
  • 执行之后,提取数据–
  • 存在环境变量— 共享的后面每个接口都可以调用数据,变量
  • 后面接口调用 – {{变量}} === 这个是{{}是一个占位符,用你从环境变量里取到值替换掉这个位置的数据 【Jmeter - ${}】

设计的思路:业务流: 登录-搜索-进入详情页-添加购物车-查询购物车-结算-提交订单

  • 1、先把业务流的接口用例都写出来,在excel表格里统一管理
  • 2、关联的接口 需要提取的数据也提取出来: 加一个提取响应字段在excel里,用jsonpath提取
  • 3、代码提取出来后,保存在环境变量里 == 类 动态属性
  • 4、其他接口要用的位置用占位符表示,后面用代码替换成为 -环境变量里存的值。

动态参数具体看下一章节

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/571385.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab/simulink 火电储能一次调频,模糊控制优化储能调频系数分配,WOA鲸鱼算法优化火电储能出力占比,可模拟连续扰动,阶跃扰动

系统频率(阶跃扰动) 储能出力 储能soc对比 系统频率(连续扰动) 可见&#xff0c;鲸鱼算法woa和模糊控制储能更能有限改善频率 这里鲸鱼算法优化的是火储出力占比&#xff0c;模糊控制优化的是储能内部调频控制系数

【Java程序设计】【C00351】基于Springboot的疫情居家办公系统(有论文)

基于Springboot的疫情居家办公系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 项目获取 &#x1f345;文末点击卡片获取源码&#x1f345; 开发环境 运行环境&#xff1a;推荐jdk1.8&#xff1b; 开发工具&#xff1a;eclipse以及i…

Kali开启远程服务

一&#xff0c;先切换root账户 二、kali开启远程服务 1&#xff0c;修改远程登录的配置文件 vim /etc/ssh/sshd_config &#xff08;用文本编辑器打开此文件) 在文件的普通模式下&#xff0c;使用/PermitRootLogin&#xff0c;回车&#xff0c;查找到该行&#xff0c;i&#…

python中的 lambda函数

lambda函数属于匿名函数&#xff0c;lambda后 冒号前面的为函数参数&#xff0c;冒号后面为函数体

什么是公网IP?

公网IP&#xff0c;即公开网络IP地址&#xff0c;是指在互联网中公开可见、可访问的IP地址。每个设备在连接互联网时&#xff0c;都需要一个唯一的公网IP地址&#xff0c;以便其他设备可以定位并与之通信。 尽管公网IP在网络通信中具有重要作用&#xff0c;但它也带来了一些安全…

C/Cpp动态内存管理

一、C的动态内存开辟 malloc函数&#xff1a;void* malloc(size_t size);向内存申请一块连续可用的内存空间&#xff0c;并返回这块空间的指针&#xff1b;calloc函数&#xff1a;void* calloc(size_t num,size_t size);开辟num个大小为size的空间&#xff0c;并且空间会被初始…

【感悟《剑指offer》典型编程题的极练之路】02字符串篇!

​ 个人主页&#xff1a;秋风起&#xff0c;再归来~ 文章所属专栏&#xff1a;《剑指offer》典型编程题的极练之路 ​​​​​​ 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c…

python入门题:输入输出练习

以下是Python基础语法的练习&#xff0c;项目要求和代码如下&#xff1a; """ 例3&#xff1a;小精灵&#xff1a;你好&#xff0c;欢迎古灵阁&#xff0c;请问您需要帮助吗&#xff1f;需要or不需要&#xff1f; 你&#xff1a;需要 小精灵&#xff1a;请问你需…

[ZKP] Freivalds’ Algorithm

Freivalds’ Algorithm Freivalds, Rusins. “Probabilistic Machines Can Use Less Running Time.” IFIP congress. Vol. 839. 1977. Problem Statement Suppose we are given as input two n n n \times n nn matrices A A A and B B B over F p \mathbb{F}_p Fp​, w…

代码随想录刷题笔记 Day 59 | 两个字符串的删除操作 No.583 | 编辑距离 No.72

文章目录 Day 5901. 两个字符串的删除操作&#xff08;No. 583&#xff09;<1> 题目<2> 题解<3> 代码 02. 编辑距离&#xff08;No. 72&#xff09;<1> 题目<2> 题解<3> 代码 Day 59 01. 两个字符串的删除操作&#xff08;No. 583&#x…

MFC(二)集成基础控件

目录 OnCreateCStatic【标签&#xff0c;图片】CEdit【文本框&#xff0c;密码框&#xff0c;数值框&#xff0c;文本区】CButton【按钮&#xff0c;单选按钮&#xff0c;多选按钮】CComboBox【下拉列表&#xff0c;列表】CSliderCtrl【滑动条】CListCtrl【表格】CAnimateCtrl【…

【Java程序设计】【C00383】基于(JavaWeb)Springboot的水产养殖系统(有论文)

【C00383】基于&#xff08;JavaWeb&#xff09;Springboot的水产养殖系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c…