binary.read/write 单元测试必须用 bytes.buffer,因其干净实现 io.reader/io.writer 接口,避免 net.conn 带来的粘包、超时等干扰;需重置 buffer、用 bytes() 而非 string()、断言 binary.size 与协议长度一致、字段全大写定长、覆盖长度校验失败/超长/截断三类边界。

binary.Read/Write 单元测试必须用 bytes.Buffer 而非 net.Conn
直接在测试里 mock net.Conn 或用 listener/dialer,会引入 TCP 粘包、缓冲区状态、超时等无关变量,让测试不稳且难 debug。真实打包/拆包逻辑只依赖 io.Reader 和 io.Writer 接口,bytes.Buffer 是最干净的实现。
常见错误现象:测试偶尔 panic 或读出 0 值,实际是读取位置错位或未重置 buffer;用 conn.Close() 后再读,得到 io.EOF 而非预期数据。
- 写入后调用
b.Bytes()获取完整字节切片,不要用b.String()(可能含非法 UTF-8 字节) - 每次测试前用
buf.Reset()清空,避免上一次残留影响 - 若需验证写入顺序,可用
hex.Dump(buf.Bytes())打印原始 hex 对照协议文档
结构体封包测试要校验 binary.Size 与协议长度是否一致
binary.Size(&s) 返回结构体按指定字节序序列化后的**确切字节数**。它不是估算值,而是 runtime 实际计算结果。如果和协议文档写的“Header 长度 = 12 字节”对不上,说明字段类型、padding 或大小声明有误——测试里必须断言这个值。
容易踩的坑:int 在不同平台长度不同;struct 里混用 int 和 int32;字段名小写导致被 binary.Read 跳过但 binary.Size 仍计入(不会,binary.Size 同样只计算导出字段)。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 测试中写:
if binary.Size(&header) != 12 { t.Fatal("header size mismatch") } - 字段必须全大写、全定长:如
Cmd uint8,不能是cmd uint8或Cmd int - 如果协议要求 4 字节对齐,而 struct 实际占 10 字节,别靠加 padding 字段硬凑——先确认是协议真需要对齐,还是你误读了文档
拆包测试必须覆盖长度校验失败、超长包、截断包三类边界
真实网络环境里,binary.Read 不会失败于“格式不对”,而会失败于“根本没读够”。测试只验证正常流程,等于放过了 90% 的线上问题。
典型错误:测试用完整 buffer 调 binary.Read(r, order, &s) 成功,上线后遇到粘包或半包就 panic 或静默错读。
- 构造长度字段为 0 的包:应拒绝并返回明确错误(如
errInvalidLength),不能继续读 payload - 构造长度字段为 2GB 的包:分配前必须校验
pkgLen MB,否则 OOM - 构造只有 header 没 payload 的包(buffer 长度 = headerSize):
io.ReadFull应返回io.ErrUnexpectedEOF,你的代码要捕获并转成业务错误
跨语言兼容测试得用 hex 字节序列做输入源
Python 的 struct.pack('IHI', 1, 2, 3) 和 Go 的 binary.Write(w, binary.LittleEndian, ...) 是否等价?不能靠“看起来一样”,要拿真实字节比。
最容易被忽略的是:Python 默认小端,而很多协议文档写“uint32”却不注明端序;或者 Python 用了 !I(大端),Go 却传了 binary.LittleEndian。
- 把 Python 输出存成 hex 字符串,如
"0100000002000300",在 Go 测试里用hex.DecodeString转成[]byte,再喂给bytes.NewReader - 不要用 Python 的 repr 或 str 输出,它们可能做转义或编码转换
- 测试断言时,别只 assert 字段值,也 assert 整个解析后 buffer 读取位置是否刚好到末尾(
r.Len() == 0)
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










