第18节 go 模糊测试

❤️💕💕Go语言高级篇章,在此之前建议您先了解基础和进阶篇。Myblog:http://nsddd.topopen in new window

Go语言基础篇open in new window

Go语言100篇进阶open in new window


[TOC]

Why you need it?

提示

对测试进行一部分的增强,我们平常写代码后肯定需要一部分的单元测试,在原有基础上增强。

测试源

💡简单的一个案例如下:

package main

import "fmt"

func reverse[T string](s T) T {
	b := []byte(s)
	for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
		b[i], b[j] = b[j], b[i]
	}
	return T(b)
}

func main() {
	fmt.Println("反转之前:", "hello")
	fmt.Println("反转之后:", reverse("hello"))

	fmt.Println("反转之前:", "hello welcome to golang")
	fmt.Println("反转之后:", reverse("hello welcome to golang"))
}

🚀 编译结果如下:

[Running] go run "c:\Users\smile\Desktop\test\fuzz-demo\reverse.go"
反转之前: hello
反转之后: olleh
反转之前: hello welcome to golang
反转之后: gnalog ot emoclew olleh

需要测试

我们测试过上面的字符串是没有问题的,但是怎么知道测试其他的内容会不会出现问题呢?

我们需要单元测试:reverse_test.go

package main

import "fmt"

func Reverse[T string](s T) T {
	b := []byte(s)
	for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
		b[i], b[j] = b[j], b[i]
	}
	return T(b)
}

func main() {
	fmt.Println("反转之前:", "hello")
	fmt.Println("反转之后:", Reverse("hello"))

	fmt.Println("反转之前:", "hello welcome to golang")
	fmt.Println("反转之后:", Reverse("hello welcome to golang"))
}

🚀 编译结果如下:

补充

按理说我们只需要 go test 就可以执行测试用例,但是我们报错:

directory . is contained in a module that is not one of the workspace modules listed in go.work. You can add the module to the workspace using go work use .
# 目录中。包含在不是go.work中列出的工作空间模块之一的模块中。您可以使用go work use将模块添加到工作区。

我们如果要使用 go work 下的测试,我们需要加入到工作区:

 go work use .\fuzz-demo\

⚠️ 如果是工作区,我们可以直接在工作区测试:

go test mszlu.com/fuzz-demo
PS C:\Users\smile\Desktop\test\fuzz-demo> go test        
--- FAIL: TestReverse (0.00s)
    reverse_test.go:27: reverse("abc!def12321fedcba") == "abcdef12321fed!cba", want "abc!def12321fedcba"
FAIL
exit status 1
FAIL    nsddd.top/fuzz-demo     0.235s

是的,我留下了一个小bug,我们试着解决它:

{"abc!def12321fedcba", "abcdef12321fed!cba"},

🚀 编译结果如下:

PS C:\Users\smile\Desktop\test\fuzz-demo> go test
PASS
ok      nsddd.top/fuzz-demo     0.274s

:::

问题

即使我们看起来万无一失,但是实际上还是有个问题,当我们输入中文的时候,我们知道此时的byte[] 是很弱鸡的~

💡简单的一个案例如下:

package main

import (
	"fmt"
	"testing"
)

func test() {
	fmt.Println("这是fuzz-demo的reverse_test.go")
}

func TestReverse(t *testing.T) {
	testcases := []struct { //定义一个结构体数组
		in, want string //分别是输入和期望输出
	}{
		{"hello", "olleh"},
		{"hello welcome to golang", "gnalog ot emoclew olleh"},
		{"", ""},
		{"a", "a"},
		{"ab", "ba"},
		{"abc!", "!cba"},
		{"abc!def12321fedcba", "abcdef12321fed!cba"},
		{"我是中国人", "人中国是我"},
	}
	for _, c := range testcases {
		got := reverse(c.in)
		if got != c.want {
			t.Errorf("reverse(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

💡简单的一个案例如下:

PS C:\Users\smile\Desktop\test\fuzz-demo> go test
--- FAIL: TestReverse (0.00s)
    reverse_test.go:28: reverse("我是中国人") == "\xba\xba佛學䯘摈\xe6", want "人中国是我"
FAIL
exit status 1
FAIL    nsddd.top/fuzz-demo     0.264s

警告

当然,我们的目的不仅仅是为了解决它,而是说明我们的测试方法或许可以改进;

我们可能写不了那么多的测试用例,那么我们可以使用模糊测试了

模糊测试

💡简单的一个案例如下:

package main

import (
	"fmt"
	"testing"
	"unicode/utf8"
)

func test() {
	fmt.Println("这是fuzz-demo的reverse_test.go")
}

func TestReverse(t *testing.T) {
	testcases := []struct { //定义一个结构体数组
		in, want string //分别是输入和期望输出
	}{
		{"hello", "olleh"},
		{"hello welcome to golang", "gnalog ot emoclew olleh"},
		{"", ""},
		{"a", "a"},
		{"ab", "ba"},
		{"abc!", "!cba"},
		{"abc!def12321fedcba", "abcdef12321fed!cba"},
		// {"我是中国人", "人中国是我"},
	}
	for _, c := range testcases {
		got := Reverse(c.in)
		if got != c.want {
			t.Errorf("reverse(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

func FuzzReverse(f *testing.F) {
	// ...这是模糊测试的代码
	// 添加测试集
	testcases := []string{"hello", "hello world", "hello welcome to golang", " ", "123124"}
	for _, c := range testcases {
		//添加测试集
		f.Add(c)
	}

	f.Fuzz(func(t *testing.T, a string) {
		got := Reverse(a)
		inRevers := Reverse(got) //反转两次,应该和原来的一样(a == inRevers)
		if got == inRevers {
			t.Errorf("reverse(%q) == %q, want %q", a, got, a)
		}
		if utf8.ValidString(a) && !utf8.ValidString(got) {
			t.Errorf("Reverse produced invalid UTF-8 string: %q", got)
		}
	})
}

🚀 编译结果如下:

执行方法

执行所有 Fuzz 开头的模糊测试:

 go test -fuzz=Fuzz  # 执行所有 Fuzz 开头的模糊测试
PS C:\Users\smile\Desktop\test\fuzz-demo> go test -fuzz=Fuzz
--- FAIL: TestReverse (0.00s)
    reverse_test.go:29: reverse("我是中国人") == "\xba\xba佛學䯘摈\xe6", want "人中国是我"
FAIL
exit status 1
FAIL    nsddd.top/fuzz-demo     0.268s

END 链接