用 Go 可执行示例,让文档自己跑测试!

简介: 本文揭秘Go语言“可执行示例”(Executable Examples)这一隐藏效率神器:用`Example*`函数将文档与测试合一,示例自动同步代码、支持在线运行、防文档过期。3分钟上手,零成本提升文档可信度与用户体验——偷懒,也可以很专业!

📝 一个"偷懒型"开发者的效率秘籍 | ⏱️ 阅读 4 分钟 | ✨ 文档即测试成就解锁


🎬 起因:我又被文档坑了

上周写了一个"字符串工具包",想给同事用。打开 pkg.go.dev 一看文档...

结果:❌ 示例代码是半年前的;❌ 注释说"返回大写",实际返回小写;❌ 同事照着抄,线上 panic 了。

🤦‍♂️ 那一刻我悟了:文档不跑测试,等于没写

于是我翻出了 Go 的"隐藏技能":可执行示例(Executable Examples)。用了一周,现在我的工具库文档,每个示例都能点"Run"直接跑,还能自动防过期。真香警告⚠️


🧱 第一步:3 分钟上手,把普通函数"变"成可运行示例

假设我写了个超简单的"字符串反转"函数:

// reverse/reverse.go
package reverse

func String(s string) string {
   
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
   
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}

✅ 操作步骤:

1️⃣ 在 _test.go 文件里写示例函数

// reverse/reverse_test.go
func ExampleString() {
   
    fmt.Println(String("hello"))
    // Output:
    // olleh
}

2️⃣ 关键点:// Output: 注释不是装饰!

  • 这是测试期望值,go test 会拿实际输出跟它比对
  • 输出不匹配?测试直接红,逼你更新文档或修代码

3️⃣ 跑起来验证

$ go test -v ./reverse
=== RUN   ExampleString
--- PASS: ExampleString (0.00s)
PASS

💡 爽点:这个示例会自动出现在 pkg.go.dev 的文档里,用户点"Run"就能在浏览器里试玩,不用装 Go 环境!


🎨 第二步:给常用工具函数加"活"文档

📌 示例 1:计算阶乘(带边界处理)

// factorial/factorial.go
func Calc(n int) (int, error) {
   
    if n < 0 {
   
        return 0, fmt.Errorf("negative number: %d", n)
    }
    if n <= 1 {
   
        return 1, nil
    }
    result := 1
    for i := 2; i <= n; i++ {
   
        result *= i
    }
    return result, nil
}
// factorial/factorial_test.go
func ExampleCalc_normal() {
   
    result, _ := Calc(5)
    fmt.Println(result)
    // Output:
    // 120
}

func ExampleCalc_error() {
   
    _, err := Calc(-3)
    fmt.Println(err)
    // Output:
    // negative number: -3
}

技巧:一个函数可以写多个示例,用 _ 后缀区分,文档里会并列展示,用户一眼看懂正常/异常场景。

📌 示例 2:处理"无序输出"的场景

切片去重后,如果用 map 做中间存储,遍历顺序不确定,怎么办?

// unique/unique.go
func Ints(nums []int) []int {
   
    seen := make(map[int]bool)
    var result []int
    for _, n := range nums {
   
        if !seen[n] {
   
            seen[n] = true
            result = append(result, n)
        }
    }
    return result
}
// unique/unique_test.go
func ExampleInts() {
   
    nums := []int{
   3, 1, 2, 1, 3}
    unique := Ints(nums)
    // 排序后再输出,保证顺序固定
    sort.Ints(unique)
    fmt.Println(unique)
    // Output:
    // [1 2 3]
}

✅ 如果实在不想排序,也可以加 // Unordered output:,测试只检查"这些值都有",不关心顺序。


📦 第三步:进阶玩法,示例也能"组团"

🔹 给整个包写"入门示例"

// 不带函数名,就是包级示例
// mathutil/mathutil_test.go
func Example() {
   
    // 演示工具包常用组合
    a := Add(2, 3)        // 5
    b := Multiply(a, 4)   // 20
    fmt.Println(IsEven(b))
    // Output:
    // true
}

🔹 给类型和方法写示例

// counter/counter.go
type Counter struct {
   
    count int
}

func (c *Counter) Inc() {
   
    c.count++
}

func (c *Counter) Value() int {
   
    return c.count
}
// counter/counter_test.go
func ExampleCounter_basic() {
   
    var c Counter
    c.Inc()
    c.Inc()
    fmt.Println(c.Value())
    // Output:
    // 2
}

func ExampleCounter_reset() {
   
    c := Counter{
   count: 10}
    c.count = 0  // 直接重置内部字段(演示用,生产环境建议加 Reset() 方法)
    fmt.Println(c.Value())
    // Output:
    // 0
}

🎯 命名口诀:Example + 类型名 + _ + 方法名,文档工具自动关联,不用手动配置。


💡 第四步:集成到我的开发流

🔄 本地开发:示例即测试

# 每次改代码,跑测试顺便验证文档
$ go test ./... -run Example
# ✅ 所有示例通过,文档放心发布

🚀 CI/CD:防"文档腐烂"

# .github/workflows/test.yml
- name: Verify examples
  run: go test -run Example ./...
# 💥 示例挂了?合并请求直接拦下,逼你更新文档

🌐 线上文档:用户能"玩"的代码

  • 用户访问 pkg.go.dev/我的工具库
  • 看到示例 → 点"Run" → 浏览器里直接看效果
  • 不用 git clone,不用 go install,零门槛体验函数行为

🎯 复盘:为什么我强烈安利这个功能?

