登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go tool test2json 怎么把独立测试二进制输出转成事件流

来源:17golang原创

时间:2026-11-05 08:33:59 494浏览 收藏

独立运行 Go 测试二进制时,把可执行文件交给 go tool test2json,并给测试程序传入 -test.v=test2json,就能把测试文本转换成连续的 JSON 事件。转换器的 -p 设置事件中的包名,-t 添加时间信息;没有测试命令时,它改从标准输入读取文本。

这个入口适合“编译测试产物,再交给单独的执行环境运行”的场景。普通项目直接运行测试时优先采用 go test -json;本文只处理一个独立测试二进制,不把多包文本日志混在一起转换。

官方地址:https://pkg.go.dev/cmd/test2json

报告只有文本时,先固定独立二进制入口

考虑一种报告接入问题:执行器已经能运行测试产物,报告脚本却只会搜索日志里的 PASS 或 FAIL。测试日志也可能出现这些字符串,子测试名称又带层级,单纯匹配文字容易把业务日志当成状态。下面以这个设想场景拆解接入方式,不把它写成已经发生的线上事故。

先固定单包测试入口。以下命令用于 POSIX shell,要求当前目录已经有可编译的 Go 测试,二进制与执行环境的系统、架构匹配。包名示例是报告标签,应换成项目实际的包路径。

# 在当前单包模块目录编译测试产物;失败则不运行旧二进制。
go test -c -o ./fixture.test . &&
go tool test2json -p example.test/fixture -t ./fixture.test -test.v=test2json > events.jsonl

编译与执行之间使用条件连接,目的是避免本次编译失败却继续运行残留产物。events.jsonl 是这一次执行的事件文件,使用覆盖写入,避免把上一次结果追加到同一个逻辑运行中。这里给出的是可复现写法,没有执行命令或展示伪造的终端结果。

从编译产物到转换器的参数归属

区分转换器参数和测试进程参数
原创静态结构图:转换器持有包名与时间选项,测试二进制持有详细输出选项;连线表示参数归属和接口关联,不表示运行顺序。

接入时先把配置分成两组。二进制路径前的参数控制 test2json,路径后面的参数属于测试程序。把 -test.run、-test.timeout 等测试选项放到测试命令一侧,才能明确它们由谁解释。

参数或实体归属本文用途
-p example.test/fixturetest2json给事件附上包标签,不负责查找或编译该包
-ttest2json请求事件时间信息
./fixture.test测试命令实际需要运行的独立测试产物
-test.v=test2json测试程序启用适合事件识别的详细输出形式

把包标签填对并不能证明二进制来自这个包。产物路径、构建记录与报告标签需要由调用方保持一致。对于分发到其他机器的测试产物,建议在运行记录旁保存构建标识,避免报告名称和实际内容错配;这个记录属于执行器的职责。

日志识别偏差通常出在输入协议

设想接入过程依次经历了“直接收集文本”“搜索失败行”“引入转换器”三个阶段。如果最后一步仍然使用普通 -test.v,转换并非完全不可用,但官方明确指出识别保真度较低。应先改成专用取值,再排查读取端,而不是立刻增加复杂的文本正则。

另一类偏差发生在转换之前:执行器为每一行添加日期前缀、插入颜色控制字符,或者把截断日志交给转换器。这些加工都可能改变它需要识别的文本形式。需要美化日志时,把展示工作放到事件消费端,保留转换前的原始输入。

已有原始文本文件也可以输入转换器,但文件必须来自同一个测试二进制,最好在采集时就使用专用详细输出形式。转换器不能为一份缺少详细测试信息的旧日志补造原本未记录的测试归属,也不能恢复被截掉的内容。

# 对单个二进制的原始详细输出离线转换;时间信息不能冒充原执行时间。
go tool test2json -p example.test/fixture  events.jsonl

按事件解码,区分测试失败与读取失败

解释事件字段与报告归属
原创静态结构图:Action 表示事件类型,Package 与 Test 用于归属,Output 保留文本内容。图不展示真实测试结果。

结果不是一个 JSON 数组,而是一串独立事件对象。读取端可以使用 json.Decoder 连续 Decode,直到 EOF。以下原创程序仅消费已保存的事件文件,不负责启动测试,也不据此声称测试进程一定正常结束。

