Skip to content
gohacks.dev
Testing

Testing Tools for Go Developers That Are Worth Installing

The Go testing tools worth installing, grouped by job: runners, generators, fuzzing, benchmarks, mutation and mocks, with what was run and what was not.

6 min read
Summarize in AI
Flat illustration of a test report with one failing test.

There are more Go testing tools than anyone needs. A short list is more useful than a long one, so this roundup is organised by the problem each tool solves, starts with what the toolchain already provides, and says plainly which tools I ran for this article and which I describe from their documentation. The versions are the ones from the lab, on Go 1.25.6.

Tested with Go 1.25.6, gotestsum v1.13.0, gotests v1.6.0, gremlins v0.6.0, benchstat (x/perf 2025-08), macOS on Apple Silicon (arm64)

What you already have

Before installing anything, the standard go command covers most of the job.

JobBuilt in
Run tests, subtests, examplesgo test, t.Run, Example functions
Find racesgo test -race
Find crashes and bad inputsgo test -fuzz
Measure speedgo test -bench, b.Loop
See coveragego test -cover, go tool cover, go build -cover
Profile-cpuprofile, -memprofile, go tool pprof
Catch mistakesgo vet, which runs automatically with go test
Machine-readable outputgo test -json, go tool test2json
Control timetesting/synctest since Go 1.25
Check handlersnet/http/httptest
Check log handlerstesting/slogtest

The articles in this series on test flags, benchmarks and the race detector cover these. The tools below add to the list; none replaces it.

Runners and output

gotestsum (run). It wraps go test, runs it with -json and renders the stream. The testname format printed one line per test in the lab: PASS TestAdd, then the failing test's log lines and FAIL TestAbsFails, then a summary of failures at the end. Other formats include pkgname for large repositories, dots for a compact view and standard-verbose. It can rerun failed tests (--rerun-fails) and write a JUnit file for CI. Use it when the default output of a large run is hard to read or when CI needs JUnit; the rerun option should be treated as a diagnostic, not a cure, for the reasons given in the flaky tests guide.

richgo, gotest.tools output filters and similar colourisers do less. Try gotestsum first.

Generators and scaffolding

gotests (run). It generates table-driven test skeletons from function signatures. In the lab, gotests -only Abs -w calc.go produced a TestAbs with an args struct, a table with a // TODO: Add test cases. marker, a t.Run loop and the comparison. It writes the boilerplate you would otherwise copy; it cannot choose the cases. IDEs such as GoLand and the VS Code Go extension expose the same generator from a menu. Generated tests are a starting point: read them, add the awkward cases, and delete the generic failure message in favour of one that names the input.

Assertions, comparison and mocks

These have their own article, assertion and mocking libraries compared. In brief: go-cmp for structural diffs, testify for assertions and suites if your team likes them, gomock or mockery for generated mocks. The standard library alone is enough for many projects, as the stdlib versus testify article argues.

Property tests, fuzzing and mutation

rapid generates structured inputs, shrinks failures, and tests stateful code against a model. See property based testing, where it is run.

The built-in fuzzer needs no install; see the fuzzing guide.

gremlins (run). A mutation testing tool: it changes >= to >, reruns the tests and reports which changes no test noticed. On a package with 100% line coverage it killed 5 of 10 mutants, as described in the mutation testing article. It is slow, so aim it at a few critical packages. go-mutesting is an older alternative with a broader set of mutators; I installed it and did not run it.

Benchmarking

benchstat (run). It compares benchmark runs statistically and prints the median, the spread and a p-value, with a tilde when the difference is not significant. It is part of golang.org/x/perf. See comparing runs. Pair it with -count=10 and a quiet machine.

Real dependencies

testcontainers-go starts real databases and services in Docker from a test, with a wait strategy and a random port. The article integration tests with testcontainers runs it against PostgreSQL. Its weakness is start-up time and the need for Docker in CI.

Service-specific fakes are often better value: miniredis for Redis (see the miniredis article), SQLite in memory for portable SQL, httptest for HTTP, bufconn for gRPC. Use the fake for the bulk and the container for a contract suite.

Browser and end to end

chromedp drives Chrome from Go with no external driver; see the chromedp article. playwright-go drives Chrome, Firefox and WebKit through the Playwright driver. For HTTP-level end to end tests, the standard net/http client against a started service is usually enough.

Behaviour and documentation

godog runs Gherkin scenarios as Go tests; see BDD with godog. go-snaps stores expected output in snapshot files; see snapshot testing. The standard Example functions double as documentation and tests.

Load and contracts

k6 is a scriptable load tester with thresholds, and vegeta a simple Go load library and command; I describe k6 in load testing Go services without having run it. Pact handles consumer-driven contract tests with a broker; the idea, hand-rolled, is in the contract tests article.

Keeping tool versions in step

A tool that is installed by hand drifts between developers and CI. Since Go 1.24, a tool directive in go.mod records executables a module depends on (go get -tool gotest.tools/gotestsum) and go tool gotestsum runs the pinned version. Before that, the convention was a tools.go file with blank imports under a build tag. Either way the version lives in the repository and go mod tidy keeps it. Pin the tool's version the same way you pin libraries, and update it on purpose.

A starter kit by team size

What to install depends on who is using it.

One developer, one service. The toolchain, go-cmp if comparisons are structured, and benchstat the first time you tune something. Nothing else is worth a dependency yet. The fuzzer and the race detector are already installed, and running them is the larger win.

A small team with a database. Add testcontainers or a template-database helper for repository tests, gotestsum for CI output, and a Makefile that gives each tier a command. Add rapid or a fuzz target to the two or three parsers that take outside input. Pin the tool versions.

Several teams and services. Add contract tests, a shared test-support module with fakes and builders, a flake tracker fed by go test -json, scheduled fuzzing and mutation runs on the critical packages, and load tests before release. At this size, tooling choices are a platform concern, and the cost is in keeping versions and conventions consistent, not in any one tool.

Tools that are not worth it for most projects

A short list of things that are easy to adopt and hard to justify.

  • A second test runner on top of go test that changes how tests are discovered or named. It breaks IDE integration and -run, and the gain is cosmetic.
  • A mocking framework for everything. The tool is fine; the habit of mocking every collaborator is what produces tests that break on refactors.
  • Coverage services that gate every pull request on a percentage. The number is easy to game, as the coverage article shows.
  • Retry plugins that rerun failing tests until they pass. They convert a signal into noise.
  • Sleep-based wait helpers that wrap time.Sleep in a nicer name. Wait on a condition or use synctest.

None of these are wrong in every case. Each tends to be adopted for convenience and kept for inertia.

How to choose

  1. Name the problem first: unreadable output, boilerplate, flaky timing, unknown boundaries, slow suite.
  2. Check whether the toolchain solves it. It often does.
  3. Try the tool on one package for a week before adopting it team-wide.
  4. Prefer tools that wrap or produce standard formats (go test -json, standard testing APIs), so leaving costs little.

The go command's own documentation lists the built-in features in the testing flags reference, and the golang.org/x repositories hold the official extras such as benchstat. The larger practice they support is in the testing guide.

Also inTooling
Taggedroundup
Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.