✅ 真香点 💡 适用场景
📚 文档自动同步代码,永不"过期" 写工具库/算法包,希望用户快速上手
🧪 示例即测试,改代码先过文档关 团队协作,减少"文档谁维护"的扯皮
🌍 用户浏览器里直接试玩,降低使用门槛 开源项目,需要大量代码演示
⚡ 写一次,两用(文档+测试),效率翻倍 个人项目,想偷懒但又要专业感

🤔 我的血泪经验:以前写文档像"写情书",写完就忘;现在写示例像"写剧本",每次跑测试都是"彩排",上线自然稳。


🎬 尾声:让代码自己"说话"

上周有同事问:"这个 UniqueInts 函数,输入空切片会返回 nil 还是空切片?"

以前我得:查代码→写回复→贴截图→等反馈。

这次我直接回:"点这个示例链接,改输入为 []int{} 点 Run,看输出👇"

30 秒后同事回复:"👍 懂了,返回空切片,文档真方便!"

那一刻我觉得:最好的文档,不是写得最漂亮的,而是用户能亲手"玩"起来的。


相关文章
|
2月前
|
运维 Kubernetes 监控
Kubernetes Pod 重启之谜:何时重启?何时静默?
配置更新后Pod无反应?真相是:kubelet只监听Pod Spec变更,ConfigMap/Secret内容变化不会触发重启。环境变量注入不可热更,卷挂载需应用主动监听;UID变=重建,UID不变+重启计数增=容器重启。精准判断,避免误操作。(239字)
227 1
|
3月前
|
Rust 中间件 API
BustAPI:当 Python 遇上 Rust,Web 框架也能“起飞“
BustAPI 是融合 Python 易用性与 Rust 高性能的 Web 框架:基于 PyO3 封装 Actix-Web,保留 Flask 风格语法,请求性能提升 10–50 倍;支持自动文档、类型校验、异步、中间件等生产级功能,迁移零成本,部署极简——让 Python 服务轻松应对高并发。
417 5
|
图形学
Unity 3D游戏-消消乐(三消类)教程和源码
Unity 消消乐教程和源码 本文提供全流程,中文翻译。Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) ...
6613 0
|
30天前
|
弹性计算 数据挖掘 测试技术
阿里云服务器带宽怎么选?1M/3M/5M/10M价格介绍与选购省钱技巧
本文介绍了2026年阿里云服务器带宽的计费方式与选购策略。阿里云提供固定带宽(包月/包年)和按流量计费两种模式,带宽独享且价格随档位非线性增长。以热门的经济型e实例(99元/年,3M固定带宽)和通用算力型u1实例(199元/年,5M固定带宽)为例,详细对比了1M至10M带宽的年付成本差异。文章建议用户根据业务流量模型合理选配,优先通过官方活动入口购买以享折扣,善用优惠券实现折上折,并选择"续费同价"产品保障长期成本可控,避免因初期选择低带宽而导致后期付出更高升级代价。
|
2月前
|
存储 弹性计算 运维
无服务器计算简单介绍
无服务器计算(FaaS)让开发者专注业务逻辑,云厂商托管服务器配置、运维与扩缩容。函数由事件触发(如HTTP请求、文件上传、定时任务),自动弹性伸缩,按需付费。但存在冷启动延迟、无状态、厂商锁定及调试复杂等挑战。(239字)
164 3
|
2月前
|
缓存 运维 安全
Go并发生产实践:从“能跑就行“到“稳如老狗“的进阶之路
本文以真实运维事故切入,系统梳理Go并发开发五大避坑法则、原语选型指南及实用工具封装,强调“并发不是魔法,而是责任”。聚焦可测试性、资源安全与优雅终止,倡导用`errgroup`替代裸`go`、用`context`实现紧急制动、用封装隐藏复杂性,助你写出稳健、可维护的生产级并发代码。(239字)
184 3
|
2月前
|
安全 Java 编译器
Java 很快! 可能是你的代码在“摸鱼“
同一应用、同一测试、同一JDK,未改架构——响应从1198ms降至239ms,吞吐量飙升5倍至41.9万单/秒!揭秘8个Java性能隐形杀手:字符串拼接、Stream滥用、String.format误用、自动装箱、异常控流、锁粒度大、对象重复创建、虚拟线程阻塞等,并附实战优化方案。(239字)
125 1
|
3月前
|
人工智能 Linux API
OpenClaw(Clawdbot)保姆级部署手册:无影云电脑/本地全平台+千问/Coding Plan+企业微信集成指南
OpenClaw(曾用名Clawdbot、Moltbot)是一款轻量化、可扩展的多智能体协作框架,支持本地与云端部署,可对接主流大模型API,实现任务调度、智能协作与多IM平台接入。本文基于2026年最新稳定版,完整覆盖阿里云无影云电脑部署、本地MacOS/Linux/Windows11部署、企业微信接入、阿里云千问大模型API配置、免费Coding Plan API配置全流程,提供可直接复制的命令与配置,同时梳理常见问题与避坑方案,助力零基础用户快速搭建稳定可用的AI协作系统。
752 6
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
从达特茅斯到2026,AI的迭代之路与时代变革
当斯坦福大学在2026年4月发布的AI指数报告中写下“AI在加速,但衡量和管好它的能力,几乎没有跟上”这句话时,全球科技界再次感受到了人工智能发展的磅礴力量与潜在挑战。
351 0
|
4月前
|
Rust Java 编译器
Rust 和 Go 的内存管理:一个像管家,一个像房东
本文用“租房”比喻生动对比Go与Rust内存管理:Go靠GC自动回收(房东定期打扫),开发快但偶有停顿;Rust靠所有权系统在编译期确保安全(管家严控借还),零开销却学习陡峭。选Go求效率,选Rust要极致稳定与性能。(239字)
308 1