Master

Testing

Write and run tests with Go's built-in testing framework.

What you will see

--- FAIL: TestMul (0.00s) main_test.go:9: expected 12, got 1

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

You will write and run tests with go test, add assertions that pass, and watch what a failing test reports back.

1

I start with a function, Add, that the test will verify. The test file imports the testing package and defines a TestAdd function.

package main import "testing" func Add(a, b int) int { return a + b }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
package builds cleanly
2

I add a test that checks Add(2, 3) equals 5. Running go test -v shows the test running and passing.

package main import "testing" func Add(a, b int) int { return a + b } func TestAdd(t *testing.T) { if got := Add(2, 3); got != 5 { t.Fatalf("expected 5, got %d", got) } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
=== RUN TestAdd --- PASS: TestAdd (0.00s)
t.Fatalf stops the test immediately on failure. Use t.Errorf when you want the test to keep running other checks.
3

I add a second test using Max and assert it with an equality check. go test reports the package as ok when all tests pass.

package main import "testing" func Max(a, b int) int { if a > b { return a } return b } func TestMax(t *testing.T) { if got := Max(3, 7); got != 7 { t.Fatalf("Max(3, 7) = %d, want 7", got) } if got := Max(10, 4); got != 10 { t.Fatalf("Max(10, 4) = %d, want 10", got) } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
ok
4

I introduce a bug: Add now returns the wrong answer. go test fails and prints the exact message I gave t.Fatalf, so I know what broke.

package main import "testing" func Add(a, b int) int { return a + b + 1 } func TestAdd(t *testing.T) { if got := Add(2, 3); got != 5 { t.Fatalf("expected 5, got %d", got) } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
--- FAIL: TestAdd (0.00s) main_test.go:9: expected 5, got 6
The line number in the message points at the t.Fatalf call, so fix Add to return a + b and the test passes again.
5

I change the failing check to use t.Errorf instead of t.Fatalf. The test still fails, but the message comes from t.Errorf, showing how the two helpers differ.

package main import "testing" func Mul(a, b int) int { return a - b } func TestMul(t *testing.T) { if got := Mul(4, 3); got != 12 { t.Errorf("expected 12, got %d", got) } }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
--- FAIL: TestMul (0.00s) main_test.go:9: expected 12, got 1
t.Errorf lets other tests keep running, while t.Fatalf stops after the first failure. Choose based on what you want to see.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A test runs a function, compares output to an expected value, and reports pass or fail.
1 go test discovers Test functions
↓
2 The function runs the code
↓
3 Assert the result matches expectation
↓
4 t.Errorf/Fatal records the outcome
Why this works
Test conventions

Go testing rules:

  • File ends in _test.go
  • Function starts with Test
  • Takes *testing.T
  • Use t.Log, t.Errorf, t.Fatalf

func TestSomething(t *testing.T) {}

go test ./... runs every test in the module.

Setup and teardown

Use t.Cleanup for teardown:

func TestDB(t *testing.T) {
    db := setup()
    t.Cleanup(func() { db.Close() })
    // ...
}

t.Cleanup runs registered functions when the test ends, in reverse order. It replaces manual teardown.

Examples

Writing a test

A basic test

func TestAdd(t *testing.T) {
    got := Add(2, 3)
    if got != 5 {
        t.Fatalf("expected 5, got %d", got)
    }
}

Tests live in *_test.go files and start with Test. t.Fatalf stops the test on failure.

Why it works: go test finds and runs every TestXxx function automatically.

Table-driven tests

Many cases at once

cases := []struct {
    a, b, want int
}{
    {1, 1, 2},
    {2, 3, 5},
}
for _, c := range cases {
    if got := Add(c.a, c.b); got != c.want {
        t.Errorf("Add(%d,%d)=%d, want %d", c.a, c.b, got, c.want)
    }
}

A table-driven test lists input/output cases and loops over them. It is the idiomatic Go testing style.

Benchmarks

Measuring speed

func TestBench(t *testing.B) {
    for i := 0; i < t.N; i++ {
        Add(1, 2)
    }
}

Benchmark* functions measure performance. Run with go test -bench ..

Why it works: benchmarks report operations per second so you can compare implementations.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Modules and Packages
Previous Lesson
Modules and Packages