编写测试
testing
模块提供了一个编写和运行测试的框架。它鼓励开发者编写简单而富有表达力的测试,以确保代码正确性。
在 Go 中,测试文件通过它们的文件名来识别。命名测试文件的约定是使用 _test.go
后缀。
测试函数是以 Test
开头的函数,它们接受一个类型为 *testing.T
的单一参数。你可以使用 t.Error
或 t.Fail
来指示测试未通过。
pkg/grep/search_test.go:
package grep
import (
"os"
"path/filepath"
"reflect"
"testing"
)
func TestGrep(t *testing.T) {
// 创建用于测试的临时文件
tmpfile, err := os.CreateTemp("", "example")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpfile.Name())
// 将内容写入到临时文件
content := "line1\nline2\nline3\npattern\nline4"
if _, err := tmpfile.Write([]byte(content)); err != nil {
t.Fatal(err)
}
// 关闭文件
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
// 测试 Grep 函数
pattern := "pattern"
options := &Options{}
result, err := Grep(pattern, tmpfile.Name(), options)
if err != nil {
t.Fatal(err)
}
expectedResult := MatchResult{
tmpfile.Name(): {
{LineNumber: 4, Line: "pattern"},
},
}
if !reflect.DeepEqual(result, expectedResult) {
t.Errorf("Expected %v, but got %v", expectedResult, result)
}
}
func TestGrepCount(t *testing.T) {
// GrepCount 函数的测试用例
t.Run("EmptyResult", func(t *testing.T) {
result := make(MatchResult)
count := GrepCount(result)
expectedCount := 0
if count != expectedCount {
t.Errorf("Expected count %v, but got %v", expectedCount, count)
}
})
t.Run("NonEmptyResult", func(t *testing.T) {
result := MatchResult{
"file1.txt": {
{LineNumber: 1, Line: "pattern"},
{LineNumber: 5, Line: "pattern"},
},
"file2.txt": {
{LineNumber: 3, Line: "pattern"},
},
}
count := GrepCount(result)
expectedCount := 3
if count != expectedCount {
t.Errorf("Expected count %v, but got %v", expectedCount, count)
}
})
}
func TestGrepRecursive(t *testing.T) {
// 创建临时目录用于测试
tmpdir, err := os.MkdirTemp("", "example")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
// 创建嵌套的文件和目录
files := []string{
"file1.txt",
"file2.txt",
"subdir/file3.txt",
"subdir/file4.txt",
}
for _, file := range files {
filePath := filepath.Join(tmpdir, file)
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
t.Fatal(err)
}
// 讲内容写入到文件里
content := "pattern"
if err := os.WriteFile(filePath, []byte(content), os.ModePerm); err != nil {
t.Fatal(err)
}
}
// 测试 GrepRecursive 函数
pattern := "pattern"
options := &Options{}
result, err := GrepRecursive(pattern, tmpdir, options)
if err != nil {
t.Fatal(err)
}
expectedResult := MatchResult{
filepath.Join(tmpdir, "file1.txt"): {
{LineNumber: 1, Line: "pattern"},
},
filepath.Join(tmpdir, "file2.txt"): {
{LineNumber: 1, Line: "pattern"},
},
filepath.Join(tmpdir, "subdir/file3.txt"): {
{LineNumber: 1, Line: "pattern"},
},
filepath.Join(tmpdir, "subdir/file4.txt"): {
{LineNumber: 1, Line: "pattern"},
},
}
if !reflect.DeepEqual(result, expectedResult) {
t.Errorf("Expected %v, but got %v", expectedResult, result)
}
}
使用 go test
命令执行测试:
go test ./...
# 或者,用 -v (verbose)显示详细输出
go test -v ./...
应该得到如下结果:
=== RUN TestGrep
--- PASS: TestGrep (0.00s)
=== RUN TestGrepCount
=== RUN TestGrepCount/EmptyResult
=== RUN TestGrepCount/NonEmptyResult
--- PASS: TestGrepCount (0.00s)
--- PASS: TestGrepCount/EmptyResult (0.00s)
--- PASS: TestGrepCount/NonEmptyResult (0.00s)
=== RUN TestGrepRecursive
--- PASS: TestGrepRecursive (0.00s)
PASS
ok github.com/Literank/gorep/pkg/grep
如果遇到错误,你会看到具体的报错信息,如下所示:
=== RUN TestGrep
search_test.go:43: Expected map[/var/folders/wx/hdjympxj7h3_ntwgzbhddyr40000gn/T/example236042624:[0xc0000b40c0]], but got map[/var/folders/wx/hdjympxj7h3_ntwgzbhddyr40000gn/T/example236042624:[0xc0000b40a8]]
--- FAIL: TestGrep (0.00s)
=== RUN TestGrepCount
=== RUN TestGrepCount/EmptyResult
=== RUN TestGrepCount/NonEmptyResult
--- PASS: TestGrepCount (0.00s)
--- PASS: TestGrepCount/EmptyResult (0.00s)
--- PASS: TestGrepCount/NonEmptyResult (0.00s)
=== RUN TestGrepRecursive
--- PASS: TestGrepRecursive (0.00s)
FAIL
FAIL github.com/Literank/gorep/pkg/grep 1.649s
FAIL
然后,你可以根据错误信息修复代码实现中的问题。