Its been long since I wrote my last blog post. I am feeling really happy to be able to make a come back again. 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 getting into golang. I loved this language and its capability. As part, I had to dive into golang unit testing as well. However, interestingly, I did find very few resources that are useful for me as beginner on this area.
One of the interesting reading was this medium post which showed some interesting approach in writing tests. If you have few extra minutes, go ahead and read on!
After starting with different approach, we did able to become stable in one approach, that I am gonna explain today. Obviously, this is a beginners tutorial in aim that you have a quick start without wasting much time or heads around.
Golang Unit Testing Resources:
- Native Support: Well, you will be happy to know that, golang itself has a very nice support for testing. So, you can write your tests even without any third-party tools at all.
- Testify Assertion Library: Though providing very useful and powerful native support, unfortunately golang doesn’t provide any behavioral/assertion library. If you were about to start writing tests in golang and haven’t found this awesome assertion library, testify yet, you were gonna miss a lot! Besides providing assertion support, it also comes up with a test suite library, which will be very useful for you to write tests with setup/teardown methods. Basically, this is main library which I used for this tutorial.
Writing Tests:
Golang conventions:
- Write tests in same directory as the source files are in.
- name the test files as “{source_filename}_test.go” structure
- You can name separate package for test files. But it only recommended if you are writing tests for exported interfaces only. To write tests for both exported/internals, you should use same package name.
- For test methods, they should follow pascal case structure name like “TestMyFunc”. Name like “TestmyFunc” wouldn’t work and not exectued at all, as it expects the next letter after ‘Test’ to be capital letter. If you are testing internal functions which starts with small letters and you would like to use the same name in test file as well, use convention 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 per your need. and 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) }
You will notice that, we are writing all our test functions as part of the struct, which will be automatically called via testify library. Usually, in golang passes and 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") }
If for some conditions things doesn’t look good, fail them forcefully:
assert.Fail(suite.T(), "It's failing intentionally")
Skipping a test:
Sometimes, either because of deadline or you just not feeling some tests are that important, you will might 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, use following line:
if testing.Short() { suite.T().Skip("Skipping this test for now") }
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 next section
Running Tests:
to run tests, golang provides the generic ‘go test’ method. To make it more useful, you can use something like as follows:
go test -timeout 120s -v -short
If you have multiple package 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 so that you can plan and write tests for the uncovered code segments. Golang also comes up with a very nice support for coverage as well. You will need to use two extra flags to tell go compiler to analyze test coverage and pass a file path name in which it will 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 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 about how to get started with golang unit testing, which might be in your help to have a kick ass start as beginner! If any basic parts is missing that you would like to know, feel free to comment and I will update the post with more details. Happy coding 🙂
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.