Skip to content
gohacks.dev
Testing

No Test Files and Build Failed Messages in go test Fixed

Fix the go test messages no test files, setup failed and build failed: a misnamed file, a malformed test name, build tags and directories with no Go files.

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

You run go test ./... and some package prints [no test files], or a block of text ending in [setup failed] or [build failed]. The messages look alike and mean different things. This article reproduces each on Go 1.25.6, shows what the toolchain is telling you, and gives the fix.

Tested with Go 1.25.6, macOS on Apple Silicon (arm64)

Reproduce the four causes

calc.goGo
package calc

func Add(a, b int) int { return a + b }

The package is a single function, Add. The lab adds test files in different states and runs go test after each.

Shell
echo '# 1. the file is named calc_tests.go, not calc_test.go'
go test -count=1 ./... 2>&1 | sed -E 's/\t[0-9.]+s//'
mv calc_tests.go calc_test.go
echo
echo '# 2. renamed: it runs'
go test -count=1 -v ./... 2>&1 | grep -E '^(---|ok)' | sed -E 's/ \([0-9.]+s\)//; s/\t[0-9.]+s//'
cat > lower_test.go <<'EOF'
package calc

import "testing"

func Testlower(t *testing.T) {}
func TestUpper(t *testing.T) {}
EOF
echo
echo '# 3. a name with a lower case letter after Test is not a test'
go test -count=1 -list '.*' . | head -5
go vet . 2>&1 | grep -v '^#' | sed -E 's#^vet: ##; s#^\./##'
rm lower_test.go
echo
echo '# 4. a package with tests only behind a build tag'
cat > tagged_test.go <<'EOF'
//go:build integration

package calc

import "testing"

func TestTagged(t *testing.T) {}
EOF
mkdir -p tagonly && mv tagged_test.go tagonly/ && echo 'package tagonly' > tagonly/x.go && sed -i.bak 's/package calc/package tagonly/' tagonly/tagged_test.go && rm tagonly/*.bak
go test -count=1 ./tagonly 2>&1 | sed -E 's/\t[0-9.]+s//'
echo
echo '# 5. a directory that is not a package at all'
mkdir -p docs && go test ./docs 2>&1 | head -3 | sed -E 's#in /[^ ]*/docs#in ./docs#'
Text
# 1. the file is named calc_tests.go, not calc_test.go
?   	example.test/lab	[no test files]

# 2. renamed: it runs

# 3. a name with a lower case letter after Test is not a test
FAIL	example.test/lab [build failed]
FAIL
lower_test.go:5:6: Testlower has malformed name: first letter after 'Test' must not be lowercase

# 4. a package with tests only behind a build tag
?   	example.test/lab/tagonly	[no test files]

# 5. a directory that is not a package at all
# ./docs
no Go files in ./docs
FAIL	./docs [setup failed]
mv: rename calc_tests.go to calc_test.go: No such file or directory
# example.test/lab
# [example.test/lab]
./lower_test.go:5:6: Testlower has malformed name: first letter after 'Test' must not be lowercase

[no test files]: the toolchain found nothing to run

The line ? example.test/lab [no test files] is not an error. The question mark in place of ok or FAIL says nothing was run. The Go command looks in each package directory for files whose names end in _test.go. If it finds none that apply to this build, it reports this and moves on. Exit status is zero, which is why a CI job can stay green while a package has no tests at all.

Cause 1: the file name

The first block of the lab has the test in calc_tests.go. The test is correct, the function is TestAdd, and the toolchain ignores it, because the suffix is _test.go, singular. A file called calc_tests.go is an ordinary source file, compiled into the package for non-test builds, which means it also imports testing into your production binary. Rename it. After mv calc_tests.go calc_test.go, the second block shows --- PASS: TestAdd and ok.

The same applies to calc.test.go, calc_test.GO on a case-sensitive file system and test_calc.go. The rule is exact: the file name must end in _test.go. Names starting with an underscore or a dot are ignored by the toolchain altogether, so _calc_test.go is skipped as well.

