“Crew, prepare for landing” echoed through the cabin. I looked out of the window and I saw the island – I was freaking out, but super excited at the same time. As a kid growing up in the Balkans, if you told me I would visit Bali one day I am sure I would not believe you for a second.
The first memory I have of walking in the hotel was the May heat and humidity. It did take me a couple of heavy breaths to realise that I can actually breathe just fine in that humidity. Next memory are all the helpful people that we met in and on the way to the hotel - they all greeted us with Sembah - a traiditonal Balinese greeting, forming a clasp with their hands placed in front of their chest.
Looking back at it, all of the interactions with other humans and their way of expression were part of one culture that contains some conventions. For example the Sembah greeting often felt fanciful, but the locals explained that that’s a good way to show respect through a greeting. As someone that is definitely not an anthropologist, what I can speculate is that all people, wherever they are located, abide to some conventions. And hopefully these conventions make their lives a bit simpler or effortless.
Following that line of thinking, there’s no surprise that programming languages and tools, which are made by people, often have conventions. These conventions help make our work more straightforward. Just like all tools out there, Go also has some conventions when it comes to testing. Some are defined in the language itself (official), while others are coined by community members (unofficial).
In this article we will look at the naming conventions for files, functions and variables separately.
File and package naming conventions
testing package comes with an
expectation that any test file must have a
_test.go suffix. For example, if
we would have a file called
person.go its test file must be named
person_test.go. This is due to the package building process, where Go knows
to ignore these files when building the package due to their naming. Simply,
it ignores test files as they are not needed for the program to run.
Additionally, Go ships with a command line tool called
go test. This tool
automates testing the packages named by the import paths. It recompiles each
package along with any files with names that match the file pattern
*_test.go. This means that
go test recognizes these files as special and
compiles them as a separate package, and then links and runs them with the main
When it comes to packages, Go by default expects that all test files are part
of the same package that they test. For example, if
person.go defines a
person package, the respective
person_test.go should also be part of the
person package. This also means that both, the
person_test.go files should be placed in the same directory - we let Go worry
what files should be loaded depending on what
go command we run.
Looking at Golang’s source code oddly I found some disregard for these rules.
For example, the tests for the
fmt package in the standard library, belong to
instead of the
At first, my observation was that this is wrong and for some reason it is not fixed yet. After a more thorough research it was obvious that this is an intentional approach and not a mistake. As explained in this Stack Overflow answer, the best way to look at this is to differentiate the two approaches as “black box” and “white box” testing.
The black box approach, where the test and the production code are in separate packages, allows us to test only the exported identifiers of a package. This means that our test package will not have access to any of the internal functions, variables or constants that are in the production code.
The white box approach, where the test and the production code are in the same package, allows us to test both the non-exported and expored identifiers of the package. This is the preferrable approach when writing unit tests that require access to non-exported variables, functions, and methods.
I personally find the white box approach preferrable, because this is the default behaviour of the tooling that ships with the language. We as users of said tooling should employ good judgement and conventions to write code that is testable and avoid touching non-exported identifiers in the tests. In other words, we should adhere to the defaults, unless we have a really good reason not to.
In any case, if you would like to learn how to idiomatic Go and how to organise your packages properly, the source code of the language is the best place to learn from.
Function naming conventions
While the file naming convention is enforced by the language and its toolkit, test function naming conventions are loosely enforced by Go, but are community driven.
In Go, each test file is composed of one or many test functions. Each test function has the following signature structure:
What’s important to notice is that
Xxx does not start with a lowercase
letter. The function name serves to identify the test routine. A simple test
function looks like this (stolen from here):
And that’s all that’s enforced by Go and it’s toolkit. Still, there are a few
common ways to name your test functions. For example, we have a simple type
age attribute. It receives a function
older which checks what
Person is older than another
Person, by comparing their
We would write a test function for
older, looking like this:
Here we name our test function
TestOlder, which clearly states the function
under test -
older. This is in line with what the
testing package expects -
PascalCased function name, starting with
Test. What comes after is up to
In our small example, calling the test function
TestOlder is the most common
approach that you will see in the wild. But, what if we want to test the same
older) in more test functions? Do we use
TestOlder2, etc. as test function names? Or is there a better way?
For such scenarios, I have found a few approaches in the wild:
The Golang source code itself has a naming convention. If we zero in on an
example, like the
strings.Compare function is tested, we can see the convention in
- The base test function follows the format of
Test+ the name of the function under test. For example
TestCompare, which tests the
- More specific tests, for example a test that compares two idential strings,
TestCompareIndentialStrings. Tests that are more specific express that in the name, using the
We can see the same pattern in other files, for example in
where the functionality of the
flags package is tested. Notable examples there
TestUserDefinedForCommandLine test functions:
Moving on to another popular Golang project, consul
by HashiCorp, we can see a different test functions naming convention. If we
look at the
file, where the API client is tested, we can see that the project uses a naming
- The base test function follows the format of
Test+ the name of the package where the function is placed, with the function name appended after an underscore (
_). For example, the
API.Debug.Heap()function is tested in the
TestAPI_DebugHeaptest function. Similarly, the
API.SetupTLSConfigis tested in
- There are functions like
API.Agent.Services()that require more specific tests. That’s why, for example there are
TestAPI_AgentServicesWithFilter, where in the latter there is more specific functionality being tested.
These are just a few examples of test function naming conventions, so expect
to find some others in the ecosystem. There are various conventions when it
comes to naming testing functions, but all of them have to follow the basic
TestXxx that the
testing package enforces.
Variable naming conventions
While there is strict enforcement of the file name convention, and a loose enforcement of test function naming, things are very relaxed when it comes to variables naming. Basically, Golang does not enforce any conventions on the naming of the variables that we can use in our tests via the tooling.
This in theory means that everyone can come up with their own variable names. But, what does that mean in practice? What do popular open source projects do when it comes to naming variables?
Before we dive in any open source projects, we have to go back to the basics. I am not sure if this is well known (enough), as I have found it a bit too burried in the Github wiki, but Go has a nice “Go Code Review Comments” page where variable names are discussed.
While the section is short, it says a lot about how we should be naming our variables:
Variable names in Go should be short rather than long. This is especially true for local variables with limited scope. Prefer
This part is self-explanatory. Go errs on the side of short variable names. In
my personal opinion this does not make sense in a time of very powerful text
editors that autocomplete our code. I prefer to be lazy and read what each
variable means than figuring out what
p mean. Still, if you
believe in consistency, we should all follow the same guidelines when writing
Futher, it says:
The basic rule: the further from its declaration that a name is used, the more descriptive the name must be.
This is something I personally like as a rule – the cognitive weight should be
small when regaining context of what a variable or concept means. In such
conf) can do wonders compared to
Lastly, it states:
Common variables such as loop indices and readers can be a single letter (
r). More unusual things and global variables need more descriptive names.
k variable names for indices are very commonly used, especially
in C-inspired languages, so if you are a little bit experienced (or been exposed)
to them this will be expected.
But, are we bikeshedding here? Why are we discussing variable names, does it matter that much?
Well, Go tests are just code. Being code, we should expect that all tests follow these guidelines just like all other code does. Also, we should write our tests using these guidelines so our tests feel familiar to others that will work with them. There shouldn’t be a major change of code style when switching between business logic and tests.
Now, let’s go back to popular open source projects writen in Go:
Looking at Terraform, another popular HashiCorp
project, one thing that is ubiquitous about its test suite is that the project
has both, the
expected and the
want naming convention when
it comes to test failures.
Basically, the test case has an
expected value, which is what we expect the
function under test to return. The
actual value is what the function under
test returned. What is convenient about following that naming convention is
actual clearly state that those are the values that will
have to be compared with each other, which will drive the decision if the test
will pass or not:
Another way to achieve the same is the
want naming, where the
comparison looks like:
The idea behind the naming is the same, but the goal is to fail with a practical message to whoever’s debugging your code in the future.
From here on, delving in the variables conventions further would not prove to be productive, yet following the naming conventions that we discussed above is good enough for your tests. If you would like to read more on the topic, I suggest reading the “Names” section of Effective Go and “Variable Names” section of the Go Code Review Comments wiki page.
Making things boring
Probably you now:
Why do you bother me with these rules and conventions?
go fmttakes care of my code, isn’t that enough?
I hear you and I get your point.
Here’s how I look at it: easy conventions to follow diminish entropy, which makes for simpler and predictable code. The less cognitive load we have to absorb when working with a piece of code – the better.
In other words: I like boring code. Boring code is good. I like code that will
dully adhere to Go’s naming conventions, regardless if I find them appealing or
not. Conventions are put in place to make our lives easier, by not having to
think if it should be
index, and knowing that
conf will always mean
And I hope that you will find the boringness of such conventions liberating and empowering over time.