Skip to main content

Small Rules, Big Difference

Good tests follow a few habits:

1. Clear names — TestGreetEmptyName says what broke without opening the file.
2. One behavior per test — small tests point straight at the problem.
3. Tables for many cases — one loop instead of copied tests.
4. Messages with got and want — a failure should explain itself.
5. t.Error to see everything, t.Fatal when continuing is pointless.
6. Run tests often — after every small change, not once at the end.
7. Tests live next to the code — same folder, _test.go suffix.


Failure Messages That Help

A failing test is read in a moment of confusion — the message must carry the whole story: which call, what came back, what was expected.

The Go convention is Func(input) = got; want want. With %q strings are quoted, so missing spaces or punctuation are visible right away.

A message with the whole story

Greet returns the wrong greeting. The failure message shows the call, the got and the want — the bug (wrong word, missing comma and exclamation mark) is visible in the output alone, without opening the code.

Prompt
Write a Go test file where a Greet function returns the wrong greeting ("Hi Ana" instead of "Hello, Ana!") and the test reports the failure in the Go convention: the call, the got value and the want value, using %q so the strings are quoted. Show the failing go test -v output.
Output: Go test file and captured go test output

One Behavior per Test

A test that checks five things has five reasons to fail — and its name can only describe one of them. Splitting by behavior keeps every test small and its name honest.

Here the normal greeting and the empty-name fallback are two behaviors, so they get two tests. When one fails, its name already tells you which behavior broke.

Two behaviors, two tests

TestGreetName checks the normal case, TestGreetEmpty the fallback. The verbose output lists them separately — a failure would point at exactly one behavior.

Prompt
Write a Go test file with a Greet function that greets by name and falls back to "Hello, stranger!" for an empty name. Write two separate focused tests, one per behavior, with names that say what they check. Show the passing go test -v output with both tests listed.
Output: Go test file and captured go test output

How Much Is Tested: Coverage

go test -cover measures which part of your code the tests actually execute, as a percentage of statements.

The number is a compass, not a goal: a low value shows untested paths — here the test only walks the mild branch, so the freezing and hot branches are never checked. 100% coverage does not prove the code is correct, but the uncovered lines are guaranteed unchecked.

Measure the coverage

Describe has three branches, the test exercises one. The coverage number reveals it — adding tests for the cold and hot branches would drive it up.

Prompt
Write a Go test file with a Describe function that returns freezing, mild or hot depending on a temperature, and a single test that only checks the mild branch. Show the go test -cover output with the coverage percentage.
Output: Go test file and captured go test output
Measure the coverage