Cause 2: build constraints

The fourth block moves a test file that starts with //go:build integration into its own package directory. A plain go test ./tagonly shows [no test files], because the constraint is false without -tags integration, so the file is excluded from the build. This is by design and a common source of surprise when a suite is split by tag, as in the integration tag article. Check with go list -f '{{.TestGoFiles}}' ./tagonly (empty without the tag) and with go list -tags integration -f '{{.TestGoFiles}}' ./tagonly (the file). File names with an operating system or architecture suffix, such as calc_windows_test.go, are constrained the same way.

Cause 3: wrong directory

Running go test in a directory that holds only test data or documentation, or running it from the repository root without ./..., finds nothing in that directory. For the root, go test ./... expands to every package under it. Packages in directories named testdata, vendor, or starting with _ or . are skipped by the ./... pattern.

Cause 4: the package is a main package with nothing to test

It is legitimate for a main package that only wires dependencies to have no test files. The message is accurate, and whether it is a problem is a judgement about the code. If you want CI to catch accidental omissions, count the packages: go test ./... 2>&1 | grep -c 'no test files' against a known list.

[setup failed]: the package could not be loaded

The last block runs go test ./docs, a directory that exists but has no Go files. The output is no Go files in .../docs and FAIL ./docs [setup failed]. "Setup" is the phase where the toolchain resolves and loads packages, before compiling. It fails for a package that does not exist, one with no buildable Go files, an import that cannot be resolved, a missing module, or an import cycle.

For the import cycle, the message names the chain: import cycle not allowed in test, with each package that imports the next, as shown in the shared helpers article. For a missing dependency, the line above says no required module provides package ..., and the fix is go get or go mod tidy. For a directory with no Go files, you asked for something that is not a package: use a path that has .go files, or ./....

[build failed]: it compiles, or vets, badly

[build failed] means the toolchain loaded the package and failed while building the test binary, or while running go vet on it. The compiler error is printed above the line: undefined: Foo, declared and not used, a type mismatch. Read the first error and fix it; the others are often consequences.

One case surprises people. The third block of the lab adds a file with func Testlower(t *testing.T) {}. The name starts with Test followed by a lower case letter, so the testing package does not treat it as a test and silently skips it. In Go 1.25, go test runs a small subset of go vet automatically, and the check for test names is in it: the run fails with Testlower has malformed name: first letter after 'Test' must not be lowercase and [build failed]. Without that check, the function would be ignored and the test would never run, which is the worse outcome. Rename it TestLower.

The same vet check covers Example functions that refer to names that do not exist, a Benchmark with the wrong signature, and a Fuzz with the wrong parameters. When go test reports build failed and the compiler is silent, look for the vet line.

A checklist

  1. [no test files]: is the file named *_test.go exactly? Is a build tag excluding it (go list -f '{{.TestGoFiles}}')? Are you in the right directory, or missing ./...?
  2. [setup failed]: does the path contain Go files? Does go build ./... work? Are all modules available (go mod tidy)? Is there an import cycle?
  3. [build failed]: read the first compiler or vet message above the line; fix that.
  4. Tests run but not the one you want: the name must be TestXxx with an upper case letter after Test, in a file ending _test.go, with signature func(*testing.T). Check with go test -list '.*'.

Making CI catch the silent case

A pipeline that treats [no test files] as success will not notice a test file renamed by accident. Two cheap defences: run go vet ./..., whose test-name check catches malformed names even in packages go test would skip; and keep a list of packages that are expected to have tests, failing the job if any shows the message. For the whole repository, go test -json ./... produces a skip action for packages with no tests, which a script can count.

The rules for file names and test function names are in the go test documentation, and the build constraint rules in the build constraints reference. If a test runs but hangs, see the timed out panic.

Taggederror fix
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.