package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "io"
    "os"
)

type event struct {
    Action  string
    Package string
    Test    string
    Output  string
}

func collect(r io.Reader, logs io.Writer) (bool, error) {
    dec := json.NewDecoder(r)
    failed := false
    for {
        var ev event // 每次解码创建新对象,避免缺省字段残留。
        err := dec.Decode(&ev)
        if errors.Is(err, io.EOF) {
            return failed, nil
        }
        if err != nil {
            return failed, fmt.Errorf("读取事件失败: %w", err)
        }
        if ev.Action == "fail" {
            failed = true // 无 Test 的包级失败也要保留。
        }
        if ev.Action == "output" {
            // 不用 Fprintln,Output 本身可能已经带换行。
            if _, err := io.WriteString(logs, ev.Output); err != nil {
                return failed, fmt.Errorf("保存日志失败: %w", err)
            }
        }
    }
}

func main() {
    failed, err := collect(os.Stdin, os.Stdout)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(2) // 事件读取或日志写入错误。
    }
    if failed {
        os.Exit(1) // 这是示例消费端自己的退出码约定。
    }
}

保存为 read_events.go 后,可把事件文件重定向到程序的标准输入。程序把所有 output 文本写到标准输出;它的退出码 1 表示观察到 fail,2 表示消费端错误。这是本文示例自己的约定,不是给 test2json 重新定义退出码。

这个最小结构只声明读取端用到的字段,其他字段保留在原始事件文件中。不要在每轮循环外复用一个未重置对象,否则缺省的 Test 等字段可能保留上次解码的值,导致包级事件被误认为某个测试的事件。

Action 用于判断状态,Package 和 Test 用于归属。Test 为空的事件需要作为包级信息处理;不能为了界面整齐随便补上“上一个测试名”。output 事件只是输出,里面出现失败字样,不等于事件自身就是 fail。

保留原始输出,避免再次破坏事件流

报告可以同时保存事件文件与可读日志:前者保留字段供后续分析,后者便于人工阅读。对于 output 事件,按接收顺序拼接 Output;不要额外添加换行,也不要对每一段 TrimSpace,否则原本的空格、段落边界和缩进都可能被改写。测试输出中无效 UTF-8 会由转换器替换为有效字符,因此不应把重建文本称为任意二进制字节的逐字节备份。

测试进程的标准输出与标准错误会进入转换后的输出事件。执行器自己的提示应使用独立诊断通道,不能直接插入 JSON 文件。否则连续 Decode 可能在一条普通提示文本处失败,而问题其实来自数据通道污染。

还应单独记录转换命令的进程退出状态。文件读到 EOF 只说明输入结束,不能独自证明事件完整;运行被中断、磁盘写入失败或传输提前关闭,都需要结合执行器记录解释。本文的 collect 只做消费端最小处理,没有把这些情况伪装成成功。

接入报告脚本时的边界速查

  • 一个转换入口对应一个测试二进制,保留每次运行的独立事件文件。
  • 需要测试归属时使用专用详细输出形式,不依赖普通日志正则。
  • 转换前不加前缀、不美化、不拼接无关执行器日志。
  • 读取端同时处理包级事件与具名测试事件,不能忽略解码错误。
  • 报告归档包含命令退出状态,EOF 不作为唯一成功标志。

必须先保存文本,再转换吗?

不必。直接由 test2json 运行测试二进制即可连续输出事件,适合实时状态展示。已有原始日志时再使用标准输入模式,两个入口不要混淆。

能把 events.jsonl 再交给 test2json 吗?

不应这样做。它的输入是测试文本,已经转换好的事件应交给 JSON 读取端。二次转换不会增加测试信息,反而会把事件文本误当成测试输出。

为什么明明测试失败,读取程序却没有报 JSON 错误?

测试失败与 JSON 语法错误属于不同层次。合法的 fail 事件仍然是有效 JSON。读取端应把业务状态和解码错误分别处理,不能只依靠 Decode 是否成功生成测试结论。

参考:Go test2json 官方使用与事件格式、Go test2json 命令源码、encoding/json 连续解码接口。命令与读取示例为本文原创。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>