» Make grep CLI App in Go » 2. Development » 2.4 Add Tests

Add Tests

The testing module provides a framework for writing and running tests. It encourages writing simple and expressive tests to ensure the correctness of your code.

In Go, test files are identified by their file names. The convention for naming test files is to use the _test.go suffix. This naming convention signals to the Go tooling that the file contains test code.

Test functions are functions whose names start with Test. They take a single argument of type *testing.T. You can use t.Error or t.Fail to indicate a test failure.

pkg/grep/search_test.go:

package grep

import (
	"os"
	"path/filepath"
	"reflect"
	"testing"
)

func TestGrep(t *testing.T) {
	// Create a temporary file for testing
	tmpfile, err := os.CreateTemp("", "example")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	// Write some content to the temporary file
	content := "line1\nline2\nline3\npattern\nline4"
	if _, err := tmpfile.Write([]byte(content)); err != nil {
		t.Fatal(err)
	}

	// Close the file before testing
	if err := tmpfile.Close(); err != nil {
		t.Fatal(err)
	}

	// Test Grep function
	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) {
	// Test cases for GrepCount function
	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) {
	// Create a temporary directory for testing
	tmpdir, err := os.MkdirTemp("", "example")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmpdir)

	// Create nested files and directories
	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)
		}

		// Write some content to the files
		content := "pattern"
		if err := os.WriteFile(filePath, []byte(content), os.ModePerm); err != nil {
			t.Fatal(err)
		}
	}

	// Test GrepRecursive function
	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)
	}
}

Tests are run using the go test command:

go test ./...

# Or -v for verbose output
go test -v ./...

You should get test results as below:

=== 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

If there's something wrong, you will get a detailed error messge like this:

=== 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

Then, you can fix your code based on this error message.

PrevNext