Testing

By the end of this lesson you'll write real Go tests with the built-in testing package — single tests, table-driven tests with subtests, and benchmarks — and run them with go test . No third-party libraries needed; it's all in the standard library.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ Your First Test

Go's test runner is built into the language — there's no framework to install. Three conventions are all you need: the file name ends in _test.go , each test function is named Test + a capitalised name, and it takes a single t *testing.T . Go has no assert keyword: you compare the values yourself and call a method on t when something's wrong. Read this worked example, then run it.

2️⃣ Reporting Failures: t.Errorf vs t.Fatalf

When a check fails you tell the test runner with a method on t . t.Errorf records the failure but keeps running the rest of the function, so one run can surface several issues. t.Fatalf records it and stops the test right there — use it after setup or an error check, when the lines below would otherwise crash. Both take a printf -style format string.

Now you try. The test below is almost complete — fill in the two blanks marked ___ using the hints, then run it. You're writing the expected value and the comparison.

3️⃣ Table-Driven Tests with Subtests

This is the pattern you'll write most in Go. Instead of copy-pasting a near-identical test for every input, you describe the cases as data in a slice of structs, then loop over them. Wrapping each case in t.Run(name, func) turns it into a named subtest , so the report tells you precisely which case broke and you can re-run just that one. Adding a new case is a single line.

Your turn again. The table below tests a Max function but is missing a case. Add one row that covers two equal numbers, then run it.

4️⃣ Running Tests: go test , -v , -run , -cover

You drive everything from the command line. go test runs the package's tests and prints ok or FAIL . Add -v for a line per test, -run to filter by a name regex (great with subtests), and -cover for the percentage of your code the tests exercise. ./... means "this package and everything below it".

5️⃣ Benchmarks (a quick note)

Once your code is correct , you may want to know if it's fast . A benchmark is named Benchmark + capital, takes a b *testing.B , and runs your code in a for i := 0; i b.N; i++ loop. The framework raises b.N until the timing is stable. Benchmarks don't run with a plain go test — you opt in with -bench .

📋 Quick Reference — Go Testing

No blanks this time — just a brief and an outline. The Reverse function is written for you; write a table-driven test for it from scratch, run it, and check each case passes. This is exactly the kind of test you'll write every day in real Go projects.

Practice quiz

Where must Go test functions live?

  • In a file named tests.go
  • In any .go file with a //test comment
  • In a file ending in _test.go
  • In a folder called tests/

Answer: In a file ending in _test.go. The go tool only compiles _test.go files when you run go test.

What signature must a test function have?

  • func TestX(t *testing.T)
  • func TestX(b *testing.B)
  • func testX(t *testing.T)
  • func TestX() error

Answer: func TestX(t *testing.T). Name is Test + Capital, taking exactly t *testing.T.

Which call reports a failure but keeps running the rest of the test?

  • t.Fatalf
  • t.Skip
  • panic
  • t.Errorf

Answer: t.Errorf. t.Errorf records the failure and continues; t.Fatalf stops the test now.

What does t.Fatalf do after reporting the failure?

  • Continues to the next line
  • Stops the current test immediately
  • Skips the whole package
  • Retries the test

Answer: Stops the current test immediately. Use Fatalf when continuing makes no sense, e.g. before a line that would panic.

In a table-driven test, what turns each case into a named subtest?

  • t.Run(tt.name, func(t *testing.T){...})
  • t.Subtest(tt.name)
  • go test -sub
  • t.Case(tt.name)

Answer: t.Run(tt.name, func(t *testing.T){...}). t.Run starts a subtest that passes or fails on its own.

For a subtest case named "with zero", how does it appear in -v output?

  • TestAdd/with zero
  • TestAdd.with_zero
  • TestAdd/with_zero
  • TestAdd-with-zero

Answer: TestAdd/with_zero. Spaces in case names become underscores in the subtest path.

Which flag filters which tests run by a name regex?

  • -only
  • -run
  • -filter
  • -match

Answer: -run. go test -run 'TestAdd/with_zero' targets one subtest.

What does ./... mean in go test ./...?

  • Only the current file
  • Re-run failed tests
  • The parent package only
  • This package and every package below it

Answer: This package and every package below it. It recurses into all subpackages.

Do plain go test runs execute Benchmark functions?

  • Yes, always
  • No, you opt in with -bench
  • Only with -v
  • Only if named TestBenchmark

Answer: No, you opt in with -bench. Benchmarks run only with the -bench flag, e.g. go test -bench=.

How should you compare two slices in a test?

  • got == want
  • got.Equals(want)
  • reflect.DeepEqual(got, want)
  • compare(got, want)

Answer: reflect.DeepEqual(got, want). Slices are not comparable with ==; use reflect.DeepEqual or slices.Equal.