Go中的fuzz模糊测试使用实战详解
作者:Go学习日记
前言
软件系统越复杂,测试就越重要。然而手动测试可能会非常费时和乏味。这就是为什么自动化测试变的越来越流行的原因。今天我们来聊一聊自动化测试中的一种测试技术就是fuzz测试,它是一种随机测试技术,可以帮助发现软件系统中的漏洞和错误。
Go语言目前已经是非常流行的语言了,具有高效、并发等特性。今天我们浅谈使用Go语言进行fuzz测试。从Go1.18开始,Go在其标准工具链中支持模糊测试。
什么是fuzz测试
模糊测试 (fuzz testing, fuzzing)是一种软件测试技术。其核心思想是将自动或半自动生成的随机数据输入到一个程序中,并监视程序异常,如崩溃,断言(assertion)失败,以发现可能的程序错误,比如内存泄漏。模糊测试常常用于检测软件或计算机系统的安全漏洞。
Go语言中的模糊测试
Go为开发者提供了一套强大的工具,用于实施模糊测试,核心是testing
包中的Fuzz
函数。通过创建以Fuzz
为前缀的函数,按特定的签名定义输入输出,可以很容易集成模糊测试到我们的Go程序中。
上图是一个模糊测试的例子,突出显示了它的主要组件。
模糊测试必须要遵循的规则:
Fuzz测试必须是一个函数,函数名必须以"Fuzz"开头,后面必须是一个大写字母开头的名称,例如"FuzzAdd"。
Fuzz测试函数必须接受一个*testing.F类型的参数,不能有返回值。
Fuzz测试函数必须在*_test.go文件中定义才能运行。
Fuzz目标必须是一个方法调用,第一个参数必须是*testing.F类型,后面跟随着fuzz测试的参数。
每个fuzz测试函数只能有一个fuzz目标。
所有的种子语料库条目的类型必须与fuzz测试的参数类型相同,顺序也必须相同。这适用于对Fuzz函数的调用以及fuzz测试的testdata/fuzz目录中的任何语料库文件。
Fuzz测试的参数类型只能是以下类型:
string, []byte
int, uint, uintptr, int8, int16, int32, rune, int64, uint8, uint16, uint32, uint64
float32, float64
bool
这些规则和要求是Go语言fuzz测试的基本要求,遵循这些规则可以帮助我们编写有效的fuzz测试并提高测试覆盖率。
接下来我们通过一些简单代码来体验一下Fuzz test。
代码编写
我们创建一个名为fuzz的目录
> mkdir fuzz > cd fuzz > go mod init example/fuzz > touch main.go
在main.go中我们贴如以下代码
package main import "fmt" func Add(a, b int) int { return a + b } func main(){ fmt.Printf("Add(1, 2) = %d\n", Add(1, 2)) }
运行代码
go run . Add(1, 2) = 3
现在代码正常运行,接下来我们来测试它。
添加单元测试
我们先为Add函数编写一个基本的单元测试。
在
fuzz
目录创建一个文件为add_test.go
。
贴如以下代码到add_test.go
。
package main import "testing" func TestAdd(t *testing.T) { testcases := []struct { a, b, want int }{ {1, 2, 3}, {0, 0, 0}, {-1, -2, -3}, {1, -2, -1}, } for _, tc := range testcases { got := Add(tc.a, tc.b) if got != tc.want { t.Errorf("Add(%d, %d) == %d, want %d", tc.a, tc.b, got, tc.want) } } }
执行单元测试
> go test -v === RUN TestAdd --- PASS: TestAdd (0.00s) PASS ok example/fuzz 0.003s
添加模糊测试
单元测试的局限是我们必须把每个输入添加到测试中,模糊测试的好处是它提供了你的代码,并且可能识别你想出的测试用例的边缘情况。
我们把add_test.go中的单元测试替换为以下内容模糊测试。
func FuzzAdd(f *testing.F) { f.Add(1, 2) f.Add(0, 0) f.Add(-1, -2) f.Add(-2, -2) f.Fuzz(func(t *testing.T, a, b int) { got := Add(a, b) if got != a+b { t.Errorf("Add(%d, %d) == %d, want %d", a, b, got, a+b) } t.Logf("Add(%d, %d) == %d", a, b, got) }) }
接下来我们执行模糊测试, 通过-run
选项用于指定要运行的测试函数的正则表达式,-fuzz
选项用于指定要运行的fuzz测试函数的正则表达式,-fuzztime
选项来控制fuzz测试的持续时间。
> go test -v -run=FuzzAdd -fuzz=Fuzz -fuzztime 10s === RUN FuzzAdd fuzz: elapsed: 0s, gathering baseline coverage: 0/11 completed fuzz: elapsed: 0s, gathering baseline coverage: 11/11 completed, now fuzzing with 8 workers fuzz: elapsed: 3s, execs: 718882 (239620/sec), new interesting: 1 (total: 12) fuzz: elapsed: 6s, execs: 1444761 (241916/sec), new interesting: 1 (total: 12) fuzz: elapsed: 9s, execs: 2177414 (244215/sec), new interesting: 1 (total: 12) fuzz: elapsed: 10s, execs: 2415524 (216791/sec), new interesting: 1 (total: 12) --- PASS: FuzzAdd (10.10s) === NAME PASS ok example/fuzz 10.106s
总的来说,Go语言内置的fuzz测试框架提供了一种方便的方式来进行fuzz测试,它与testing包紧密集成,可以更方便的进行单元测试和fuzz测试。
以上就是Go中的fuzz模糊测试使用实战详解的详细内容,更多关于Go fuzz模糊测试的资料请关注脚本之家其它相关文章!