在接口自动化测试中,如何利用Pytest + JSONPath 进行接口响应断言

本文深入讲解JSONPath表达式语法,演示如何在自动化测试中利用JSONPath高效地从复杂JSON数据中提取特定数值,进行精确断言。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前有一篇文章,介绍了如何使用JSONSchema对接口响应进行断言,主要的适用场景是对响应的数据结构进行校验,保证客户端收到的数据结构稳定和合法。今天,介绍使用JSONPath对接口响应的值进行断言方法。

上一篇文章《在接口自动化测试中,如何利用Pytest + JSON Schema 进行接口响应断言》中,介绍了JSON Schema校验接口响应的数据结构的方法。在实际的测试工作中,很多时候是需要对接口的响应数值进行校验的。这时候就不能利用JSON Schema进行校验了,需要借助JSONPath表达式从JSON中抽取数值,进行断言。

通过JSONPath表达式,使得从多层嵌套JSON数据中提取数据变得非常简单。

下面这段数据是从JsonPath的官网https://goessner.net/articles/JsonPath/上摘抄的,假定这是一个接口的response。

{ "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
}

从这个response中,提取所有商品的价格,放到一个列表中,如果使用JSONPath表达式,只需要一行代码:

$.store..price

代 表 J S O N 对 象 的 根 节 点 , 代表JSON对象的根节点, JSON.store表示根节点下面的store属性,$.store…price则表示根节点下面的store属性下面的所有price属性的值。两个点表示store下面递归查找所有的price属性,因此在这个例子中可以查到所有的book的price和bicycle的price。

一个更简单的办法是从根节点开始递归查找price:

$..price

如果不使用JSONPath,想要获得所有的商品价格,则要写一个循环来读取,可以看出,使用JSONPath从JSON中提取数据变得简单和高效。

接下来看看JSONPath中都有哪些表达式。

01 — JSONPath表达式

JSONPath通过表达式定位JSON中的数据,一共有15个表达式,通过它们的灵活组合,提取JSON中数据非常容易。我们把JSON对象中每一个key都叫做一个属性(property)。下表是所有的JSONPath表达式,及其含义。
在这里插入图片描述
以response这个JSON对象为例,通过上面这些表达式的组合来取得值:

表达式取得的值
$.store.*response的store属性下的所有对象,是个list,第1个元素book,是一个list,第2个元素是bicycle,是一个对象。
$.store.bicycle.colorresponse的store属性下bicycle属性的color属性的值,red
$.store..price$..priceresponse中所有的price属性的值: [8.95, 8.99, 22.99, 19.95]
$.store.book[*] $..book[*]response中所有的book对象,是个list
$…book[*].titleresponse中所有的book对象的title的list:[Sayings of the Century,Moby Dick,The Lord of the Rings]
$…book[0]response中的第一本书,也是个list:[ { “category”:“reference”, “author”:“Nigel Rees”, “title”:“Sayings of the Century”, “price”:8.95 } ]
$…book[0].titleresponse中的第1本书的title:“Sayings of the Century”
$..book[0,1].title $..book[:2].titleresponse中的前2本书的title:[Sayings of the Century, Moby Dick]
$…book[::2].titleresponse中每一个取一本图书的title
$..book[-1:].title $..book[(@.length-1)].titleresponse最后1本书的title,是个list:[The Lord of the Rings]
$…book[?(@.author==‘J.R.R. Tolkien’)].title过滤表达式,作者为’J.R.R. Tolkien’的book的书名
$…[?(@.isbn)]过滤表达式,所有包含isbn这个属性的对象
$…[?(!@.isbn)]过滤表达式,所有不包含isbn这个属性的对象
$…book[?(@.price < 10)]价格低于10的书,是一个对象list
$..book[?(@.price > $.expensive)]价格比$.expensive高的书,是个list
$..book[?(@.author =~ /.*Tolkien/i)]作者的名字以Tolkien结尾的书,是个list,大小写敏感
$..book[?(@.category == 'fiction' || @.category == 'reference')]category为fiction或者reference的书,是个list

通过上面的例子,可以看到还是挺简单的。

