Go - Test Suites
Specification-driven test suites for Go.
gotest generates standard go test code from struct-based suites — BDD organization, lifecycle hooks, and structured output. No reflection, no runtime deps.
This extension brings that into VS Code: run, debug, watch, and verify your suites without leaving the editor.
Why this extension?
Test Explorer shows your suites as Package > Suite > Method > Subtest — not a flat list of functions.
Run or debug at any level. Coverage gutters track implementation progress. Watch mode streams results as you code.
The Spec View renders your test structure as a behavioral specification — a readable tree with pass/fail indicators, go-to-source navigation, and clipboard export. Your tests become documentation that's always in sync.
AI-assisted workflows — Paste specs into AI conversations as context. The clean exports feed well into LLM toolchains. You define what the system should do — the tests verify.
- Spec View — Your quality dashboard. BDD-formatted specification tree with pass/fail/skip indicators, go-to-source navigation, and structured clipboard export.
- Suite-aware Test Explorer — Navigate tests as Package > Suite > Method > Subtest, not a flat list of functions.
- Coverage gutters — Track implementation progress with native VS Code coverage integration, per-statement highlighting, and persistent results across sessions.
- One-click Run and Debug — CodeLens buttons above every suite and method. Debug with Delve, breakpoints included.
- Watch mode — Continuous verification on file changes with streaming results and a status bar indicator.
- Focus/Exclude management — Toggle
F_/X_ prefixes via Quick Fix actions. Diagnostic warnings prevent focused tests from reaching CI.
- Scaffold generation — Generate test suite skeletons from types and files via code actions or the command palette.
Getting started
Prerequisites
- Go (1.25+)
- VS Code (1.123+)
- Delve (for debugging only)
The extension invokes the gotest CLI automatically — no separate install needed.
It runs the CLI your go.mod selects: go tool when the tool directive is declared, otherwise go run at the pinned version.
The CLI must be v1.31.0 or newer. A binary or a go.mod pin is checked at activation; a CLI reached through go run or go tool is checked from the version that opens every document it writes, so an older one is refused at discovery rather than trusted.
Install
Search "gotest" in the Extensions panel, or install from the command line:
code --install-extension mvrahden.gotest
Also available on Open VSX for VS Code forks.
First run
- Open a Go project that uses gotest suites.
- The extension discovers test suites automatically on activation.
- Open the Testing sidebar to see your suites organized by package.
- Click the Run or Debug button next to any suite or method.
Features
Test Explorer
Tests appear in a structured tree: Package > Suite > Method > Subtest.
Run or debug at any level — a single method, an entire suite, or all tests in a package.
Multi-select is fully supported.
Test results persist across sessions, so you see pass/fail state immediately after reopening the editor.
The tree itself is restored from disk on startup, so suites are browsable straight away instead of waiting for the Go toolchain to finish discovery.
CodeLens
Run and Debug buttons appear inline above every suite and test method in _test.go files.
Click to execute immediately.
An ↻ Update Snapshots lens additionally appears above methods that call MatchSnapshot (and their suite), re-running them with --update-snapshots.
Benchmark methods get Bench, 5×, and a persistent result annotation (see Benchmarks); fuzz methods get Fuzz and Debug Seeds (see Fuzzing).
Package-level and file-level actions appear on the package declaration line:
- Run Package — run all suites in the package
- Run File — run all suites defined in the current file (shown when the file contains multiple suites)
Coverage
Use the Coverage run profile in Test Explorer to run tests with go test -coverprofile.
Results appear as native VS Code coverage gutters.
Coverage data persists across sessions and accumulates across packages.
Source file edits automatically invalidate stale coverage for the affected package.
Copy a tabular coverage summary to the clipboard via the Go Test: Copy Coverage Summary command.
Watch mode
Start continuous testing with Go Test: Start Watch.
The extension spawns a gotest watch process that re-runs tests on file changes.
Results stream into Test Explorer in real-time.
A status bar item shows active watcher count and lets you stop all watchers with a click.
Spec View
After each test run, the Spec View panel renders BDD-formatted output with color-coded pass/fail/skip indicators.
Open it with Go Test: Show Spec View.
- Go-to-source — click any suite or method to navigate to its definition
- Toolbar — expand/collapse all, filter by pass/fail/skip status, search behaviors by name
- Copy / Clear — copy the full spec report to clipboard or clear results
- Persistence — the panel survives editor reload and restores its last state
- Live updates — auto-refreshes from test runs, coverage runs, and watch mode
Focus and Exclude
Place your cursor on a suite or method definition and use the Quick Fix menu (Ctrl+. / Cmd+.) to:
- Focus a test (
F_ prefix) — only focused tests run
- Exclude a test (
X_ prefix) — test is skipped
- Unfocus / Include — remove the prefix
A status bar warning and inline diagnostics alert you when focused tests exist, preventing CI failures from gotest --ci.
Benchmarks
A benchmark answers with a number, and a number is meaningless in isolation.
Every surface here exists to close the loop between "I changed this code" and
"what happened to the number" — powered by gotest bench --json; all
statistics (Welch's t-test significance, deltas, the gate rule) are computed
by the CLI, never re-derived in the extension:
- ▶ Bench CodeLens on every
Benchmark* method — runs exactly that
method (go test's sub-benchmark scoping). ▶ Bench Suite on the suite
runs them all; 5× runs with -count=5 for a trustworthy mean ± spread.
- Result annotations — the last measured numbers render right above the
method (
1.24µs/op · 480 B/op · 3 allocs/op — 2m ago) and survive editor
reloads. Results are keyed per goos/goarch: a number measured on another
platform is a different number and is never shown on this host.
- Trend on hover — hovering a benchmark shows its run-over-run sparkline
with the endpoints spelled out, from a bounded local history (last 50 runs).
- Baselines — Go Test: Save Bench Baseline and Go Test: Compare vs
Baseline wrap
--save/--against, defaulting to bench.baseline from
.gotest.yml. After a compare, significant deltas render inline
(+12.3% vs baseline); deltas the CLI did not mark significant display
nothing — the UI never dresses up noise.
- Gate warnings — when
bench.gate is configured and a run breaches it,
the offending methods get warning diagnostics at their definitions.
- Test Explorer — benchmarks appear under their suites with a dedicated
Bench run profile. They never run as part of a normal test run: timing
numbers taken while tests hammer the machine are noise.
- Profiling — Go Test: Profile Benchmark (CPU/Mem) runs one benchmark
with
-cpuprofile/-memprofile and opens go tool pprof -http on the
result.
Benchmarks are deliberate acts: there is no bench-on-save and watch mode
never benchmarks.
Fuzzing
Fuzz methods on suites get their own surfaces, built on the gotest fuzz CLI and its exit contract:
- Fuzz run profile in the Test Explorer's run dropdown, beside Bench — offered where fuzz targets are selected, it searches every selected target for the
gotest.fuzz.for budget (the CLI's minute by default): one session per package when all of its targets are selected, one per target otherwise. A target passes with the session line, fails with its new crasher files named, and a cancelled search is skipped, not failed.
- ▶ Run CodeLens on every
Fuzz* method replays its seeds as an ordinary test run, like a test method's Run.
- ▶ Fuzz CodeLens on every
Fuzz* method — pick a budget (30s, 5m, 30m, until stopped, or any Go duration) and the target fuzzes in a cancellable background session with live execs/sec progress. Nothing found ends quietly; time exhaustion is not a failure.
- Crasher notifications — when the session finds a new crasher, choose Show Decoded Input (triage prints the typed Go literal, not corpus bytes), Promote to Seed (splices a typed
f.Add(...) into the fuzz method and reveals the edit), or Debug Crasher (replays exactly that corpus entry under the debugger, suite lifecycle included).
- ⚠ Promote N crashers CodeLens — pending corpus entries surface right on the target until promoted.
- Debug Seeds CodeLens — replay a target's whole seed corpus under the debugger.
- Test Explorer and Spec View — fuzz targets appear under their suite in both, the Spec View as a property row beside the suite's examples, marked
FUZZ with its seed count and counted as fuzz targets in the trailer; running one replays its seeds as ordinary subtests. Searching for new inputs is deliberately a CodeLens action, never an explorer run: fuzzing burns CPU on demand, not as a side effect.
- Suite runs replay seeds — running a whole suite from the explorer includes its fuzz-seed replay, matching what
gotest ./... does on the CLI.
Watch mode deliberately never fuzzes: watch is fast, deterministic feedback; fuzzing is a budgeted stochastic search.
Scaffold
Generate test suite skeletons from existing code:
- Code action on a type declaration — "Generate test suite for TypeName"
- Code action on any Go file — "Generate test suite for this file"
- Command palette — "Go Test: Scaffold Suite" for manual target entry
The generated file opens automatically and discovery refreshes.
Multi-root workspaces
Fully supported.
Each workspace folder is discovered independently.
Commands resolve the correct workspace folder from the active editor, and file watchers trigger per-folder discovery.
Per-project settings like cliPath, testFlags, and buildTags can be configured per folder via .vscode/settings.json.
Projects using go.work are also supported.
Commands
| Command |
Description |
| Go Test: Run |
Run a specific test by ID |
| Go Test: Run File |
Run all suites in the current file |
| Go Test: Debug |
Debug a specific test by ID |
| Go Test: Refresh |
Re-run test discovery for all workspace folders |
| Go Test: Show Focused Tests |
List all focused tests and navigate to them |
| Go Test: Show Spec View |
Open the BDD spec output panel |
| Go Test: Start Watch |
Start continuous testing for a package scope |
| Go Test: Stop Watch |
Stop all active watch processes |
| Go Test: Run Benchmark |
Run a suite's or method's benchmarks via gotest bench |
| Go Test: Bench (stable, 5×) |
Run a benchmark with -count=5 for mean ± spread |
| Go Test: Save Bench Baseline |
Save the workspace's benchmark results as a baseline |
| Go Test: Compare vs Baseline |
Compare current numbers against a saved baseline |
| Go Test: Profile Benchmark (CPU/Mem) |
Profile one benchmark and open go tool pprof |
| Go Test: Fuzz Target |
Start a budgeted fuzz session for one target |
| Go Test: Debug Fuzz Seeds |
Replay a fuzz target's seeds under the debugger |
| Go Test: Triage Fuzz Crashers |
Show decoded inputs for a package's crashers |
| Go Test: Promote Fuzz Crashers |
Turn crashers into typed f.Add seeds |
| Go Test: Scaffold Suite |
Generate a test suite from a target |
| Go Test: Scaffold Target |
Generate a test suite for a specific target |
| Go Test: Update Snapshots |
Re-run tests with --update-snapshots to rewrite MatchSnapshot baselines (also a run profile and a CodeLens on snapshot tests) |
| Go Test: Copy Coverage Summary |
Copy coverage table to clipboard |
| Go Test: Copy Test Results |
Copy test results to clipboard (also available as context menu on test items) |
| Go Test: Clear Results |
Forget stored results, coverage and spec output, so a window reload does not restore them. The Test Explorer's own "Clear all results" only empties VS Code's in-memory view; clearing the icons in the tree is still its job, because no stable API lets an extension retract them. |
Settings
Per-project settings (resource scope)
These can be set in .vscode/settings.json per workspace folder:
| Setting |
Default |
Description |
gotest.cliPath |
"" |
Path to a gotest binary (overrides all other resolution) |
gotest.modulePath |
github.com/mvrahden/go-test/cmd/gotest |
Package path of the gotest CLI |
gotest.suggestToolDirective |
true |
Offer once per folder to declare the pinned CLI as a tool in go.mod |
gotest.buildTags |
"" |
Comma-separated Go build tags (e.g. integration,e2e) |
gotest.testFlags |
[] |
Additional flags passed to gotest (-- prefixed) and go test (- prefixed) |
gotest.buildFlags |
[] |
Additional build flags passed to Delve |
gotest.discoverOnSave |
true |
Re-discover tests when _test.go files change |
gotest.coverOnRun |
true |
Collect coverage data alongside normal test runs |
gotest.coverOnSave |
false |
Re-run package coverage when a .go file is saved |
gotest.coverTestOnlyPackages |
false |
Enable cross-package coverage instrumentation for test-only packages |
gotest.debug.prepareTimeout |
60 |
Seconds to wait for debug preparation before timing out |
gotest.discoveryTimeout |
120 |
Seconds to wait for test discovery before giving up (raise it for a very large workspace) |
gotest.forceKillTimeout |
360 |
Seconds to wait after SIGTERM before sending SIGKILL to a cancelled process. SIGTERM is what triggers shared-fixture teardown, and the CLI allows that up to 5m30s, so this must outlast it |
gotest.fuzz.for |
"" |
Budget for fuzz sessions started from the Test Explorer's Fuzz profile (a Go duration; empty uses the CLI's default of one minute) |
gotest.watch.scope |
./... |
Default package scope for watch mode |
Global settings (window scope)
| Setting |
Default |
Description |
gotest.showCodeLens |
true |
Show Run/Debug CodeLens above suites and methods |
gotest.showFocusWarnings |
true |
Show diagnostics for F_ prefixed (focused) tests |
gotest.specView.autoRefresh |
true |
Auto-refresh Spec View after test runs |
gotest.watch.autoRestart |
true |
Auto-restart watch process on crash |
Example .vscode/settings.json
{
// Use a local gotest binary instead of go run
"gotest.cliPath": "./bin/gotest",
// Pass build tags to discovery and test runs
"gotest.buildTags": "integration,e2e",
// Extra flags for go test (e.g. timeout, verbose)
"gotest.testFlags": ["-timeout=120s", "-v"],
// Control parallelism:
// --parallel=N total concurrent tests across all suites (gotest flag)
// -parallel=N concurrent tests within each suite (go test flag)
// "gotest.testFlags": ["--parallel=12", "-parallel=4"],
// Extra build flags for Delve debug sessions
"gotest.buildFlags": ["-gcflags=all=-N -l"]
}
Gotest CLI resolution
The extension runs whatever go.mod selects, in this order:
gotest.cliPath — Explicit path to a binary. Highest priority; version-validated against the minimum required version.
- Workspace is gotest module — If the workspace's
go.mod declares the gotest module itself (development or go.work overlap), uses go run ./cmd/gotest.
- Tool directive — If
go.mod declares the CLI as a tool (go get -tool github.com/mvrahden/go-test/cmd/gotest@<version>), uses go tool <modulePath>.
go.mod + replace directive — If go.mod has a replace directive for the gotest module, uses go run modulePath (no version, respects replace resolution).
go.mod pinned version — If go.mod requires gotest at v1.31.0 or newer, uses go run modulePath@version, and offers once to declare the tool at that version (gotest.suggestToolDirective; never in a vendored module).
- Pinned below v1.31.0 — Refused with an Upgrade action; the pinned CLI's stream lacks verdicts the tree shows (a shared fixture that fails to set up reached it as a bare exit code), and a newer CLI would generate code the pinned runtime cannot compile.
go run @latest — Only when no module requires gotest yet, so scaffold can run before a pin exists.
In a go.work root every use module is read: a tool declared by any of them counts, and the pin is the highest across modules, which is what the workspace builds.
Go binary resolution
The extension finds one go and leaves the toolchain to Go: go run and go tool honour the module's go directive and pass their own go to the CLI.
GOROOT — $GOROOT/bin/go if set.
- Login shell — Runs
bash -lc 'command -v go' to find Go on the user's full PATH.
- Common paths —
/usr/local/go/bin/go, ~/go/bin/go, /usr/bin/go, ~/sdk/go*/bin/go.
Requirements
- VS Code 1.123 or later
- Go 1.25 or later
- A Go project using gotest suites
- Delve for debug support
License
MIT