Testing
Purpose and Scope
Testing in Go is a built-in workflow rather than a separate framework bolted onto the toolchain. A developer writes ordinary Go files whose names end in a test suffix, places test functions beside the code under test, and runs the main toolchain command to compile and execute those tests. This page explains that workflow from two angles: the public API exposed by the standard testing package and the command behavior implemented by the go command test driver. Together, these pieces cover unit tests, examples, benchmarks, fuzz targets, output formatting, and test selection.
Sources: src/testing/testing.go, src/cmd/go/internal/test/test.go
The user-facing model is intentionally small. Test functions are discovered by naming convention, receive a testing state object, and report failure through methods on that object rather than by returning a value. The official tutorial demonstrates this with functions named for the behavior being checked and a parameter from the testing package. That convention scales from a tiny module to the Go repository itself: standard library packages use regular package tests, while specialized toolchain and runtime checks may live under the repository test tree when they need a black-box harness or must apply to more than one Go implementation.
Sources: src/testing/testing.go, test/README.md
Relevant Source Files
- src/cmd/go/internal/test/test.go - Implements the go command's test action, including package test compilation, test binary construction, flag handling, output behavior, and cache-aware execution decisions.
- src/testing/testing.go - Defines the public testing package used by Go test files, including the core test state types and the conventions for tests, benchmarks, examples, and fuzzing entry points.
- test/README.md - Documents the Go repository's top-level toolchain and runtime test directory, why those tests exist, and how to run either the full directory suite or selected files through the internal test runner.
Core Primitives
The central runtime primitive is the testing package. It supplies the state values passed to tests, benchmarks, and fuzz targets, and it defines the methods that record failures, log diagnostic information, skip cases, run subtests, and manage cleanup. A test function generally checks one behavior and reports mismatches through the supplied state object. Because the package is part of the standard library, there is no project-level dependency to add before writing tests. The same import works for application modules, standard library packages, and repository-internal packages.
Sources: src/testing/testing.go
The command primitive is the test action implemented by the go command. When a user invokes the test subcommand, the tool loads packages, identifies test files, builds package code together with test code, links a test binary when execution is needed, and runs that binary with the selected options. The distinction matters because some flags affect package selection and compilation, while others are passed through to the generated test binary. Understanding that split explains why selection, verbosity, caching, benchmark execution, and profiling options can interact in visible ways.
Sources: src/cmd/go/internal/test/test.go
Writing and Organizing Tests
A package test normally lives in a file whose name ends with the test suffix, and its functions use names that describe the case under test. Tests may be in the same package when they need direct access to unexported identifiers, or in an external package when they should exercise only the public API. The official tutorial emphasizes the common same-package case: create a test file next to the implementation, import the testing package, call the function under test, and report an unexpected result with an error method. That style keeps the feedback loop short during development.
Sources: src/testing/testing.go
Examples and benchmarks use the same discovery model but serve different readers. Examples double as documentation checks: they show a small use of an API and can verify expected output when the example includes an output comment. Benchmarks measure repeated execution and are driven by the benchmark state rather than a single assertion. Fuzz targets add generated inputs and corpus handling to the same package-level workflow. The important unifying idea is that all of these are ordinary Go declarations compiled by the go command and coordinated by the testing package at execution time.
Sources: src/testing/testing.go, src/cmd/go/internal/test/test.go
Execution Flow
A practical local run begins with package selection. With no explicit package arguments, the command tests the package in the current directory. With package patterns, it expands those patterns and runs the selected packages. For each package, ordinary source files and test source files are compiled in a test context. If the package has tests that must run, the command creates and executes a test binary; if there is nothing to run, it can still report the package status. Failures are associated with packages so that multi-package runs remain readable.
Sources: src/cmd/go/internal/test/test.go
The test binary is responsible for calling registered tests, examples, benchmarks, and fuzz targets according to the flags it receives. A failed assertion marks the current test as failed, while a fatal failure stops that test immediately. Subtests let a single test organize related cases under names, which then become selectable by the run pattern. Skips are reported separately from failures so that platform conditions, unavailable external resources, or intentionally unsupported modes can be distinguished from broken behavior. This structure produces useful output without requiring every test to print progress.
Sources: src/testing/testing.go, src/cmd/go/internal/test/test.go
go test ./...
go test -run TestHello ./...
go test -v ./pkg
go test -bench . ./pkgFlags, Caching, and Output
The test command has to balance reproducibility with speed. Cacheable successful results can be reused when the relevant inputs and cacheable options have not changed, which makes repeated package-pattern runs much faster. Some options force fresh execution because they alter runtime behavior, request additional diagnostics, or gather measurements. Developers should treat the cache as an optimization rather than a semantic difference: if a test depends on wall-clock state, external services, random global state, or undeclared files, that dependency can make local results harder to reason about even when the command behaves correctly.
Sources: src/cmd/go/internal/test/test.go
Output is package oriented by default. Passing the verbose option asks for more detail from individual tests and is especially useful when diagnosing skips, subtests, or logs that are otherwise hidden for passing tests. The run option narrows normal tests and examples by name, while benchmark selection is controlled separately so that performance measurements are not run accidentally during ordinary correctness checks. In repository work, this distinction is important because correctness suites are expected to run frequently, while comprehensive benchmarks or architecture-sensitive compiler checks can be more expensive.
Sources: src/cmd/go/internal/test/test.go, src/testing/testing.go
Repository Test Suites
Most standard library tests should be written as regular Go tests in the package they validate. The top-level repository test directory exists for cases that are better expressed by a specialized runner, for regression and black-box tests of the toolchain and runtime, or for tests that should also apply to other Go toolchains. Its README states that these tests are run as part of the full repository test script and gives direct commands for running only that suite or a selected set of files. That guidance helps contributors choose the narrowest appropriate location for a new test.
Sources: test/README.md
For day-to-day repository development, the same command vocabulary applies, but paths and packages are chosen more carefully. A standard library change usually runs the affected package tests. A compiler, linker, or runtime change may require package tests plus tests from the repository test directory. The README shows that selected files can be run through the internal test directory package with a run pattern, which is useful when iterating on a focused regression. Contributors should prefer the smallest reliable test command while editing, then broaden coverage before sending or submitting a change.
Sources: test/README.md, src/cmd/go/internal/test/test.go
../bin/go test cmd/internal/testdir
../bin/go test cmd/internal/testdir -run='Test/(file1.go|file2.go|...)'Compact Reference
| Area | Public shape | Notes |
|---|---|---|
| Unit tests | Functions named like TestName | Receive a testing state value and report failures through it. |
| Subtests | Parent tests call a subtest runner | Useful for table-driven cases and name-based selection. |
| Examples | Functions named like examples | Can be checked against documented output and serve documentation readers. |
| Benchmarks | Functions named like BenchmarkName | Selected separately from normal tests and driven by repeated execution. |
| Fuzz targets | Functions named like FuzzName | Combine seed corpus inputs with generated inputs under the test command. |
| Repository test directory | Internal testdir package commands | Used for toolchain, runtime, black-box, regression, and cross-toolchain tests. |
Next Steps
When adding a new application test, start with the smallest package-local test file and run the current package before expanding to broader package patterns. When adding a Go repository test, first decide whether the behavior belongs in the relevant package as a normal Go test or in the top-level test directory because it needs the special runner or applies across toolchains. For adjacent topics, read the Add a Test tutorial for a beginner workflow, the Coverage page for measurement output, the Race Detector page for data race runs, and the go Command page for command dispatch details.
Sources: src/testing/testing.go, src/cmd/go/internal/test/test.go, test/README.md