技术分享 | 接口自动化测试如何搞定 json 响应断言?

原文链接

本文节选自霍格沃兹测试开发学社内部教材

在之前的的章节已经简单介绍了如何断言接口的响应值,在实际工作过程中,json 的响应内容往往十分复杂,面对复杂的 json 响应体,主要通过 JSONPath 解决。JSONPath 提供了强大的 JSON 解析功能,使用它自带的类似 XPath 的语法,可以更便捷灵活的用来获取对应的 JSON 内容。

环境准备

Python 版本安装

pip install jsonpath

Java 版本安装

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.6.0</version>
</dependency>

XPath 和 JSONPath 语法

下表是 XPath 和 JSONPath 语法进行对比,这两者的定位方式,有着非常多的相似之处:

比如同样一个字段,XPath 中的语法是:

/store/book[0]/title

JSONPath 的语法是:

$.store.book[0].title
$['store']['book'][0]['title']

下面是一组 json 结构,分别通过 JSONPath 和 XPath 的方式提取出来

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

下表列出了 XPath 与 JSONPath 的对比:

更多内容请访问:JSONPath - XPath for JSON

实战练习

以下是 测试人生 | 从外包菜鸟到测试开发,薪资一年翻三倍,连自己都不敢信!(附面试真题与答案) 这个接口的正常响应值(因响应篇幅过长,删除了部分内容):

{
  'post_stream': {
    'posts': [
      {
        'id': 17126,
        'name': '思寒',
        'username': 'seveniruby',
        'avatar_template': '/user_avatar/ceshiren.com/seveniruby/{size}/2_2.png',
        'created_at': '2020-10-02T04:23:30.586Z',
        'cooked': '<p>一直以来的平均涨薪率在30%以上,这次刷新的记录估计要保持好几年了</p>',
        'post_number': 6,
        'post_type': 1,
        'updated_at': '2020-10-02T04:23:48.775Z',
        'reply_to_post_number': None,
        'reads': 651,
        'readers_count': 650,
        'score': 166.6,
        'yours': False,
        'topic_id': 6950,
        'topic_slug': 'topic',
        'display_username': '思寒',
        'primary_group_name': 'python_12',
        ...省略...
      },
    ],
  },
  'timeline_lookup': ,
  'suggested_topics':,
  'tags': [
    '精华帖',
    '测试开发',
    '测试求职',
    '外包测试'
  ],
  'id': 6950,
  'title': '测试人生 | 从外包菜鸟到测试开发,薪资一年翻三倍,连自己都不敢信!(附面试真题与答案)',
  'fancy_title': '测试人生 | 从外包菜鸟到测试开发,薪资一年翻三倍,连自己都不敢信!(附面试真题与答案)',

}

接下来则需要实现一个请求,断言以上的响应内容中 name 字段为’思寒’所对应的 cooked 包含"涨薪"

Python 演示代码

JSONPath 断言

import requests
from jsonpath import jsonpath
r = requests.get("https://ceshiren.com/t/topic/6950.json").json()
result = jsonpath(r, "$..posts[?(@.name == '思寒')].cooked")[1]
assert "涨薪" in result

Java 演示代码

JSONPath 断言

import com.jayway.jsonpath.JsonPath;
import org.junit.jupiter.api.Test;
import java.util.List;
import static io.restassured.RestAssured.given;
public class jsonTest {

    @Test
    void jsonTest() {
        //获取响应信息,并转成字符串类型
        String res = given().when().
                get("https://ceshiren.com/t/topic/6950.json")
                .then().extract().response().asString();
        //通过jsonpath表达式提取需要的字段
        List<String> result = JsonPath.read(res, "$..posts[?(@.name == '思寒')].cooked");
        // 断言验证
        assert result.get(1).contains("涨薪");
    }
}