Go 单元测试:用表驱动用例检查输入和边界
为折扣计算定义明确规则,并用正常、边界、舍入和无效输入验证实现。练习从业务约定推导测试,而不是照抄实现。
本节目标
为折扣计算定义明确规则,并用正常、边界、舍入和无效输入验证实现。练习从业务约定推导测试,而不是照抄实现。
前置知识
掌握函数、error、整数计算及模块运行。测试文件与main.go放在同一目录,执行go test而不是go run测试文件。
准备与运行
在一个新的练习目录执行以下命令。目录名可以修改;若该目录已经初始化过模块,不要重复执行go mod init。
mkdir go-lesson-12
cd go-lesson-12
go mod init example.com/devcn/lesson12将下方完整代码保存为当前目录的main.go,然后执行:
go fmt ./...
go run .先用go version确认工具链可用。本例在Go 1.25.7、macOS ARM64环境验证,只使用标准库;没有测试所有操作系统和Go版本。
核心概念
先规定接口:cents是整数分,percent是实际支付比例,80表示支付原价的80%,不是减掉80%。金额不得为负,支付比例限定在0到100之间。结果不足一分的部分向下截断。
整数分能避免直接用二进制浮点表示小数金额带来的部分精度问题,但不是完整的财务计算方案。金额上限、货币单位和舍入政策仍应由业务明确,本例只处理能够安全完成整数乘法的练习数据。
表驱动测试把案例集中为输入、预期数值与是否报错。每个子测试有名称,失败时能快速定位是哪条约定没有满足。代码覆盖率可以辅助检查,但不能代替合理的预期结果。
完整示例
package main
import (
"errors"
"fmt"
)
func discounted(cents, percent int) (int, error) {
if cents < 0 || percent < 0 || percent > 100 {
return 0, errors.New("invalid input")
}
return cents * percent / 100, nil
}
func main() {
value, err := discounted(2000, 80)
if err != nil {
fmt.Println("计算失败:", err)
return
}
fmt.Println(value)
}运行后应得到:
1600代码如何工作
discounted先检查无效范围,再计算价格。main演示2000分按80%支付得到1600分。测试则额外覆盖0金额、0%支付、100%支付以及不合法的负数和超范围比例。
测试文件名以_test.go结尾,函数名以Test开头并接收*testing.T。go test识别测试文件,运行各子测试。每条用例同时比较数值与错误状态,防止函数返回正确数值却带错误,或错误路径被误当成成功。
运行后应看到所有子测试PASS。再故意把实现中的/100改为/10,测试应失败;恢复代码后重新通过。这个实验比只看到绿色结果更能说明测试是否真的保护了约定。
测试文件与执行
将以下代码保存为同目录的main_test.go,再执行go test -v ./...。预期8个子测试全部通过;命令输出的耗时和包路径因环境而异。
package main
import "testing"
func TestDiscounted(t *testing.T) {
cases := []struct {
name string
cents, percent, want int
bad bool
}{
{"normal", 2000, 80, 1600, false},
{"zero_amount", 0, 80, 0, false},
{"free", 100, 0, 0, false},
{"full", 100, 100, 100, false},
{"negative_amount", -1, 80, 0, true},
{"negative_percent", 100, -1, 0, true},
{"too_large_percent", 100, 101, 0, true},
{"rounding", 101, 50, 50, false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, err := discounted(tc.cents, tc.percent)
if (err != nil) != tc.bad || got != tc.want {
t.Fatalf("got %d, %v; want %d, bad=%v", got, err, tc.want, tc.bad)
}
})
}
}常见错误与边界
- 测试期望值不要直接调用被测函数计算,否则错误实现可能同时生成错误答案。
- 0%是合法边界,负比例才是无效输入,两者应分别测试。
- cents*percent对超大整数可能溢出。本例明确限制为练习数据,不能直接用于无上限的金额输入。
- “101分按50%支付得到50分”是本例的截断规则,其他产品可能选择四舍五入,需另定约定。
练习
新增1999分按75%支付的测试用例。先手算结果,再加入用例,不要修改已有的101分舍入案例。
参考答案
1999 × 75 = 149925,整数除以100得到1499。因此新增案例为:
{"three_quarters", 1999, 75, 1499, false},把它加入测试表后重新执行go test -v ./...。若结果为1500,先核对是否有人把业务规则改成了四舍五入,而不是立即修改测试预期。
文章回复
0 条公开回复