提高接口自动化测试效率:使用 JMESPath 实现断言和数据提取

前言

做接口自动化,断言是比不可少的。如何快速巧妙的提取断言数据就成了关键,当然也可以提高用例的编写效率。笔者在工作中接触到了JMESPath,那到底该如何使用呢?带着疑惑一起往下看。

JMESPath是啥?

JMESPath 是一种用于查询和转换 JSON 数据的简洁、强大的查询语言。它提供了一种灵活的方式来从复杂的 JSON 结构中提取所需的数据,并支持各种操作和函数,以满足不同的查询需求。

JMESPath如何使用?

在使用 JMESPath 查询 JSON 数据之前,我们需要安装 jmespath 库。安装命令如下:

复制代码
pip install jmespath

简单路径表达式

假设我们有以下的 JSON 数据:

kotlin 复制代码
data = {
    "name": "Alice",
    "age": 25,
    "email": "alice@example.com"
}

我们想要从中提取 "name" 属性的值。使用 JMESPath,我们可以编写以下代码:

ini 复制代码
import jmespath
​
expression = "name"
result = jmespath.search(expression, data)
​
print(result)  # 输出:Alice

这里,我们定义了一个路径表达式 "name",然后使用 jmespath.search() 函数将该表达式应用于数据 data 上。结果会被存储在 result 变量中,并输出为 "Alice"

嵌套属性访问

当 JSON 数据具有嵌套结构时,可以使用点号 . 连接多个属性名来表示深层的属性访问。考虑以下 JSON 数据:

kotlin 复制代码
data = {
    "person": {
        "name": "Alice",
        "age": 25,
        "email": "alice@example.com"
    }
}

我们要提取 "name" 属性,可以使用以下路径表达式:

ini 复制代码
expression = "person.name"
result = jmespath.search(expression, data)
​
print(result)  # 输出:Alice

这里,我们将属性名 "person""name" 使用点号 . 连接起来,表示深层的属性访问。

复杂嵌套查询

假设我们有一个包含学生信息和他们的课程成绩的更复杂的 JSON 数据,如下所示:

kotlin 复制代码
data = {
    "students": [
        {
            "name": "Alice",
            "courses": [
                {"name": "Math", "score": 95},
                {"name": "English", "score": 88}
            ]
        },
        {
            "name": "Bob",
            "courses": [
                {"name": "Math", "score": 75},
                {"name": "English", "score": 92}
            ]
        }
    ]
}

现在,假设我们想要获取每个学生的数学成绩。可以通过以下表达式实现:

css 复制代码
expression = "students[].courses[?name == 'Math'].score"

再次执行查询并打印结果:

scss 复制代码
result = jmespath.search(expression, data)
print(result)

输出结果将是一个包含每个学生数学成绩的列表:

lua 复制代码
[[95], [75]]

列表索引

对于 JSON 中的列表属性,可以使用方括号 [index] 来指定索引位置来检索数据。假设我们有以下 JSON 数据:

kotlin 复制代码
data = {
    "fruits": ["apple", "banana", "cherry"]
}

我们想要提取第二个元素,即 "banana"。可以使用以下路径表达式:

ini 复制代码
expression = "fruits[1]"
result = jmespath.search(expression, data)
​
print(result)  # 输出:banana

这里,我们使用方括号 [1] 来指定索引位置,表示提取第二个元素。

过滤器

JMESPath 提供了过滤器功能,使我们能够根据特定条件筛选出符合要求的数据。过滤器使用方括号 [?],后跟过滤条件。考虑以下 JSON 数据:

kotlin 复制代码
data = {
    "users": [
        {"name": "Alice", "age": 25},
        {"name": "Bob", "age": 30},
        {"name": "Charlie", "age": 28}
    ]
}

我们想要提取年龄大于 25 岁的用户对象。可以使用以下路径表达式进行过滤:

ini 复制代码
expression = "users[?age > `25`]"
result = jmespath.search(expression, data)
​
print(result)

输出结果为:

css 复制代码
[    {"name": "Bob", "age": 30},    {"name": "Charlie", "age": 28}]

这里,我们使用了过滤器 [?age > 25],表示只选择满足条件的用户对象。

合并操作

JMESPath 还支持合并操作符 [],用于将多个查询结果合并成一个列表。假设我们想要获取所有学生的所有课程名称。可以使用合并操作符 [] 来实现:

kotlin 复制代码
data = {
    "students": [
        {
            "name": "Alice",
            "courses": [
                {"name": "Math", "score": 95},
                {"name": "English", "score": 88}
            ]
        },
        {
            "name": "Bob",
            "courses": [
                {"name": "Math", "score": 75},
                {"name": "English", "score": 92}
            ]
        }
    ]
}

