测试结果数据结构:JSON/XML/YAML 格式解析与标准化

搞回归测试的人,每天都要跟各种测试结果打交道。你想想看,不同框架吐出来的结果,格式五花八门——有的给 JSON,有的塞 XML,还有的用 YAML。如果不把它们统一成一套结构,后续的分析、告警、历史对比根本没法自动跑起来。

这一节,我就从实战角度聊聊这三种主流格式怎么解析,字段含义到底是什么,以及最重要的——怎么标准化。

测试结果数据结构解析与标准化流程 测试原始结果 JSON 格式 XML 格式 YAML 格式 解析 JSON 字段 解析 XML 字段 解析 YAML 字段 字段映射 & 标准化 统一字段名 · 时间格式 · 状态枚举 标准化测试结果

1. 三种格式,各有各的脾气

JSON——现在的测试框架大多默认吐 JSON,比如 Jest、Mocha、Pytest 加上 --json 参数。轻量、直接,解析起来不费劲。但我记得有一次,某个服务返回的 JSON 里嵌套了 8 层对象,每次读取 results[0].tests[2].failures[0].message 写到手抽筋。

XML——Java 圈子特别喜欢用 XML,比如 JUnit 报告就是标准的 XML 格式。优点是 schema 约束强,字段定义很死,不容易出现歧义。缺点嘛……你写过 XPath 就知道了,反人类。而且文件体积大,跑完一万条 case,XML 报告能顶好几 MB。

YAML——Ruby 社区和部分 CI 工具(比如 GitHub Actions)喜欢用。可读性最好,能写注释,但它对缩进非常敏感。我在项目中遇到过因为某个字段前多了一个空格,整个解析崩掉的情况,排查了半天才发现。

核心结论:格式本身不重要,重要的是字段语义必须清晰。

2. 字段含义——你得知道每段数据在说什么

不管哪种格式,最终我们关心的数据模型大同小异。下面这张表是我自己整理的「最小必备字段集」,建议直接拿来用。

字段名 类型 含义 标准化建议
suite_name string 测试套件名称 优先从 CI 环境变量注入,避免硬编码
test_name string 单个用例名称 支持通配符匹配
status enum 执行结果:passed/failed/skipped/error **统一枚举值,不要出现 SUCCESS/FAILURE/ERROR 混用**
duration_ms integer 执行耗时(毫秒) 强制使用毫秒,不接收秒或微秒
failure_message string 失败原因摘要 允许为空,但必须有该字段
failure_detail string 详细堆栈或日志 过长时截断前 2000 字符
timestamp string(ISO8601) 执行完成时间 **统一用 UTC + 毫秒,例如 2025-04-08T14:30:00.123Z**
tags array[string] 自定义标签 用于分类和过滤

⚠️ 我曾经踩过的坑:某个 CI 脚本里用了 status: "PASS",另一个地方写 status: "passed",第三个地方直接丢 status: true。标准化之前,每次汇总都得写好几个正则去匹配。后来我直接在前置步骤里做了一次 枚举归一化,所有非标准值都转成小写 enum,世界清净了。

3. 如何标准化?三步走,不废话

说白了,标准化不是重写测试框架,而是在消费端做一个 格式适配层。我自己在团队里推了下面这个流程,效果还不错。

  1. 识别来源格式——根据文件后缀或 MIME type 判断是 JSON 还是 XML 还是 YAML。
  2. 解析原始结构——用对应语言的解析器读出所有原始字段。
  3. 字段映射——把原始字段映射到前面那张表的标准字段名。这一步可以用一个配置文件做映射规则。

举个例子,假设 JUnit XML 里这样的片段:

<testcase name="testLogin" classname="com.example.LoginTest" time="0.023">
  <failure message="Expected 200, got 401" type="AssertionError">
    java.lang.AssertionError: Expected 200, got 401
      at com.example.LoginTest.testLogin(LoginTest.java:42)
  </failure>
</testcase>

映射之后变成:

{
  "suite_name": "com.example.LoginTest",
  "test_name": "testLogin",
  "status": "failed",
  "duration_ms": 23,
  "failure_message": "Expected 200, got 401",
  "failure_detail": "java.lang.AssertionError: Expected 200, got 401\n ...",
  "timestamp": "2025-04-08T14:30:00.123Z",
  "tags": []
}

你看,字段名统一了,时间单位统一了,状态枚举统一了。后面做趋势分析、失败率统计,完全不用关心原始格式。

小技巧:在 CI 流程里加一个「结果验证」步骤,读取标准化后的 JSON 做 schema 校验。如果某个字段缺失或类型不对,直接让流程失败,避免脏数据流到下游。

4. 格式解析的常见问题与应对

  • JSON 里的空数组 vs null——有些框架失败时返回 "failures": [],有些直接 "failures": null。标准化时统一转成空数组。
  • XML 的 CDATA 和特殊字符——解析时务必保留原始内容,不要丢失 escape。我见过有人用 regex 解析 XML 导致 stack trace 被截断,非常坑。
  • YAML 的多行字符串——用 | 或 > 表示折叠,解析后要注意换行符的处理,否则后续拼接到告警消息里排版会乱。

嗯,这里再强调一点:不管用什么格式,千万别在结果里写二进制或 base64 大段数据。标准化之前先把日志里的截图文件路径剥离出来,单独存到对象存储,结果里只留链接。

5. 我个人的标准化实践

我在维护一个大型 CI 平台的时候,每天要处理来自 20 多个团队的回归结果。最初大家各写各的,有的用 Python 的 unittest JSON 输出,有的用 JUnit XML,还有的直接在 Jenkins 里写 console output 正则提取。

后来我写了一个轻量的 结果归一化服务,流程很简单:

  • 监听指定目录或 S3 bucket 的新文件
  • 根据文件后缀选择解析器
  • 用 JSON Schema 校验并映射
  • 写入统一的 Elasticsearch 索引

这么一搞,后续的看板、失败分析、Flaky Test 检测都只需要查询一个索引,维护成本直线下降。

一句话总结:测试结果标准化不是技术难题,而是管理规范问题。你只要把字段字典定义清楚,把解析和映射写成一个函数,剩下的就是监督大家照做。

最后提醒一句:标准化之后,记得保留一份原始原始格式的备份。万一映射逻辑有 bug,还能回退重算。