cover_image

JSON 断言器,让测试自动化效能狂飙

乐昂 群核科技质量技术
2025年03月14日 06:01
一、背景介绍

今天,我们将聚焦自动化测试的三大核心之一,被誉为“守门员”的关键角色——断言器。在自动化测试中,断言器负责验证系统输出是否符合预期,是确保测试有效性的核心环节。

然而,面对格式多样、结构复杂的测试数据(尤其是主流的 JSON 格式),传统的断言方式往往显得效率低下、维护困难,如:

  • 效率低下 手动编写断言代码工作量大,容易出错,难以应对海量测试数据。

  • 维护困难 接口响应结构变化时,需要手动修改大量断言代码,维护成本高。

  • 可读性差 断言代码冗长复杂,难以理解和维护,影响测试用例的可读性和可维护性。

为了解决这些痛点,我们将深入探索 JSON 断言器的设计与实现,揭秘如何构建一个高效、灵活、易维护的 JSON 断言器,为您的自动化测试保驾护航!

二、架构概览

(一)框架介绍

JSON对比断言器,从功能上大概划分如下几个部分:

  • 断言器:负责接收测试数据、处理规则和忽略规则,执行 JSON 数据对比,并输出断言结果。自动化测试脚本中直接调用,完成 JSON 数据的自动化断言。
  • 前置处理:在数据对比之前,对输入的 JSON 数据进行清洗、转换、排序等操作,确保数据格式一致,提高对比准确性。
  • 后置处理:对对比结果进行格式化、过滤、统计等操作,生成更直观、易读的断言报告。
  • 数据管理:提供对输入输出数据、对比规则、忽略规则等的管理功能,例如:预期结果生成与更新、可视化数据上传。
  • 对比核心:对预处理后的 JSON 数据进行深度对比,识别出数据差异,并生成详细的对比结果。
  • 可视化页面:提供图形化界面,方便用户输入 JSON 数据、配置对比规则、查看对比结果,并进行交互式操作。
图片
(二)核心入口

1、断言器入口

KuMatchers 作为断言器工具集合的入口,封装了丰富的断言器,能够与各种自动化测试框架无缝集成,在测试执行的任何阶段,轻松完成实际结果与预期数据的对比验证。

// 与本地文件对比actual KuMatchers.equalToJsonInClassPath(expectFile)// 与对象对比actual KuMatchers.equalToJson(expect)    .withLabel("报告标签")    // 使用正则表达式指定忽略对比的 JSON 路径。    .ignorePathsRegexes(["/test1"])    // 设置浮点数对比的精度位数。    .withDecimalPlaces(2)    // 标记该断言用于异步测试场景。            .markAsAsyncTest()    // 开启严格模式,JSON 结构或字段不匹配时断言失败。    .enableStrictMode()    // 指定数组元素的排序规则,确保对比结果一致。    .setArraySorting([            "/path1": ["key1","key2"],                            "/path2": ["key1""key2"],            "/path3": [],]        )
全局配置,统一管理断言行为:
# 自动创建预期结果文件matcher.json.compare.expect.auto.create=true # 覆盖预期结果文件 (谨慎使用)matcher.json.compare.expect.overwrite=false # 测试成功时覆盖预期结果matcher.json.compare.expect.overwrite.if.success=false # 测试失败时覆盖预期结果matcher.json.compare.expect.overwrite.if.fail=false # 严格模式 (新增字段或对象会导致断言失败)matcher.json.compare.strict=false # 全局忽略路径正则表达式 (对所有测试用例生效)# matcher.json.compare.ignore.regex=/b, /subModels/.*/id#....等等

2、可视化入口

2种常见的使用场景

  • 集成测试报告:断言器将断言结果通过数据管理模块上传至服务器,并在测试报告中嵌入可视化页面。

  • 独立页面访问:通过独立的页面入口,手动输入 JSON 数据和选择对比规则,实时查看可视化结果,进行数据分析和规则调试。

页面集成丰富的可视化功能

  • 定义对比规则:灵活定义数据对比规则,包括忽略路径、排序规则、精度设置等,满足不同场景下的对比需求。

  • 结果分享:轻松分享可视化结果,方便团队成员协作分析和问题讨论。

  • 可视化索引:建立可视化结果索引,方便快速查找和定位历史对比记录。

  • 规则前端编辑:提供友好的前端界面,方便您直接在页面上编辑和调试对比规则,提升规则管理效率。

图片

三、规则设计

(一)可视化规则

规则设计思路

传统的断言框架通常使用简单的布尔值标记断言结果(通过或失败),这限制了结果的表达能力和可视化可能性。为了在页面可视化和断言器中使用统一的规则,需要选择将可视化规则(断言结果数据)作为对比核心的输出结果。这些规则经过后置处理,可以转换为可视化所需的格式或断言器需要的结果。

参考 JSON Patch (RFC 6902) 的规范,使用以下操作描述两个 JSON 对象 (a 和 b) 之间的差异:

  • add: b 相对于 a,需要在指定路径 (path) 添加值 (value)。

  • remove: b 相对于 a,需要删除指定路径 (path) 的值。

  • replace: b 相对于 a,需要将指定路径 (path) 的值替换为目标值 (value)。

路径 (path) 使用 JSON Pointer (RFC 6901) 表示

https://www.rfc-editor.org/rfc/rfc6901

断言结果判定

  • 如果 a 和 b 的对比结果生成的非空规则列表,则说明存在差异,断言结果为 false

  • 如果规则列表为空,则说明 a 和 b 完全一致,断言结果为 true

规则案例

[  {"op":"add", "path":"/0/a", "value":1},  {"op":"remove","path":"/0/a","value":1},  {"op":"replace","path":"/0/a","toPath":"/0/a","value":2,"toValue":1}]

规则优势

  • 统一规则: 可视化页面和断言器使用相同的规则,保证一致性。

  • 灵活表达: 支持多种操作类型,可以描述复杂的 JSON 差异。

  • 易于解析: 基于 JSON 格式,方便解析和处理。

(二)前置/后置处理

为了满足不同场景下的数据对比需求,需要提供灵活的前置和后置处理规则。这些规则主要分为两类:

影响对比输入数据的规则:

  • 应用场景: 当需要更新预期结果或进行数据覆写时,需要决定是否使用处理后的数据。

  • 注意事项: 在页面可视化时,需要明确标注使用的是原始数据还是处理后的数据,避免混淆。

不影响对比输入数据的规则:

  • 应用场景: 一般用于对对比结果进行过滤、排序等操作,不影响原始数据。

  • 注意事项: 无需特殊处理,可直接应用于对比结果。

代码示例:

public class JsonDiffSettings {  /**  * 后置处理,源数据无变化  * 忽略校验的 json point路径。  */  List<String> ignoreJsonPathRegexps = [];  /**  * 后置处理,源数据无变化  * 是否为严格模式,严格模式add认为对比失败。  */  boolean strict = false;  /**  * 前置处理,源数据存在  * 小数精度。  */  Integer decimalPrecision = null;  ...等等}

四、核心算法

对比核心是 JSON对比 的核心模块,负责对前置处理后的数据进行深度对比,并输出符合基础可视化规则的对比结果。为了更清晰地展示数据差异,我们将 JSON 节点值类型分为两类,并分别将其映射为基础的可视化结果,假设存在两个 JSON 对象 a 和 b,其中 a 为预期结果,b 为实际结果。

(一)对象对比

1、对比策略

  • a 和 b 相同的 key:递归对比对应的 value 值。

  • a 中多出的 key:标记为 "remove",表示预期结果中存在但实际结果中缺失的字段。

  • b 中多出的 key:标记为 "add",表示实际结果中存在但预期结果中缺失的字段。

2、对比实现

迭代 a 的所有 key:

  • 如果 b 中不存在相同的 key,则标记为 "remove"。
  • 如果 b 中存在相同的 key,则递归调用对比方法,对比对应的 value 值。

迭代 b 的所有 key:

  • 如果 a 中不存在相同的 key,则标记为 "add"。

(二)数组对比

1、效果对比

数组对比常见的有两种数组对比策略,可以基于实际情况选择实现

  • 基于数组坐标对比: 按照数组下标依次对比每个元素,性能较好,但当数组元素顺序不一致时,会导致较多的不一致结果,影响可视化效果。

图片
优先查找公共序列对比:
  •  寻找两个数组之间的最长公共子序列,并以此为基础进行对比,可以更清晰地展示数组元素的差异,但性能相对较差。

图片

2、性能对比

对比策略
数据量
不一致数量
对比时间 (100次,测试机器)
基于数组坐标对比
1.38MB
3000
9404ms
基于数组坐标对比
1.38MB
3
8296ms
基于数组坐标对比
1.38MB
0
5852ms
优先查找公共序列对比
1.38MB
3000
108066ms
优先查找公共序列对比
1.38MB
3
100390ms
优先查找公共序列对比
1.38MB
0
5434ms
3、选择建议
  • 数组元素顺序固定: 建议使用 基于数组坐标对比,以获得更好的性能。不考虑可视化的情况下数组大小对比会更快。

  • 数组元素顺序不固定: 建议使用 优先查找公共序列对比,以获得更清晰的可视化效果。

4、对比实现

实际使用了 优先查找公共序列对比 的方案,并采用了动态规划算法来求解最长公共子序列。以下是简化后的 Groovy 伪代码:

// 状态转移方程def dp(i, j) {        if (a[i-1] == b[j-1]) {        return dp(i-1, j-1) + 1    } else {        return Math.max(dp(i, j-1), dp(i-1, j))    }}// 状态回溯def backtrackDiffs(a, b, dp) {    def arrayChanges = []    def aSize = a.size(), bSize = b.size()    def aChangeCnt = 0, bChangeCnt = 0        while (aSize > 0 || bSize > 0) {       if (aSize > 0 && bSize > 0) {          if (a[aSize-1] == b[bSize-1]) {            // 记录变更            recordChanges(arrayChanges, aChangeCnt, bChangeCnt, aSize-1, bSize-1)            aChangeCnt = 0            bChangeCnt = 0            aSize--            bSize--          } else if (dp[aSize-1][bSize] > dp[aSize][bSize-1]) {            aChangeCnt++            aSize--          } else {            bChangeCnt++            bSize--          }        } else if (aSize > 0) {          aChangeCnt++          aSize--        } else {          bChangeCnt++          bSize--        }                // 处理剩余变更        if (aSize == 0 && bSize == 0) {          recordChanges(arrayChanges, aChangeCnt, bChangeCnt, -1, -1)          aChangeCnt = 0          bChangeCnt = 0        }    }    return arrayChanges.reverse()}

五、页面可视化

页面可视化功能基于 svelte-jsoneditor 组件实现,该组件提供了丰富的 JSON 数据展示和编辑功能。为了更直观地展示数据差异,我们进行了以下优化:

1、节点高亮

  • 实现原理: 利用 onClassName 方法,根据对比核心生成的规则,对存在差异的 JSON 节点进行高亮显示。

  • 高亮规则:

    • 新增节点: 使用绿色背景高亮。

    • 删除节点: 使用红色背景高亮。

    • 修改节点: 使用黄色背景高亮。

2、节点索引

  • 实现原理: 利用 scrollTo 方法,根据对比核心生成的规则,快速定位到存在差异的 JSON 节点。

  • 索引规则:

    • 支持按路径索引: 通过 JSON Path,快速定位到指定节点。

    • 支持按差异类型索引: 选择差异类型(新增、删除、修改),快速定位到所有对应类型的节点。

六、总结展望

通过本文介绍自动化测试中JSON断言器的设计与实现,基本解决了传统断言方式在效率、维护性和可读性上的不足,通过架构设计、核心算法和可视化功能提升了断言器的灵活性和易用性。未来我们还可以在效率、扩展方面不断扩展,如:

  • 智能化断言:
    • 未来可以引入机器学习技术,自动生成断言规则,减少人工编写和维护的成本。
    • 通过历史测试数据的分析,自动识别常见的数据差异模式,优化断言策略。
  • 性能优化
    • 探索并行计算或分布式处理技术,进一步提升对比效率。
  • 集成与扩展
    • 增强与通用断言器的集成,丰富各类断言的页面可视化。
    • 增强常见自动化报告的定制可视化,满足不同团队的需求。
    • 支持更多传统断言器无法支持的数据格式(如XML、YAML等)的对比断言,扩展应用场景。

推荐阅读

自动化测试 · 目录
上一篇酷家乐自动化持续集成困境突围
继续滑动看下一个
群核科技质量技术
向上滑动看下一个