Theory

Why tests: go test and a first test

Every time you changed your app, you tested it the same way: run it, type some input, read the output, decide if it looks right. That works — until the app has ten functions and you'd have to re-check all of them after every change. You won't. So bugs sneak back in.

A test is code that checks other code, automatically. You write it once; go test runs it in a fraction of a second, as often as you like.

Go has testing built in — no library to install. Two conventions do all the work:

  1. Test code lives in a file whose name ends in _test.go (e.g. text_test.go). Go keeps it out of your normal build; it only compiles when you run tests.
  2. A test is a function named TestSomething that takes one argument, t *testing.T.

Here's a complete first test. Say your app has this function:

func double(n int) int {
    return n * 2
}

The test for it:

package main

import "testing"

func TestDouble(t *testing.T) {
    got := double(5)
    want := 10
    if got != want {
        t.Errorf("double(5) = %d, want %d", got, want)
    }
}

Read it plainly: call the function, compare got with want, and if they differ, call t.Errorf to report it. t.Errorf doesn't stop the program — it records the failure and lets the rest of the test keep running.

Run it from your project folder:

go test

If everything matches, you get a quiet, satisfying:

ok      yourapp   0.2s

That ok means every test passed. Want to see each one by name? Add -v (verbose):

go test -v

got/want is the whole style. Notice the error message says exactly what happened and what was expected: double(5) = 11, want 10. When a test fails months from now, that one line tells you what broke without opening the code. Always phrase failures this way.

Next: the pattern that lets one test check many cases at once — and we'll point it straight at a bug you've already met.