An Introduction to Testing in Go
🚀 My new course - The Golang Testing Bible is out now and covers everything you need to get up and running creating tests for your Go applications!
Testing is hugely important in all software. Being able to ensure the correctness of your code and ensure that any changes you make don’t end up breaking anything else in different parts of your codebase is hugely important.
By taking the time to adequately test your go programs you allow yourself to develop faster with a greater sense of confidence that what you are developing will continue to work when you release it to production.
Goals
By the end of this tutorial, you will have a good grasp of testing basic functions and methods in Go using the standard "testing" package.
You will have had experience writing table-driven tests and you will also see how to generate more verbose output from your tests using the various flags available.
Video Tutorial
This tutorial can be found in video format if you prefer:
Introduction
In this tutorial we are going to look at how you can develop and run tests for your go code using the go test command.
Go Test Files
If you have seen any go projects before, you may have noticed that most, if not all files within the project, feature a FILE_test.go counterpart within the same directory.
This is not an accident. These are the files which contain all of the unit tests for the project and they test all of the code within their counterparts.
myproject/ - calc.go - calc_test.go - main.go - main_test.go A Simple Test File
Imagine we had a very simple go program that was made up of one file and featured a calculate() function. This calculate() function simply takes in 1 number and adds 2 to it. Nice and simple to get us up and running:
package main import ( "fmt" ) // Calculate returns x + 2. func Calculate(x int) (result int) { result = x + 2 return result } func main() { fmt.Println("Hello World") } If we wished to test this we could create a main_test.go file within the same directory and write the following test:
package main import ( "testing" ) func TestCalculate(t *testing.T) { if Calculate(2) != 4 { t.Error("Expected 2 + 2 to equal 4") } } Running Our Tests
Now that we have created our first go test, it’s time to run this and see if our code behaves the way we expect it to. We can execute our tests by running:
$ go test This should then output something similar to the following:
PASS ok _/Users/elliot/Documents/Projects/tutorials/golang/go-testing-tutorial 0.007s Table Driven Testing
Now that we are happy that one calculation works, we should look to improve confidence by adding a few extra test cases into our code. If we want to gradually build up a series of test cases that are always tested, we can leverage an array of tests like so:
func TestTableCalculate(t *testing.T) { var tests = []struct { input int expected int }{ {2, 4}, {-1, 1}, {0, 2}, {-5, -3}, {99999, 100001}, } for _, test := range tests { if output := Calculate(test.input); output != test.expected { t.Error("Test Failed: {} inputted, {} expected, recieved: {}", test.input, test.expected, output) } } } Here we declare a struct that contains both an input and the expected value. We then iterate through the list of tests with our for _, test := range tests call and check to see that our function will always return the expected results, regardless of input.
When we run our test suite now, we should see the same output as before:
PASS ok _/Users/elliot/Documents/Projects/tutorials/golang/go-testing-tutorial 0.007s Verbose Test Output
Sometimes you may wish to see exactly what tests are running and how long they took. Thankfully, this is available if you use the -v flag when running your tests like so:
=== RUN TestCalculate --- PASS: TestCalculate (0.00s) === RUN TestTableCalculate --- PASS: TestTableCalculate (0.00s) PASS ok _/Users/elliot/Documents/Projects/tutorials/golang/go-testing-tutorial 0.006s You can see that both our normal test and our table test ran and passed and took less than 0.00s to execute.
Checking Test Coverage
Test coverage is a metric that I have seen abused at times by companies. These companies set targets on all of their systems such as “the codebase must be at least 75% covered by tests”.
But these kind of targets can lead to poor behavior and development teams “gaming” the system in order to try and hit these targets. I’ve seen people in some teams spend days writing unit tests that test basic getters and setters in languages such as Java.
Testing is hugely important, but you have to be pragmatic about how you test your systems so that the tests you write are providing you with the most value.
The days spent writing tests to cover unimportant parts of your codebase could have been better spent writing test cases around the critical business logic captured within your systems and ensuring more edge cases are covered.
Using the -cover flag
With the important part out of the way, let’s look at how you can check the test coverage of your system using the go test command:
Within the same directory as your main.go and your main_test.go files, run the following:
PASS coverage: 66.7% of statements ok github.com/TutorialEdge/an-intro-to-testing-in-go 0.006s You will see that you have 66.7% of your total Go code covered by test cases.
Visualizing Coverage
Whilst this 66.7% value can tell us how much of our code we have tested, it doesn’t show us exactly what code paths we have or haven’t tested.
This is where the go test and the go tool cover come in to help us solve this particular problem.
We can use the go test tool to generate a coverprofile which can then be converted to a HTML visualization using the go tool cover command:
PASS coverage: 66.7% of statements ok github.com/TutorialEdge/an-intro-to-testing-in-go 0.008s You can then take this generated coverage.out file and use it to generate a HTML page which shows exactly what lines have been covered like so:
$ go tool cover -html=coverage.out This will open up a page in your browser of choice which will look a little something like this:

As you can see, most of the code within our Calculate function is testing and features Green coverage. Whilst the print statement in your main function is Red as this hasn’t been covered by tests.
Conclusion
Hopefully you found this tutorial useful! If you require further assistance then please feel free to let me know in the comments section below.
The full source code for this article can be found here: TutorialEdge/an-intro-to-testing-in-go
Further Reading
If you enjoyed this, you may like my other articles on testing in Go: