» Go创建命令行程序grep » 2. 开发 » 2.4 编写测试

编写测试

testing 模块提供了一个编写和运行测试的框架。它鼓励开发者编写简单而富有表达力的测试,以确保代码正确性。

在 Go 中,测试文件通过它们的文件名来识别。命名测试文件的约定是使用 _test.go 后缀。

测试函数是以 Test 开头的函数,它们接受一个类型为 *testing.T 的单一参数。你可以使用 t.Errort.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

然后,你可以根据错误信息修复代码实现中的问题。