rapid 库需通过 go get github.com/leanovate/rapid 安装,其 Check 函数必须传入 *testing.T 并用 t.Fatal 处理返回的 error;generator 应用 Filter 限制有效范围,避免无效输入导致跳过或崩溃。

rapid 库的安装和基础断言写法
Go 本身没有内置 property-based testing 支持,rapid 是目前最主流、维护活跃的选择(由 quickcheck 思路演化而来)。它不依赖 go test 的默认驱动,而是提供独立的 rapid.Check 入口,需要显式调用。
安装:
go get github.com/leanovate/rapid
关键点:不能直接在 TestXxx 函数里只写 rapid.Check(...) 就完事——它会默默运行然后退出,不向 testing.T 报告失败。必须把 *testing.T 传进去,且需用 t.Fatal 或 t.Error 捕获错误:
-
rapid.Check返回error,不是布尔值;出错时返回非 nil 错误,需手动转成测试失败 - 不要漏掉
rapid.Shrink配置(默认开启),否则反例不会最小化,调试困难 - 测试函数签名必须是
func() bool或func() error,返回false或error即视为属性被证伪
如何定义有意义的 generator(生成器)
property-based 测试质量高度依赖 generator 是否覆盖边界、是否符合实际输入分布。rapid 提供了组合式构建方式,但容易写出“看似随机、实则无效”的数据。
常见陷阱:
- 用
rapid.Int()直接生成索引类参数,却没约束范围 → 触发 panic 或越界访问,这不是逻辑错误,是 generator 设计缺陷 - 对结构体字段分别用
rapid.String()和rapid.Int()组合,但没加.Filter()排除非法状态(如负年龄、空用户名)→ 大量测试用例被Skip掉,有效覆盖率暴跌 - 用
rapid.SliceOf(rapid.Int())生成切片,但未限制长度 → 可能生成百万元素切片,OOM 或超时
推荐写法:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
rapid.Check
- 运行时加
-seed参数(如go test -run=TestSort -args -seed=12345),或代码中硬编码rapid.Check(..., rapid.WithSeed(12345)) - 一旦发现失败,立刻从错误信息里提取
Seed:后的数值,用于复现 ——rapid的错误输出末尾一定带这个字段,别忽略它 - 如果用 CI 跑,建议固定 seed(比如取 CI job ID 的哈希),否则 flaky 问题无法归因
注意:rapid.WithMaxCount(1000) 和 rapid.WithMaxFailures(3) 可控执行规模,但不影响 seed 行为;seed 决定的是整个随机流,不是单次 draw。
哪些场景不适合 rapid,该换别的方案
不是所有逻辑都适合 property-based。当 generator 构建成本高、约束强、或预期输出本身难定义时,rapid 反而拖慢开发节奏。
典型信号:
- 你花了 20 分钟写
.Filter()链,只为生成一个合法的 HTTP 请求 body,而等价的手写 3 个例子只要 2 分钟 - 被测函数返回类型是
map[string][]*Node,你找不到简洁、可验证的 invariant(比如“key 数量 == value 中非 nil Node 总数”这种断言太弱) - 测试目标是“与旧版兼容”,但旧版行为本身就是一堆历史 bug,没法抽象成数学性质
这时退回基于用例的测试更高效。可保留 rapid 做 smoke test(比如只跑 100 次 + 简单 panic 检查),主逻辑仍靠 testify/assert 驱动明确输入输出对。
generator 的复杂度很容易悄悄超过被测逻辑本身——这是最常被忽略的维护成本。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










