Testing Fundamentals
Overview
Go has built-in testing support. Test files end with _test.go and use the testing package.
Writing Tests
// math.go
package math
func Add(a, b int) int {
return a + b
}// math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}Running Tests
go test # Current package
go test ./... # All packages
go test -v # Verbose
go test -run TestAdd # Specific testTest Functions
func TestXxx(t *testing.T) // Test
func BenchmarkXxx(b *testing.B) // Benchmark
func ExampleXxx() // Example
func FuzzXxx(f *testing.F) // Fuzz testt.Error vs t.Fatal
func TestExample(t *testing.T) {
// Continues after failure
t.Error("failed but continuing")
// Stops test immediately
t.Fatal("failed, stopping now")
}Helper Functions
func assertEqual(t *testing.T, got, want int) {
t.Helper() // Marks this as helper
if got != want {
t.Errorf("got %d, want %d", got, want)
}
}
func TestMath(t *testing.T) {
assertEqual(t, Add(1, 2), 3)
}Setup and Teardown
func TestMain(m *testing.M) {
// Setup
setup()
code := m.Run() // Run tests
// Teardown
teardown()
os.Exit(code)
}Parallel Tests
func TestParallel(t *testing.T) {
t.Parallel() // Run in parallel
// Test code
}Summary
| Command | Purpose |
|---|---|
go test |
Run tests |
go test -v |
Verbose output |
go test -cover |
Show coverage |
go test -race |
Race detection |
Worked example
t.Helper, Error vs Fatal, and TestMain setup/teardown.
Save as strutil.go and strutil_test.go. Then:
go mod init example
go test -v// strutil.go
package main
import "strings"
func TitleWords(s string) string {
parts := strings.Fields(s)
for i, p := range parts {
if p == "" {
continue
}
parts[i] = strings.ToUpper(p[:1]) + strings.ToLower(p[1:])
}
return strings.Join(parts, " ")
}// strutil_test.go
package main
import (
"fmt"
"os"
"testing"
)
func TestMain(m *testing.M) {
fmt.Println("setup: suite start")
code := m.Run()
fmt.Println("teardown: suite end")
os.Exit(code)
}
func assertEq(t *testing.T, got, want string) {
t.Helper()
if got != want {
t.Errorf("got %q want %q", got, want)
}
}
func TestTitleWords(t *testing.T) {
assertEq(t, TitleWords("hello world"), "Hello World")
assertEq(t, TitleWords("gO"), "Go")
}
func TestTitleWordsFatalStops(t *testing.T) {
if TitleWords("") != "" {
t.Fatal("empty should stay empty") // would stop this test
}
// Continues only if Fatal did not fire.
assertEq(t, TitleWords("a b"), "A B")
}Expected output:
setup: suite start
=== RUN TestTitleWords
--- PASS: TestTitleWords (0.00s)
=== RUN TestTitleWordsFatalStops
--- PASS: TestTitleWordsFatalStops (0.00s)
PASS
teardown: suite end
More examples
Parallel top-level tests (t.Parallel) on independent pure functions.
// parallel_test.go
package main
import "testing"
func TestTitleHello(t *testing.T) {
t.Parallel()
if TitleWords("hello") != "Hello" {
t.Fatal("bad title")
}
}
func TestTitleWorld(t *testing.T) {
t.Parallel()
if TitleWords("world") != "World" {
t.Fatal("bad title")
}
}go test -v -parallel 4Runnable example
Save these two files in a directory (for example mathx/). Then:
cd mathx
go mod init example/mathx
go test -v
go test -covermathx.go:
package mathx
func Add(a, b int) int {
return a + b
}
func Div(a, b int) (int, error) {
if b == 0 {
return 0, errDivZero
}
return a / b, nil
}
var errDivZero = errString("division by zero")
type errString string
func (e errString) Error() string { return string(e) }mathx_test.go:
package mathx
import "testing"
func assertEqual(t *testing.T, got, want int) {
t.Helper()
if got != want {
t.Errorf("got %d, want %d", got, want)
}
}
func TestAdd(t *testing.T) {
assertEqual(t, Add(2, 3), 5)
assertEqual(t, Add(-1, 1), 0)
}
func TestDiv(t *testing.T) {
got, err := Div(10, 2)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
assertEqual(t, got, 5)
_, err = Div(1, 0)
if err == nil {
t.Fatal("expected error for divide by zero")
}
}
func TestAddParallel(t *testing.T) {
t.Parallel()
assertEqual(t, Add(10, 5), 15)
}Expected output: (verbose)
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
=== RUN TestDiv
--- PASS: TestDiv (0.00s)
=== RUN TestAddParallel
--- PASS: TestAddParallel (0.00s)
PASS
ok example/mathx 0.00xs
What to notice: Tests live in *_test.go, use TestXxx(*testing.T), and fail with t.Error / t.Fatal. t.Helper() makes failure lines point at the call site, not the helper body.
Try next: Add go test -run TestDiv -v; introduce a deliberate bug in Add and watch the assertion fail; try t.Error vs t.Fatal after a failed check.