模糊测试入门 (Fuzzing)

大家好,我是极客老墨。 单元测试只能测试你想到的情况。空字符串、负数、超长输入,这些边界情况很容易遗漏。模糊测试能自动生成大量随机输入,帮你发现那些没想到的 Bug。 这篇就聊聊 Go 的模糊测试,看看它是怎么帮我们提升代码质量的。 什么是模糊测试 模糊测试(Fuzzing)是一种自动化测试技术,通过生成随机输入来发现程序的异常。 核心思想 1// 传统单元测试:测试已知输入 2func TestAdd(t *testing.T) { 3 if Add(2, 3) != 5 { 4 t.Error("2 + 3 should be 5") 5 } 6 if Add(0, 0) != 0 { 7 t.Error("0 + 0 should be 0") 8 } 9} 10 11// 模糊测试:测试大量随机输入 12func FuzzAdd(f *testing.F) { 13 f.Fuzz(func(t *testing.T, a, b int) { 14 result := Add(a, b) 15 // 检查属性而不是具体值 16 if a > 0 && b > 0 && result <= 0 { 17 t.Errorf("Add(%d, %d) = %d, overflow?", a, b, result) 18 } 19 }) 20} 区别: 单元测试:测试已知的输入和输出 模糊测试:测试未知的输入,检查程序属性 能发现什么问题 崩溃和 panic:空指针、数组越界 整数溢出:加法、乘法溢出 无限循环:特定输入导致死循环 内存泄漏:特定输入导致内存不释放 逻辑错误:边界条件处理不当 编写第一个 Fuzz 测试 从一个简单的例子开始。 被测函数 1// reverse.go 2package stringutil 3 4// Reverse 反转字符串 5func Reverse(s string) string { 6 b := []byte(s) 7 for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { 8 b[i], b[j] = b[j], b[i] 9 } 10 return string(b) 11} Fuzz 测试 1// reverse_test.go 2package stringutil 3 4import "testing" 5 6func FuzzReverse(f *testing.F) { 7 // 添加种子输入(可选) 8 f.Add("hello") 9 f.Add("世界") 10 f.Add("") 11 12 // Fuzz 函数 13 f.Fuzz(func(t *testing.T, s string) { 14 // 属性:反转两次应该等于原字符串 15 reversed := Reverse(s) 16 doubleReversed := Reverse(reversed) 17 18 if s != doubleReversed { 19 t.Errorf("Reverse(Reverse(%q)) = %q, want %q", 20 s, doubleReversed, s) 21 } 22 }) 23} 要点: ...

2025-08-20 · 8 min · 1540 words · 老墨

[GoLang避坑实战-14] 写得爽,跑得快:表格驱动测试的工程级避坑实战

大家好,我是极客老墨! 刚转 Go 的时候,我还在找"Go 版的 JUnit 在哪"。结果发现,Go 根本不需要第三方测试框架,go test 命令 + testing 包就够了。 写测试不只是为了完成 KPI,更是为了**“让自己晚上能睡个好觉”**。这篇我们不仅聊基础,更要聊聊在大厂工程实践中,如何写出既稳健又专业的测试。 Go中的测试,不是"一等公民",是"超等公民"。Go 编译器自带 go test 工具,标准库提供 testing 包,写测试简单到爆。 1. 单元测试:从入门到专业 Go 的测试文件规则很简单:以 _test.go 结尾,函数名以 Test 开头。 1.1 基础写法 1func TestAdd(t *testing.T) { 2 if got := Add(1, 2); got != 3 { 3 t.Errorf("Add(1, 2) = %d; want 3", got) 4 } 5} 跑一下: 1$ go test -v 2=== RUN TestAdd 3--- PASS: TestAdd (0.00s) 4PASS 5ok example.com/math 0.392s 就这么简单。不需要装任何库,不需要配置文件。 1.2 进阶:表格驱动(标配) 这是 Go 社区的灵魂写法。把数据和逻辑分离,增加测试用例只需在切片里加一行,这就是“表格驱动”。 1func TestAddTableDriven(t *testing.T) { 2 // 1. 定义测试用例 3 tests := []struct { 4 name string // 用例名称 5 a, b int // 输入 6 want int // 期望结果 7 }{ 8 {"正数", 1, 2, 3}, 9 {"负数", -1, -2, -3}, 10 {"混合", -1, 1, 0}, 11 {"零", 0, 0, 0}, 12 } 13 14 // 2. 遍历执行 15 for _, tt := range tests { 16 // t.Run 启动子测试,方便定位错误 17 t.Run(tt.name, func(t *testing.T) { 18 got := Add(tt.a, tt.b) 19 if got != tt.want { 20 t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want) 21 } 22 }) 23 } 24} 为什么推荐表格驱动? ...

2025-02-19 · 3 min · 491 words · 老墨