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.

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 .

Pro Tips

Common Errors (and the fix)

📋 Quick Reference — Go Testing

Task

Code / Command

Test file name

something_test.go

Test function

func TestX(t *testing.T)

Fail, keep going

t.Errorf("got %d; want %d", g, w)

Fail, stop now

t.Fatalf("setup failed: %v", err)

Subtest

t.Run(name, func(t *testing.T) { … } )

Run all / verbose

go test · go test -v

Run one (sub)test

go test -run 'TestX/case'

Coverage

go test -cover ./...

Benchmark

go test -bench=. -benchmem

Frequently Asked Questions

Mini-Challenge: Test a String Reverser

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.

🎉 Lesson Complete!

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.

Continue this course