获取所有学生的所有课程名称:

ini 复制代码
expression = "students[].courses[].name"
result = jmespath.search(expression, data)
print(result)

输出结果为:

css 复制代码
['Math', 'English', 'Math', 'English']

排序和切片

JMESPath 还支持对查询结果进行排序和切片操作。假设我们想要按学生年龄进行降序排序。可以使用排序函数 sort() 和逆序函数 reverse() 来实现:

kotlin 复制代码
data = {
    "students": [
        {"name": "Alice", "age": 20},
        {"name": "Bob", "age": 22},
        {"name": "Charlie", "age": 21}
    ]
}

得到按年龄降序排列的学生列表:

scss 复制代码
expression = "students | sort_by(@, &age) | reverse(@)"
​
result = jmespath.search(expression, data)
print(result) # [{'name': 'Bob', 'age': 22}, {'name': 'Charlie', 'age': 21}, {'name': 'Alice', 'age': 20}]

切片

kotlin 复制代码
data = {
    "fruits": ["apple", "banana", "cherry"]
}

利用切片获取第一个元素

ini 复制代码
import jmespath
​
data = {
    "fruits": ["apple", "banana", "cherry"]
}
expression = "fruits[0:1]"
result = jmespath.search(expression, data)
​
print(result) # ['apple']

管道

使用管道符号(|),将当前节点的结果传到管道符右侧继续投影。

kotlin 复制代码
data = {
    "students": [
        {"name": "Alice", "age": 20},
        {"name": "Bob", "age": 22},
        {"name": "Charlie", "age": 21}
    ]
}

获取所有的姓名:

scss 复制代码
expression = "students[*].name"
result = jmespath.search(expression, data)
print(result) # ['Alice', 'Bob', 'Charlie']

如果在此基础上想要得到Bob这个值。我们尝试使用索引:

ini 复制代码
expression = "students[*].name[1]"
result = jmespath.search(expression, data)
print(result) # []

发现执行结果是一个空列表,这个时候怎么办呢?使用管道表达式, <expression> | <expression>

ini 复制代码
expression = "students[*].name | [1]"

内置函数

计算列表长度

makefile 复制代码
data = {
    "students": [
        {"name": "Alice", "age": 20},
        {"name": "Bob", "age": 22},
        {"name": "Charlie", "age": 21}
    ]
}
​
expression = "length(students)"  # 也可以这样写expression = "students | length(@)"
result = jmespath.search(expression, data)
print(result) # 3

length就是jmespath内置的函数。

当然还有一些其他常用的内置函数,比如:

  • starts_with(str, prefix): 检查字符串是否以指定前缀开头。
  • ends_with(str, suffix): 检查字符串是否以指定后缀结尾。
  • contains(str, substring): 检查字符串是否包含子字符串。
  • length(arr): 返回数组的长度。

最后

jmespath确实很强大,通过逐步学习和实践,可以更好地掌握 JMESPath 的功能和灵活性。

相关推荐
极客数模9 分钟前
2025年MathorCup 大数据竞赛明日开赛,注意事项!论文提交规范、模板、承诺书正确使用!2025年第六届MathorCup数学应用挑战赛——大数据竞赛
大数据·python·算法·matlab·图论·比赛推荐
xuejianxinokok19 分钟前
io_uring 快吗? Postgres 17 与 18 的基准测试
数据库·后端·postgresql
PieroPc21 分钟前
用Python Streamlit Sqlite3 写一个简单商品管理系统
数据库·python·sqlite·streamlit
小白银子22 分钟前
零基础从头教学Linux(Day 55)
java·linux·服务器·python
DokiDoki之父24 分钟前
SpringMVC—REST风格 & Restful入门案例 & 拦截器简介 & 拦截器入门案例 & 拦截器参数 & 拦截器链配置
后端·restful
程序员小远25 分钟前
Appium+python+unittest搭建UI自动化框架
自动化测试·软件测试·python·测试工具·ui·appium·测试用例
JohnYan34 分钟前
安全密钥(Security Key)和认证技术相关词汇表
后端·安全·设计模式
北海道浪子42 分钟前
[免费送$1000]ClaudeCode、Codex等AI模型在开发中的使用
前端·人工智能·后端
MavenTalk1 小时前
如何根据不同的场景选择YOLO相应的基座模型
python·yolo·yolo11n·yolo11m·yolo11s·yolo11x
神奇的代码在哪里1 小时前
使用python开发任天堂gameboy模拟器|pyboy开发实践
python·宝可梦·pyboy·gameboy模拟器·任天堂掌机模拟器