It’s been a while since I wrote my last blog post. I am feeling thrilled to be able to make a comeback again. I hope I can hold this from now on. So, in these last couple of months, I have learned a lot, and one of them is to get into Golang. I loved this language and its capability. As part of this, I had to dive into Golang unit testing as well. However, interestingly, I found very few useful resources for me as a beginner in this area.
One of the exciting readings was this medium post, which showed some interesting approaches to writing tests. If you have a few extra minutes, go ahead and read on!
After starting with a different approach, we became stable in one approach, which I will explain today. Obviously, this is a beginner’s tutorial with the aim of giving you a quick start without wasting much time or heads around.
Golang Unit Testing Resources:
- Native Support: You will be happy to know that Golang itself has very nice testing support. So you can write your tests without using third-party tools.
- Testify Assertion Library: Though providing instrumental and powerful native support, unfortunately, Golang doesn’t offer any behavioural/assertion library. If you were about to start writing tests in Golang and haven’t found this excellent assertion library “Testify” yet, you were going to miss a lot! Besides providing assertion support, it also comes up with a test suite library, which will be very useful for you when writing tests with setup/teardown methods. This is the main library which I used for this tutorial.
Writing Tests:
Golang conventions:
- Write tests in the same directory as the source files.
- name the test files as “{source_filename}_test.go” structure
- You can name separate packages for test files. However, it is recommended that you write tests only for exported interfaces. You should use the same package name to write tests for both exported/internals.
- They should follow Pascal’s case structure name for test methods like “TestMyFunc”. A name like “TestmyFunc” wouldn’t work and not be executed at all, as it expects the following letter after ‘Test’ to be a capital letter. If you are testing internal functions starting with small letters and would like to use the same name in the test file as well, use conventions like “Test_myFunc”, which will work just fine.
Golang Test Suite Skeleton
Here is a basic skeleton of the test suite for you. Use the appropriate setup/tear-down as needed. The test suite struct is for adding necessary variables that you would like to set across setup/test methods/
type myTestSuite struct {
suite.Suite
}
func (suite *myTestSuite) SetupSuite() {
}
func (suite *myTestSuite) TearDownSuite() {
}
func (suite *myTestSuite) SetupTest() {
}
func (suite *myTestSuite) TearDownTest() {
}
func (suite *myTestSuite) TestMyFunc() {
}
func TestMyTestSuite(t *testing.T) {
tests := new(myTestSuite)
suite.Run(t, tests)
}
Code language: JavaScript (javascript)
You will notice that we are writing all our test functions as part of the struct, which will be automatically called via the Testify library. Usually, Golang passes an instance of the test. So, we are taking that in our above ‘TestMyTestSuite’ method and passing that to the testify struct we just coded. It will internally call all the test methods we have created(in this example, ‘TestMyFunc’).
Example Test method
A sample of how a test function could be is as follows:
func (suite *myTestSuite) TestMyFunc() {
assert.NotEqual(suite.T(), "foo", "bar", "oo and bar aren't equal")
assert.Equal(suite.T(), "foo", "foo", "Should pass as equal")
assert.Nil(suite.T(), nil, "expected nil value")
}
Code language: JavaScript (javascript)
If some conditions don’t look good, fail them forcefully:
assert.Fail(suite.T(), "It's failing intentionally")
Code language: JavaScript (javascript)
Skipping a test:
Sometimes, either because of a deadline or you just do not feel some tests are that important, you will want to skip them for now but come back and write them later on. If you want to skip writing some tests but want to keep track of them, use the following line:
if testing.Short() {
suite.T().Skip("Skipping this test for now")
}
Code language: JavaScript (javascript)
It will need an extra flag(‘-test.short’, which isn’t set by default) to show up in the output as skipped status, which you will find in the next section
Running Tests:
Golang provides the generic ‘go test’ method for running tests. To make it more useful, you can use something like as follows:
go test -timeout 120s -v -short
If you have multiple packages that you would like to run tests together for, use a command like this:
go test ./... -timeout 120s -v -short
Test Coverage:
It’s always best to see the test coverage to plan and write tests for the uncovered code segments. Golang also provides very nice coverage support. You will need to use two extra flags to tell the Go compiler to analyze test coverage and pass a file path name to save the coverage report.
go test -timeout 120s -v -short -cover -coverprofile=coverage.out
To see the coverage on a nice HTML view, use this command after running the above command:
go tool cover -html=coverage.out
It will have the list of all files in the package you just ran tests for and coverage details.
Final Words:
This is just a basic overview of how to get started with Golang unit testing, which might help you have a kick-ass start as a beginner! If you want to know any missing basic parts, feel free to comment, and I will update the post with more details. Happy Golang unit testing 🙂
djui says
I would suggest having a look at the `require.` package next to the `assert.` package in Testify. The difference is `assert.` uses `Fail()` but `require.` uses `FailNow()`. Not all tests can be written in a way allowing to continue after a failure. One example is: Imagine you instantiate an object, but that fails Continuing then, even though one used `Assert.NoError()` will only make the test as failed, but not stop it, leading to not very pretty panics in your test output.