02 — 在断言中的应用

依然假定上面的JSON对象是某个接口的response反序列化得到的。接下来,看看在自动化测试中如何利用JSONPath表达式进行断言。

在Python语言中,使用JSONPath需要安装一个第三方库jsonpath。

pip install jsonpath

测试用例1:

验证response中包含’Nigel Rees’, ‘Evelyn Waugh’, ‘Herman Melville’, 'J. R. R. Tolkien’这四位作者的图书。

import jsonpath
def test_authors():
    author_list = jsonpath.jsonpath(response, '$.store.book[*].author')
    assert author_list == ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'J. R. R. Tolkien']
2. 测试用例2:

验证response中的商品价格包含8.95, 12.99, 8.99, 22.99, 19.95这5种。

def test_all_price():
    store_price_list = jsonpath.jsonpath(response, '$.store..price')
    # store_price_list = jsonpath.jsonpath(response, '$..price')
    assert store_price_list == [8.95, 12.99, 8.99, 22.99, 19.95]
3.测试用例3:

验证response中的第3本图书的书名是Moby Dick。

def test_third_book_title():
    book_3 = jsonpath.jsonpath(response, '$.store.book[2]')
    assert book_3[0]['title'] == "Moby Dick"
4.测试用例4:

验证response中的最后一本图书的isbn是0-395-19395-8。

def test_last_book_isbn():
    last_book_isbn = jsonpath.jsonpath(response, f'$.store.book[-1:].isbn')
    # last_book_isbn = jsonpath.jsonpath(response, f'$.store.book[(@.length-1)].isbn')
    assert last_book_isbn == "0-395-19395-8"
5.测试用例5:

验证repsonse中前两本书价格之和是8.95 + 12.99

def test_12_books_price():
    book_12 = jsonpath.jsonpath(response, '$..book[0,1].price')
    assert book_12[0] + book_12[1] == 8.95 + 12.99
6.测试用例6:

验证response中有两本书的价格小于10

def test_2books_cheap_than_10():
    book_lg10 = jsonpath.jsonpath(response, '$..book[?(@.price<10)]')
    assert len(book_lg10) <= 2
7.测试用例7:

验证response中具有color属性的商品,其color是red

def test_has_color():
    colorful_goods = jsonpath.jsonpath(response, '$.store..[?(@.color)]')
    assert "red" == colorful_goods[0]['color']

好了就举这些例子吧。更多的例子大家可以参考第一小节JSONPath表达式来尝试。

Python还有一个包叫jsonpath2,用法和jsonpath差不多。大家可以自行尝试,示例代码可以参考:

https://jsonpath2.readthedocs.io/en/latest/_modules/bookstore_test.html#TestBookStore

03 — 总结

本文介绍了JSONPath表达式语法,以及在自动化测试中的应用。使用JSONPath表达式提取JSON数据的数值,非常简介和易懂。推荐大家需要对JSON数据的准确性进行校验时,使用JSONPath表达式。

参考资料

https://goessner.net/articles/JsonPath/
https://www.cnblogs.com/angle6-liu/p/10580792.html
https://support.smartbear.com/alertsite/docs/monitors/api/endpoint/jsonpath.html
https://jsonpath2.readthedocs.io/en/latest/_modules/bookstore_test.html#TestBookStore

