Package testing provides support for automated testing of Go packages. It is intended to be used with the “go test” command, which automates execution of test functions.
Writing Tests
Test Functions
Test functions must be named TestXxx where Xxx does not start with a lowercase letter. The function takes a single argument of type *testing.T.func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}
T Type Methods
T.Error
func (t *T) Error(args ...any)
Marks the test as failed and logs the arguments. The test continues execution.if got != want {
t.Error("values do not match")
}
T.Errorf
func (t *T) Errorf(format string, args ...any)
Like Error but formats the message according to a format specifier.if got != want {
t.Errorf("got %v, want %v", got, want)
}
Marks the test as failed but continues execution.if err != nil {
t.Fail()
}
Marks the test as failed and stops execution immediately.if db == nil {
t.FailNow()
}
T.Fatal
func (t *T) Fatal(args ...any)
Equivalent to Error followed by FailNow.db, err := OpenDB()
if err != nil {
t.Fatal("Failed to open database:", err)
}
T.Fatalf
func (t *T) Fatalf(format string, args ...any)
Equivalent to Errorf followed by FailNow.if err != nil {
t.Fatalf("setup failed: %v", err)
}
T.Log
func (t *T) Log(args ...any)
Logs the arguments. Output is only printed if the test fails or -v flag is set.t.Log("Starting database connection")
T.Logf
func (t *T) Logf(format string, args ...any)
Like Log but formats according to a format specifier.t.Logf("Processing item %d of %d", i, total)
T.Skip
func (t *T) Skip(args ...any)
Marks the test as skipped and stops execution.if runtime.GOOS != "linux" {
t.Skip("Skipping Linux-specific test")
}
T.Skipf
func (t *T) Skipf(format string, args ...any)
Like Skip but formats the message.if !hasFeature {
t.Skipf("Feature %s not available", featureName)
}
Subtests
T.Run
func (t *T) Run(name string, f func(*T)) bool
Runs a subtest with the given name. Useful for table-driven tests.func TestSum(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive", 2, 3, 5},
{"negative", -1, -2, -3},
{"zero", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Sum(tt.a, tt.b)
if got != tt.want {
t.Errorf("Sum(%d, %d) = %d; want %d",
tt.a, tt.b, got, tt.want)
}
})
}
}
Benchmarks
Benchmark
func BenchmarkXxx(*testing.B)
Benchmark functions must be named BenchmarkXxx and take a single argument of type *testing.B. Run with: go test -bench=.func BenchmarkFibonacci(b *testing.B) {
for b.Loop() {
Fibonacci(20)
}
}
B Type Methods
Reports whether the benchmark should continue running iterations. Modern way to write benchmarks.func BenchmarkAppend(b *testing.B) {
data := make([]int, 0)
for b.Loop() {
data = append(data, 1)
}
}
Resets the benchmark timer. Useful for excluding setup time from measurements.func BenchmarkExpensiveSetup(b *testing.B) {
// Expensive setup
data := generateLargeDataset()
b.ResetTimer() // Don't count setup time
for b.Loop() {
processData(data)
}
}
Starts the benchmark timer. The timer is initially running.
Stops the benchmark timer.for b.Loop() {
b.StopTimer()
// Setup for this iteration (not measured)
data := prepareData()
b.StartTimer()
processData(data) // Measured
}
B.RunParallel
func (b *B) RunParallel(body func(*PB))
Runs the benchmark in parallel.func BenchmarkParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
// Code to benchmark
ExpensiveOperation()
}
})
}
Memory Benchmarks
B.ReportAllocs
func (b *B) ReportAllocs()
Enables allocation statistics for the benchmark.func BenchmarkAllocations(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
_ = make([]byte, 1024)
}
}
B.SetBytes
func (b *B) SetBytes(n int64)
Records the number of bytes processed in a single operation.func BenchmarkRead(b *testing.B) {
data := make([]byte, 1024)
b.SetBytes(int64(len(data)))
for b.Loop() {
reader.Read(data)
}
}
Examples
Example functions demonstrate how to use a function or feature. The output comment verifies the example.func ExampleSum() {
fmt.Println(Sum(2, 3))
// Output: 5
}
func ExampleReverse() {
s := []int{1, 2, 3, 4, 5}
fmt.Println(Reverse(s))
// Output: [5 4 3 2 1]
}
Helper Functions
Marks the calling function as a test helper. When printing file and line information, that function will be skipped.func assertPositive(t *testing.T, n int) {
t.Helper()
if n <= 0 {
t.Errorf("expected positive number, got %d", n)
}
}
func TestValue(t *testing.T) {
assertPositive(t, 5) // Error will point to this line
}
T.Cleanup
func (t *T) Cleanup(f func())
Registers a function to be called when the test completes. Cleanup functions are called in LIFO order.func TestWithCleanup(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "test")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
os.RemoveAll(tmpDir)
})
// Use tmpDir in test
}
T.TempDir
func (t *T) TempDir() string
Creates a temporary directory for the test and arranges for it to be removed when the test completes.func TestFileOperations(t *testing.T) {
dir := t.TempDir()
// Use dir for test files
// Automatically cleaned up after test
}
Main
Can be used to set up and tear down before and after all tests. If TestMain is present, it will be called instead of running tests directly.func TestMain(m *testing.M) {
// Setup
fmt.Println("Setting up tests")
db := setupDatabase()
// Run tests
code := m.Run()
// Teardown
db.Close()
fmt.Println("Cleaning up")
os.Exit(code)
}
Test Examples
Basic Test
package math
import "testing"
func Add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}
func TestAddNegative(t *testing.T) {
result := Add(-1, -2)
if result != -3 {
t.Errorf("Add(-1, -2) = %d; want -3", result)
}
}
Table-Driven Tests
package math
import "testing"
func TestMultiply(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 6},
{"negative numbers", -2, -3, 6},
{"mixed signs", -2, 3, -6},
{"with zero", 5, 0, 0},
{"one", 7, 1, 7},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Multiply(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Multiply(%d, %d) = %d; want %d",
tt.a, tt.b, result, tt.expected)
}
})
}
}
Benchmarks
package strings
import (
"strings"
"testing"
)
// Modern benchmark using Loop()
func BenchmarkConcatLoop(b *testing.B) {
for b.Loop() {
var s string
for i := 0; i < 100; i++ {
s += "x"
}
}
}
func BenchmarkConcatBuilder(b *testing.B) {
for b.Loop() {
var builder strings.Builder
for i := 0; i < 100; i++ {
builder.WriteString("x")
}
_ = builder.String()
}
}
// With memory reporting
func BenchmarkMakeSlice(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
_ = make([]int, 1000)
}
}
// Parallel benchmark
func BenchmarkParallelWork(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
doExpensiveWork()
}
})
}
Test Helpers
package mypackage
import "testing"
func assertEqual(t *testing.T, got, want interface{}) {
t.Helper()
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}
func assertNoError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func TestWithHelpers(t *testing.T) {
result, err := DoSomething()
assertNoError(t, err)
assertEqual(t, result, "expected")
}
Using TempDir and Cleanup
package files
import (
"os"
"path/filepath"
"testing"
)
func TestFileOperations(t *testing.T) {
// Create temporary directory
dir := t.TempDir()
// Create a test file
filename := filepath.Join(dir, "test.txt")
err := os.WriteFile(filename, []byte("test data"), 0644)
if err != nil {
t.Fatal(err)
}
// Test reading the file
data, err := os.ReadFile(filename)
if err != nil {
t.Fatal(err)
}
if string(data) != "test data" {
t.Errorf("got %q, want %q", string(data), "test data")
}
// No cleanup needed - TempDir handles it
}
Example Functions
package greet
import "fmt"
func Greet(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
func ExampleGreet() {
fmt.Println(Greet("Alice"))
// Output: Hello, Alice!
}
func ExampleGreet_empty() {
fmt.Println(Greet(""))
// Output: Hello, !
}
Running Tests
# Run all tests
go test
# Run tests with verbose output
go test -v
# Run specific test
go test -run TestAdd
# Run tests matching pattern
go test -run "TestAdd.*"
# Run benchmarks
go test -bench=.
# Run specific benchmark
go test -bench=BenchmarkConcat
# Show memory allocations
go test -bench=. -benchmem
# Run with coverage
go test -cover
# Generate coverage report
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
# Run with race detector
go test -race
# Run tests in parallel
go test -parallel 4