skip to content

What does the go test -short flag actually do, and how does a slow test opt out under it?

level: juniorimportance: should knowfreq 46%

answer

  1. one flag with no behaviour of its own
  2. the test does the skipping, not the tool
  3. a boolean the test reads
  4. testing.Short() paired with t.Skip

basics

~10 s

The -short flag only sets a boolean that testing.Short() reports; it skips nothing on its own. Each slow test has to check testing.Short() itself and call t.Skip so the fast run passes over it.

solid answer

~40 s

`go test -short` sets the test binary's `-test.short` flag, and the only thing that flag does is make `testing.Short()` return true. All the skipping is a convention the test author writes: a slow test opens with `if testing.Short() { t.Skip("skipping migration test in short mode") }`, so a pipeline can run `go test -short ./...` for a fast pre-merge pass and plain `go test ./...` for the full one. A test with no `testing.Short()` check ignores the flag completely and always runs. The nice property is that the file is still compiled, type-checked and vetted in the short run, and a skipped test still announces itself as `--- SKIP` under `-v`, so you can see exactly what the fast pass gave up.

code

go · 6 lines
go
func TestMigrateLargeSchema(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping large-schema migration in short mode")
	}
	// ... applies several hundred migrations against a real database
}

go deeper

for a junior

Be ready to write the guard from memory: if testing.Short() { t.Skip(...) }, and to say that go test runs everything unless you pass -short.

for a middle

Explain that -short only flips a boolean the test reads, that the guarded file is still compiled and vetted, and how a skip surfaces in verbose output but not in the plain one.

for a senior

Show how you would split a real suite: which tests get the guard, what the fast command is, and how you stop the skipped work from drifting out of relevance.

for a principal

Own the policy behind it: what a short run is allowed to omit, who is accountable when a defect slips through the fast pass, and whether that omission is legible in the pipeline output.

## The flag is a boolean, not a filter `go test -short` is often described as "skip the slow tests", which gets the mechanism backwards. The `go` command translates `-short` into `-test.short` on the compiled test binary, and the `testing` package stores it. The only observable effect is that `testing.Short()` returns `true`. No test is selected, deselected, reordered or timed out because of it. If nobody in the package ever calls `testing.Short()`, then `go test -short ./...` and `go test ./...` do exactly the same work. ## The convention you have to write yourself The whole contract lives in the test: ```go func TestMigrateLargeSchema(t *testing.T) { if testing.Short() { t.Skip("skipping large-schema migration in short mode") } // ... applies several hundred migrations against a real database } ``` `t.Skip` records the message, marks the test skipped and stops it immediately — it is not a failure, the package still reports `ok`, and the process exit status stays 0. `t.Skipf` takes a format string; `t.SkipNow` skips without a message. The same guard works at the top of a subtest or inside a helper, since `Short()` is just a function call. ## Where the value comes from `testing.Short()` reads a flag that the test binary registers at start-up and parses before it runs any test function. That is why calling it from a package-level variable initializer is a mistake: those initializers run before the testing flags exist, and the call panics rather than returning a plausible-looking `false`. Keep the call inside the test function body, where the value is real. ## What the short run costs and what it buys Because the guarded test is ordinary code in an ordinary file, it is compiled, linked and type-checked on every build, short or not. The `go test` default vet pass sees it. A rename or a signature change in the package breaks it immediately, so the test cannot silently rot. That is the central difference from putting the same suite behind a build constraint, where the file is not part of the build at all in the default run and stops being checked by anything. The cost is that everything the test *imports* and *links* is still paid for in the short run. If the slow test needs a heavyweight dependency, the fast build still pulls it in; only the run time is saved. If the suite genuinely cannot compile or link in a normal developer environment, `testing.Short()` is the wrong tool and a build constraint is the right one. ## Reading the output Without `-v`, `go test` prints one line per package (`ok`, or a failure) and skipped tests are invisible — the package that skipped every one of its tests still says `ok`. With `-v` you get `--- SKIP: TestMigrateLargeSchema (0.00s)` followed by the file, line and skip message. That visibility is a real feature: someone reading a short run's verbose log can enumerate exactly what was not exercised, which is impossible for a suite that was excluded from the build. ## Practical use Two commands, one repository: - `go test -short ./...` — what a developer runs constantly and what a fast pre-merge job runs. Fast, and every file still compiles. - `go test ./...` — the full run, on a schedule or before a release. The convention is worth applying consistently: an unguarded slow test in a package where every peer is guarded is the reason a "short" run takes four minutes. Conversely, guarding a test that runs in 200 ms adds a branch and buys nothing. Reserve the guard for tests whose cost is measured in seconds and whose failure the fast run does not need to catch.

  • Where must you not call testing.Short(), and why?
    Not from a package-level variable initializer. Those run before the test binary has registered and parsed its flags, so the call panics instead of returning a meaningful value. Call it inside the test function body, where the flag has already been parsed and the answer reflects what the user actually passed on the command line.
  • Why might a team prefer -short over a build constraint for its slow suite?
    Because the code stays in the build. A file behind a build constraint is not compiled in the default run, so it quietly rots until someone remembers the tag. A -short-guarded test is compiled, type-checked and vetted on every run and announces itself as SKIP, which keeps the omission visible and the code honest.
  • What does go test print for a test that skipped itself?
    With -v you get `--- SKIP: TestX (0.00s)` and the skip message. Without -v, skips are folded into the package's single `ok` line and are invisible. Either way the package passes: t.Skip is not a failure and the exit status stays 0.

The -short flag is a sign on the door reading "we close early today". It locks nothing; each test has to read it and decide for itself to go home.

saying these in an interview costs you the question

  • Says -short automatically skips tests it judges slow
  • Thinks -short is the default for go test
  • Believes t.Skip fails the package or returns non-zero
  • Calls testing.Short() from a package-level variable initializer
  • Expects skipped tests to be listed without -v