Go 测试新提案的野望:Example 函数不再只是个“花瓶”

📅 2026/7/13 21:31:27
Go 测试新提案的野望:Example 函数不再只是个“花瓶”
当你在 Go 里写Example函数时有没有过这种感觉你在文档里展示了一段完美代码但它其实根本没法运行或者你想演示一个带*testing.T的测试助手函数但标准库的go test直接给你报了个编译错误这种感觉就像你写了一本精美的烹饪书但里面所有菜谱都缺了最关键的一步——你只能看不能做。现在Go 官方的一个新提案正在试图改变这一切。它的核心思想很简单但影响可能很深远允许 Example 函数拥有任意签名并让它们出现在文档里但不强制它们运行。故事的开头为什么我们要折腾 Example事情要从一个“老冤家”说起很多人在写测试框架或测试辅助库时想给用户展示一个“可测试的示例”Testable Example。但这个示例函数往往需要接受一个*testing.T参数以便演示如何断言、如何记录日志。按照 Go 现在的规矩Example函数只能长这样funcExampleXxx(){...}// 不带任何参数也不返回任何值一旦你试图给它加个参数比如func ExampleXxx(t *testing.T)go test会直接无视它甚至报错。你的精心演示就真的只能“演示”了——它不会出现在文档里更不会被自动测试。于是就有了这个新提案。新方案你的 Example 你做主这个提案提出的解决办法有一种“大道至简”的味道。它提议任意签名都能上墙任何叫Example或ExampleXxxXxx 不能是小写字母开头的函数无论它有多少参数、返回什么都会出现在go doc生成的文档中。但别想跑go test不会运行任何带参数或有返回值的Example函数。规矩要守如果这种带参数的Example函数里还写了// Output:注释go test会报错因为那意味着你想验证输出但函数却无法被运行。这样一来你就能写出下面这种“原汁原味”的示例代码了它会老老实实地躺在文档里供人瞻仰// 展示一个可能返回错误的示例funcExample_returning_an_error()error{f,err:os.Open(testfile)iferr!nil{returnerr}deferf.Close()// ... 你的业务逻辑returnnil}// 展示一个需要 *testing.T 的测试助手funcExampleFunc_taking_a_t(t*testing.T){iferr:examplepackage.Func(t);err!nil{t.Fatal(err)}}社区众生相这个提案在社区引起了热烈的讨论各方观点也很有意思。“实用派”叫好很多人认为这解决了实际问题让文档写作更自由。正如提案人所说这比以前试图让Example支持*testing.T或返回error的提案“更通用”且避免了复杂性问题比如// Output:如何与t.Log()交互。“完美主义派”的纠结一位开发者提出了一个“回退”困境。他用os.CreateTemp的例子指出如果按提案一个带error返回的、包含真实错误处理逻辑的示例将会失去在pkg.go.dev上那个珍贵的“Run”按钮即无法在 Playground 中一键运行。这确实是一个痛点。原来的代码里defer os.Remove(f.Name())这样的写法很自然但为了在文档中展示却不得不用一个匿名函数包裹只为了return error这增加了理解成本。“分离派”的解法另一位网友提出可以用一个“外部包裹函数”的技巧既让示例能跑又能测试。比如先写一个带*testing.T的ExampleXxx_t示例函数再用一个普通的TestExampleXxx测试函数去调用它。这样示例函数本身无法被go test运行的问题就被这个“测试包裹”绕过去了。在我看来这个提案是一个典型的 Go 式解决方案实用、可预测、且不增加新的“魔法”。它最大的价值是解开了束缚。它承认了“代码示例”的两个独立维度“展示性”Documentation和“可验证性”Testing。以前这两者是强绑定的导致很多好的例子无法展示。现在提案把选择权还给了开发者你可以写一个精美的、带参数和错误处理的示例它负责展示而你自己写的普通Test函数则负责验证它的正确性。至于那个“失去 Run 按钮”的担忧我认为是一个可以接受的折中。毕竟pkg.go.dev上的“Run”按钮是一个强大的便利功能但不是所有示例都需要它。对于复杂的、需要外部依赖或特定状态如文件系统操作的示例让它们安静地待在文档里提示用户“这只是展示用法而非可直接运行的代码”可能比强求一个“Run”按钮却带来更多复杂性更负责任。所以这个提案如果能落地它不会让现有的、无参数的Example函数失去什么反而为无法被“测试”的“示例”正了名赋予了它们合法的文档地位。对于开发者而言我们写文档时终于少了一道需要绕过去的坎。这本身就是一个值得称赞的进步。