<think>好的,用户想了解如何使用Pytest、Yaml、Excel和Allure构建接口自动化测试框架的最佳实践。我需要结合之前的引用内容,整理出一个清晰的步骤指南。首先,用户提到要使用Yaml和Excel,所以需要说明两者如何结合,比如用管理部分。引用中提到数据驱动和分层思想,这点很重要,应该强调代码与数据的分离,比如Yaml负责用,Excel可能存储测试数据或参数。接下来,结构设计部分需要分模块,比如用管理、请求处理、断言、报告生成。参考引用3和5,pytest的配置文件pytest.ini需要配置运行参数,比如addopts中的--alluredir指定报告目录。同时,测试用的目录结构需要明确,比如testpaths指向test_cases,这样用户知道如何组织文件。 用编写部分,Yaml的示需要展示基本结构,如请求方法、路径、参数和断言。引用3提到使用jsonpath和正则表达式提取数据,这点在断言和参数提取时要说明。Excel可能作为数据源,比如参数化测试数据,这里需要举说明如何与Yaml结合,或者分开使用。框架执行部分,引用5中的入口代码用pytest.main()执行,并生成Allure报告,需要给出具体的代码示,包括清理历史报告和生成新报告的步骤。优化技巧方面,数据驱动可以用pytest.mark.parametrize,结合Yaml或Excel的数据,这点引用4有提到,要详细说明如何实现。 此外,异常处理和日志记录也是关键,确保框架的健壮性。引用1和2提到持续集成,可以建议后续加入Jenkins,但用户当前问题可能不需要,但可以提一下作为扩展。 最后,相关问题需要覆盖框架设计、数据驱动、报告优化和CI集成,帮助用户深入思考。需要确保每个步骤都参考了提供的引用内容,并且结构清晰,用户能够逐步实施。同时,注意使用正确的中文术语,避免技术术语错误,比如正确使用“参数化”而不是“参数化处理”。</think>### 使用Pytest+Yaml+Excel+Allure构建接口自动化框架的最佳实践 #### 一、框架设计核心原则 1. **分层架构**:将框架分为用层(Yaml/Excel)、执行层(Pytest)、报告层(Allure)和工具层(Requests/Jsonpath),实现代码与数据分离[^1][^4] 2. **数据驱动**:通过Yaml定义测试流程,Excel存储参数化数据,实现测试数据与逻辑解耦[^4] 3. **统一断言机制**:使用Python断言结合JSONPath表达式进行结果验证[^3] #### 二、目录结构设计 ```bash ├── test_data # Excel参数化数据 ├── test_cases # Yaml测试用 ├── core # 核心框架代码 │ ├── ApiClient.py # 封装的请求模块 │ └── Assert.py # 自定义断言方法 ├── conftest.py # Pytest全局fixture ├── pytest.ini # Pytest配置文件[^3] └── requirements.txt # 依赖库列表 ``` #### 三、关键配置实现 1. **pytest.ini配置**(核心运行规则): ```ini [pytest] addopts = -vs --alluredir=./results --clean-alluredir testpaths = ./test_cases python_files = test_*.py python_classes = Test* python_functions = test_* ``` 2. **Yaml用**: ```yaml - name: 用户登录接口测试 request: method: POST url: /api/login headers: Content-Type: application/json data: username: ${EXCEL_DATA(login,username)} password: ${EXCEL_DATA(login,password)} validate: - eq: [status_code, 200] - contains: $.token ``` 3. **Excel数据驱动**(参数化示): | TestCaseID | username | password | expected | |------------|----------|-----------|----------| | login_001 | admin | Admin@123 | success | | login_002 | test | wrong_pwd | fail | #### 四、框架执行流程 1. 通过`pytest.main()`启动测试执行[^5] ```python # run.py主入口 import pytest import os if __name__ == '__main__': pytest_args = [ '-s', '-v', '--capture=sys', '--alluredir=./results', '--clean-alluredir' ] pytest.main(pytest_args) os.system("allure generate ./results -o ./report --clean") ``` 2. Allure报告生成命令配置: ```bash allure serve ./results # 本地查看报告 allure generate ./results -o ./report # 生成静态报告 ``` #### 五、优化实践技巧 1. **动态参数处理**: - 使用`${}`语法实现Excel数据与Yaml用的关联[^4] - 通过正则表达式提取响应数据供后续用使用[^3] 2. **异常处理机制**: ```python # ApiClient.py封装示 import requests from requests.exceptions import RequestException class ApiClient: def send_request(self, method, url, **kwargs): try: response = requests.request(method, url, **kwargs) response.raise_for_status() return response except RequestException as e: logger.error(f"请求失败: {str(e)}") raise CustomRequestError("接口请求异常") ``` 3. **持续集成准备**: - 在Jenkins中配置Allure报告插件 - 使用`pytest-xdist`实现并行测试 - 添加邮件通知扩展[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值