Skip to main content
Unit testing is an important part of writing principled Go programs. The testing package provides the tools we need to write unit tests and the go test command runs tests.

Basic Testing

Go has a built-in testing framework that makes it easy to write and run tests. Test files are named with a _test.go suffix and contain functions that begin with Test.

Simple Test Example

Here’s a simple function and its test:
func IntMin(a, b int) int {
    if a < b {
        return a
    }
    return b
}
Test files typically live in the same package as the code they test. For example, code in intutils.go would have tests in intutils_test.go.

Test Function Requirements

A valid test function must:
1

Start with 'Test'

The function name must begin with Test followed by a capitalized word or phrase.
2

Accept *testing.T parameter

The function must accept a single parameter of type *testing.T.
3

Use t methods for reporting

Use t.Error* to report failures while continuing execution, or t.Fatal* to report failures and stop immediately.

Table-Driven Tests

Writing tests can be repetitive, so it’s idiomatic to use a table-driven style where test inputs and expected outputs are listed in a table and a single loop walks over them and performs the test logic.
func TestIntMinTableDriven(t *testing.T) {
    var tests = []struct {
        a, b int
        want int
    }{
        {0, 1, 0},
        {1, 0, 0},
        {2, -2, -2},
        {0, -1, -1},
        {-1, 0, -1},
    }

    for _, tt := range tests {
        testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
        t.Run(testname, func(t *testing.T) {
            ans := IntMin(tt.a, tt.b)
            if ans != tt.want {
                t.Errorf("got %d, want %d", ans, tt.want)
            }
        })
    }
}
Using t.Run enables running “subtests”, one for each table entry. These are shown separately when executing go test -v.

Running Tests

Use the go test command to run tests:
go test -v

Sample Output

=== RUN   TestIntMinBasic
--- PASS: TestIntMinBasic (0.00s)
=== RUN   TestIntMinTableDriven
=== RUN   TestIntMinTableDriven/0,1
=== RUN   TestIntMinTableDriven/1,0
=== RUN   TestIntMinTableDriven/2,-2
=== RUN   TestIntMinTableDriven/0,-1
=== RUN   TestIntMinTableDriven/-1,0
--- PASS: TestIntMinTableDriven (0.00s)
    --- PASS: TestIntMinTableDriven/0,1 (0.00s)
    --- PASS: TestIntMinTableDriven/1,0 (0.00s)
    --- PASS: TestIntMinTableDriven/2,-2 (0.00s)
    --- PASS: TestIntMinTableDriven/0,-1 (0.00s)
    --- PASS: TestIntMinTableDriven/-1,0 (0.00s)
PASS
ok      examples/testing-and-benchmarking    0.023s

Benchmarking

Benchmark tests measure the performance of your code. They follow a similar pattern to unit tests but use different naming and parameters.

Writing Benchmarks

Benchmark functions:
  • Must begin with Benchmark
  • Accept a *testing.B parameter
  • Run the code being benchmarked in a loop using b.Loop()
func BenchmarkIntMin(b *testing.B) {
    for b.Loop() {
        IntMin(1, 2)
    }
}
Any setup code that’s required for the benchmark to run but should not be measured goes before the b.Loop() loop. The benchmark runner will automatically execute the loop body many times to determine a reasonable estimate of the run-time of a single iteration.

Running Benchmarks

Use the -bench flag to run benchmarks:
go test -bench=.
The bench flag filters benchmark function names with a regexp. Use . to run all benchmarks.

Sample Benchmark Output

goos: darwin
goarch: arm64
pkg: examples/testing
BenchmarkIntMin-8 1000000000 0.3136 ns/op
PASS
ok      examples/testing-and-benchmarking    0.351s
  • BenchmarkIntMin-8: Benchmark name with GOMAXPROCS value (8)
  • 1000000000: Number of iterations run
  • 0.3136 ns/op: Time per operation in nanoseconds
  • Avoid setup code inside the benchmark loop
  • Use b.ResetTimer() if setup is necessary before timing
  • Run benchmarks multiple times to ensure consistency
  • Use -benchmem to include memory allocation statistics

Testing Methods

t.Error / t.Errorf

Report test failures but continue executing the test

t.Fatal / t.Fatalf

Report test failures and stop the test immediately

t.Run

Run subtests for table-driven tests or test grouping

t.Skip / t.Skipf

Skip the test with a message explaining why

Complete Example

package main

import (
    "fmt"
    "testing"
)

func IntMin(a, b int) int {
    if a < b {
        return a
    }
    return b
}

// Basic test
func TestIntMinBasic(t *testing.T) {
    ans := IntMin(2, -2)
    if ans != -2 {
        t.Errorf("IntMin(2, -2) = %d; want -2", ans)
    }
}

// Table-driven test
func TestIntMinTableDriven(t *testing.T) {
    var tests = []struct {
        a, b int
        want int
    }{
        {0, 1, 0},
        {1, 0, 0},
        {2, -2, -2},
        {0, -1, -1},
        {-1, 0, -1},
    }

    for _, tt := range tests {
        testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
        t.Run(testname, func(t *testing.T) {
            ans := IntMin(tt.a, tt.b)
            if ans != tt.want {
                t.Errorf("got %d, want %d", ans, tt.want)
            }
        })
    }
}

// Benchmark
func BenchmarkIntMin(b *testing.B) {
    for b.Loop() {
        IntMin(1, 2)
    }
}

Next Steps

Testing Package

Explore the full testing package documentation

Go Test Command

Learn about all go test command options

Build docs developers (and LLMs) love