Which Package Name for Go Tests?

October 13, 2020

Imagine a simple Go project:

.
├── go.mod
├── main.go
└── stats
    └── avg.go

No tricks:

content of all files click to enlarge

You are about to create stats/avg_test.go: what is the right package name declaration for that file?

  1. package stats
  2. package stats_test
[reveal the answer]

Both 1 and 2 are correct ⚠️

BUT they will work differently... (keep reading)

Urgh… What?

Here’s what Go in Action says:

The convention for naming your package is to use the name
of the directory containing it. (p.40)

That’s what most Go programmers would say. Keeping in mind:

Here’s another one for you:

You could be forgiven for not knowing this rule. In fact, I managed to spend years with Go without knowing this rule. In denial, I looked all through Go in Action and found:

[…] the package name also ends with _test. When the package name ends like this, the test code can only access exported identifiers. (p.226)

Ouch… never saw that. Or I never realized what it implied.
Here’s another gem from go help test:

Test files that declare a package with the suffix “_test” will be compiled as a separate package, and then linked and run with the main test binary.

Thank you: testpackage

I talked about golangci-lint before. On April 22, 2020, testpackage was added to golangci-lint. If you ran golangci-lint on this project after you added a test, you would get:

testpackage error

First reaction: my tests run and pass, what do you want from me?!

Second reaction: “use stats_test?” … but that would mean putting the tests in a separate directory (named stats_test)?! 🤔

As it turns out, you CAN use package stats_test while keeping the test file in the stats directory.

Implications

Why would you want this? Why does testpackage nag you about this?

It’s a matter of blackbox versus whitebox testing.

Using stats allows you to test lowercase functions; you’re allowed to reach for those because your tests live in the same package. In the code above, your stats package can invoke sum even though other packages wouldn’t be allowed to.

Using stats_test places your test code outside the stats package itself; you’re only allowed to invoke the “public”/uppercase functions. You can test stats.Mean (but not just Mean ⚠️) because that’s what you export and that’s what client code would be allowed to use.

In both cases, you keep the _test.go files in the same directory as the rest of the package. File organization -wise, nothing has changed. Philosophically speaking, you’re taking a different stance on “the right way to test things”.

If you still want to test lowercase functions but survive linting, you can suffix your test file with _internal_test.go

You can read motivation straight from the testpackage’s README. I recommend the referenced articles if you need more convincing.

Thoughts

Is this a good thing or not? Personally, I have mixed feelings about this.

I love being able to test internal/helper functions. Other programming languages don’t necessarily have a great answer for this. Going through the public interface often makes the tests more complicated and stateful… (arguably, that’s what blackbox proponents would like to address)

I dislike that’s it’s another exception to package naming, and an obscure one at that… 😫 Learning about these often feels like preparing for trivia night. Placing “public package tests” in a separate directory would have accomplished the same goal without adding an exception.

Also, from the outside, you cannot tell if _test.go files are internal/external tests. You have to open the test files to confirm (probably depending on a per-project testing philosophy).

If this was more explicit, or more frequently discussed… it would be easier to accept as “another quirk of Go”. In the end, I haven’t decided whether I would embrace this … or work around it:

--
linters:
  disable:
    - testpackage

You can read more on stackoverflow.

Discuss on Twitter