1. JsonPath基本介绍
1.1 JsonPath简介
JsonPath是一种用于在JSON数据中定位和提取特定数据的表达式语言。它类似于XPath用于XML的定位和提取,可以帮助我们灵活地从复杂的JSON结构中获取所需的数据。
1.2 JsonPath的特点
● JsonPath可处理的报文类型为字典类型
● 通过JsonPath成功获得的内容,会以list的形式进行返回,也就意味着你的 JsonPath是可以有一个值或者多个值同时存在的。
● 如果要基于JsonPath来处理json数据,就一定要去同步处理list
● JsonPath定义中,如果表达式出现错误,则会返回False(布尔类型的值)
● JsonPath要么返回False,要么返回list
1.3 JsonPath安装
pip install jsonpath # 安装命令 pip show jsonpath # 安装效验
1.4 JsonPath实际运行场景
对应做软件测试来说:
1. 主要能够通过这个方式提取数据
2. 业务场景: 断言 、接口关联
常规的报文也避免不了比较复杂的情况,如下所示包含了一个人员的信息,包括姓名、年龄、邮箱、地址、爱好、教育背景和参与的项目。
它展示了JSON数据的嵌套结构和多种数据类型的使用,那么我们应该如何进行解决呢?
{ "name": "Alice", "age": 25, "email": "[email protected]", "address": { "street": "456 Elm Street", "city": "Los Angeles", "country": "USA" }, "hobbies": ["reading", "traveling", "cooking"], "education": [ { "degree": "Bachelor's", "major": "Computer Science", "university": "ABC University", "year": 2018 }, { "degree": "Master's", "major": "Business Administration", "university": "XYZ University", "year": 2020 } ], "projects": [ { "name": "Project A", "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "contributors": ["John", "Sarah", "Mike"], "completed": "true" }, { "name": "Project B", "description": "Nulla vel sagittis elit. Vivamus auctor massa in lacinia pellentesque.", "contributors": ["Alice", "David"], "completed": "false" } ], "is_active": "true" }
举一个栗子:提取上面这段代码中education里的年份值
更多提取信息,语法格式,先下面“JsonPath基本格式规范-示例参考”
从上图可以看出:返回的数据类型是列表
1.5 JsonPath基本格式规范
以下是一些JsonPath的常用语法和示例:
附:jsonpath在线解析器 http://www.atoolbox.net/Tool.php?Id=792
$ 根节点,也是所有JsonPath表达式的开始 . 表示获取子节点 .. 表示获取所有符合条件的内容 * 代表所有的元素节点 [index] 表示迭代器的标示(可以用于处理下标等情况) [,] 表示多个结果的选择 [start:end] 指定范围内的元素 ?(@.property == value) 表示过滤操作 # 比较运算符 @ 表示当前节点
示例参考:
① 基本语法
$ :根节点,也是所有JsonPath表达式的开始 . :当前节点 .. :递归下级节点
② 属性操作
$.property :选择指定属性 $['property'] :选择指定属性,属性名带有特殊字符时使用
③ 数组操作(含头不含尾)
$.array[index] :选择指定索引处的元素 $.array[start:end] :选择指定范围内的元素
④ 过滤器(比较运算符都可以用)
$.array[?(@.property == value)] :根据条件过滤数组元素 $.array[?(@.property > value)] :根据条件过滤数组元素
⑤ 路径组合
$.parent.child :选择父节点下的子节点 $.parent[*].child :选择父节点下所有子节点的某个属性
1.6 JsonPath断言实例
一个登陆接口的正向用例:
断言 :提取响应数据和期望数据进行对比
import jsonpath import requests import json # 完整的请求url url = "http://xxxx域名/index.php?s=/api/user/login" # 公共参数 pulic_data = {"application": "app", "application_client_type": "weixin"} # 登陆接口请求参数 data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"} json_data = json.dumps(data) # 将请求参数转为json格式 # 指定请求头的参数的数据格式为json header = {'Content-Type':'application/json;charset=utf-8'} # 发送请求 res = requests.post(url,params=pulic_data,headers=header,data=json_data) print(res.json()) exData = "登录成功" # 期望结果 # 实际结果:最后记得加索引,不然会报错 SjData = jsonpath.jsonpath(res.json(),"$.msg")[0] # 断言 期望结果结果等于实际结果,并格式化输出期望结果、实际结果 assert exData == SjData,"期望结果是:{0},实际结果是:{1}".format(exData,SjData)
所有工具中的断言都一样,如果断言成功,则不会显示断言信息,也不会报错
如果断言失败,则报错信息中显示断言信息
2. 接口关联
2.1 接口关联意义
接口关联是在进行接口测试时,将一个接口的返回结果中的某些数据提取出来,然后作为后续接口请求的参数或者验证的依据。通过接口关联,可以实现接口间的数据传递和依赖关系的建立。
2.2 接口关联类型
接口关联通常分为两种类型:请求关联和响应关联。
1. 请求关联:
提取关键参数
:在一个接口的请求中,某些参数的值是由之前接口的响应结果提供的。需要提取出这些关键参数,并将其作为后续接口的请求参数。例如,一个接口的请求中需要使用到某个用户的登录令牌(token),可以通过在登录接口的响应结果中提取出令牌(token),然后在后续接口的请求中使用。
----------->>>
2. 响应关联:
验证关键数据
:在一个接口的响应结果中,某些数据是需要验证的,可以将这些数据提取出来,并进行断言或者其他验证操作。例如,一个接口的响应结果中包含了某个订单的状态信息,可以将该状态信息提取出来,然后进行断言,验证订单是否处于正确的状态。
2.3 加入购物车案例
这里还是基于上个案例的登录接口(某电商平台):
1. 首先登录成功,提取token值。
2. 通过提取的token,将商品加入购物车。
使用josnpath提取token
第一步:将打印信息换成text文件,方便接下来去调试
import json import requests import jsonpath url = "http://xxxx域名/index.php?s=/api/user/login" pulic_data = {"application": "app", "application_client_type": "weixin"} data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"} header = {'Content-Type': 'application/json; charset=utf-8'} json_data = json.dumps(data) # --------------------发送接口请求--------------------------- res = requests.post(url, params=pulic_data, headers=header,data=json_data) # 正确的演示 # --------------------获取响应数据--------------------------- print(res.text) # 改成获取text文本内容 # --------------------获取数据进行断言处理--------------------------- exData = "登录成功" SjData = jsonpath.jsonpath(res.json(),"$.msg")[0] assert exData == SjData, "期望结构是:{0},实际结果是:{1}".format(exData, SjData)
第二步:将上一步的代码运行,复制控制台输出的响应内容,全部黏贴到json在线工具中,美化一下格式:JSON在线解析及格式化验证 - JSON.cn
第三步:复制黏贴美化后的响应内容,到josnpath在线工具中,进行调试
ps:如果代码能力强的,可以跳过这两步,直接写josnpath提取token的代码
josnpath提取响应信息中的token,有上面两种写法,第二种更简单
第四步:去完善代码
import json import requests import jsonpath url = "http://xxxx域名/index.php?s=/api/user/login" pulic_data = {"application": "app", "application_client_type": "weixin"} data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"} header = {'Content-Type': 'application/json; charset=utf-8'} json_data = json.dumps(data) # --------------------发送接口请求--------------------------- res = requests.post(url, params=pulic_data, headers=header,data=json_data) # 正确的演示 # --------------------获取响应数据--------------------------- print("响应信息:",res.text) # 改成获取text文本内容 # --------------------获取数据进行断言处理--------------------------- exData = "登录成功" SjData = jsonpath.jsonpath(res.json(),"$.msg")[0] assert exData == SjData, "期望结构是:{0},实际结果是:{1}".format(exData, SjData) # --------------------获提取token值--------------------------- token =jsonpath.jsonpath(res.json(),"$..token")[0] print("提取出来的token值:",token)
加入购物车
完整代码:
import json import requests import jsonpath url = "http://xxxx域名/index.php?s=/api/user/login" pulic_data = {"application": "app", "application_client_type": "weixin"} data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"} header = {'Content-Type': 'application/json; charset=utf-8'} json_data = json.dumps(data) # --------------------发送接口请求--------------------------- res = requests.post(url, params=pulic_data, headers=header,data=json_data) # 正确的演示 # --------------------获取响应数据--------------------------- print("响应信息:",res.text) # 改成获取text文本内容 # --------------------获取数据进行断言处理--------------------------- exData = "登录成功" SjData = jsonpath.jsonpath(res.json(),"$.msg")[0] assert exData == SjData, "期望结构是:{0},实际结果是:{1}".format(exData, SjData) # --------------------获提取token值--------------------------- token =jsonpath.jsonpath(res.json(),"$..token")[0] print("提取出来的token值:",token) # --------------------加入购物车--------------------------- # 加入购物车请求参数 data = { "goods_id":"10", # 商品id "spec": "", # 商品规格,无规格 "stock": 1 # 加入数量 } # 公共参数 params = { "application": "app", "application_client_type": "weixin", "token": token } res = requests.post(url="http://xxxx域名/index.php?s=api/cart/save", params=params, json=data) print(res.json())
打开web页面:也显示加入成功
拿到对应的项目,一定要梳理出来对应的业务流程及它对应接口之间的关联关系
任何项目:都需要理解需求、业务流程=====================================================================
基于这个思路:还可以梳理出不同的测试场景
1. 浏览商品--加入购物车 (有规格/无规格) --从购物车去提交订单
a.选择商品[一般对应列表都是会有 (购物车)列表接口,都会有id)
b.选择对应的地址
c.选择支付方式)--订单支付---------------->>>
2. 浏览商品--加入购物车--从购物车去提交订单--取消订单---------------->>>
3. 浏览商品--提交订单
a.从数据库找商品表 (找到商品名称字段)
b.从页面上获取 (id,一般会在页面对应的url中)=====================================================================
可以看出,接口测试,与功能测试的思路、场景都是一样的
再换一个有商品规格的商品,加入购物车
import json import requests import jsonpath url = "http://xxxx域名/index.php?s=/api/user/login" pulic_data = {"application": "app", "application_client_type": "weixin"} data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"} header = {'Content-Type': 'application/json; charset=utf-8'} json_data = json.dumps(data) # --------------------发送接口请求--------------------------- res = requests.post(url, params=pulic_data, headers=header,data=json_data) # 正确的演示 # --------------------获取响应数据--------------------------- print("响应信息:",res.text) # 改成获取text文本内容 # --------------------获取数据进行断言处理--------------------------- exData = "登录成功" SjData = jsonpath.jsonpath(res.json(),"$.msg")[0] assert exData == SjData, "期望结构是:{0},实际结果是:{1}".format(exData, SjData) # --------------------获提取token值--------------------------- token =jsonpath.jsonpath(res.json(),"$..token")[0] print("提取出来的token值:",token) # --------------------加入购物车--------------------------- # 加入购物车请求参数,有规格值 data = { "goods_id": "11", "spec": [ { "type": "尺寸", "value": "L" } ], "stock": "10"} # 公共参数 params = { "application": "app", "application_client_type": "weixin", "token": token } res = requests.post(url="http://xxxx域名/index.php?s=api/cart/save", params=params, json=data) print(res.json())
再加一个查看购物车接口、并提取购物车列表Id:
import json import requests import jsonpath url = "http://xxxx域名/index.php?s=/api/user/login" pulic_data = {"application": "app", "application_client_type": "weixin"} data = {"accounts": "hailey", "pwd": "hailey123", "type": "username"} header = {'Content-Type': 'application/json; charset=utf-8'} json_data = json.dumps(data) # --------------------发送接口请求--------------------------- res = requests.post(url, params=pulic_data, headers=header,data=json_data) # 正确的演示 # --------------------获取响应数据--------------------------- print("响应信息:",res.text) # 改成获取text文本内容 # --------------------获取数据进行断言处理--------------------------- exData = "登录成功" SjData = jsonpath.jsonpath(res.json(),"$.msg")[0] assert exData == SjData, "期望结构是:{0},实际结果是:{1}".format(exData, SjData) # --------------------获提取token值--------------------------- token =jsonpath.jsonpath(res.json(),"$..token")[0] print("提取出来的token值:",token) # --------------------加入购物车--------------------------- # 加入购物车请求参数,有规格值 data = { "goods_id": "11", "spec": [ { "type": "尺寸", "value": "L" } ], "stock": "10"} # 公共参数 params = { "application": "app", "application_client_type": "weixin", "token": token } res = requests.post(url="http://xxxx域名/index.php?s=api/cart/save", params=params, json=data) print(res.json()) # --------------------购物车列表--------------------------- # 公共参数,查看购物车没有请求参数 params = { "application": "app", "application_client_type": "weixin", "token": token } res = requests.get(url="http://xxx域名/index.php?s=api/cart/index", params=params) print(res.json()) cardid =jsonpath.jsonpath(res.json(),"$..id")[0] # 推荐的用法 print("提取出来的商品id:>>",cardid)
总结
思路:
web自动化、api接口自动化,本质上都是模拟用户操作,所以手工测试有哪些步骤、自动化同样也有哪些步骤。
列表页面:商品列表、购物列表,任何列表都有增删改查,所以都会有对应的接口,接口关联依赖上一个接口的数据,一般都会有一个 (列表)唯一值 id。