Source: UnitTest.
So it is like test your code with another code. But, do I really need unit test? Is it worth my time? Or when I only support legacy code that does not has unit test, why should I build one?
There are many reasons for us not to write unit test. But in fact, nobody can write bug-free code. Unit test gives us a high confidence in our code.
I will use Go for the implementation. It is very easy to write unit test in Go. Go has a built-in testing command called
go test
and a package testing which combined to give a minimal but complete testing experience.
Let’s say we have simple add
function in file add.go
.
package main
func Add(a, b int) int {
return a + b
}
func main() {
Add(3, 2)
}
The requirements to write unit test in Go:
xxx_test.go
, xxx
is your filename that you want to test..Test
and followed by the function name you want to test start with capital letter.t *testing.T
Source: T.
Here is add_test.go
, unit test for add.go
package main
import "testing"
func TestAdd(t *testing.T) {
result := Add(3, 2)
if result != 5 {
t.Errorf("Add expect %d but got %d", 5, result)
}
}
And the result will be
$ go test
PASS
ok path/to/your/package 0.007s
That means the test case you write above is passed. When someone in the future changes your function, your unit test will be broken. Let’s change the Add
function.
func Add(a, b int) int {
return a - b
}
When you run your unit test, it will be
$ go test
--- FAIL: TestAdd (0.00s)
add_test.go:8: Add expect 5 but got 1
FAIL
exit status 1
FAIL path/to/your/package 0.007s
The point is, the only reason we change the behavior of a function is when that function has a bug. In this case, unit test has saved our life before it’s too late.
Sometimes we need more than one test case to feel safe. We can improve our code this way
package main
import "testing"
func TestAdd(t *testing.T) {
testCases := []struct {
input1, input2, expected int
}{
{3, 2, 5},
{1, 1, 2},
{123, 456, 579},
}
for _, testCase := range testCases {
result := Add(testCase.input1, testCase.input2)
if result != testCase.expected {
t.Errorf("Add expect %d but got %d", testCase.expected, result)
}
}
}
Now, the problem is how do we know that our unit test has covered all the code.
Source: Code Coverage.
Code coverage in Go is a statement coverage. That means the statement percentage which covered by unit test. go test
has built in cover
function to know unit test coverage.
$ go test -cover
PASS
coverage: 50.0% of statements
ok path/to/your/package 0.007s
It says 50.0% coverage of statement. But, how do we know which statement that is missed from unit test? Luckily again, Go has numerous opensource tools by communities. In this case, we use gocov-html. It will give us beautiful html output of the go test -cover
result.
You need to install gocov
and gocov-html
first.
$ go get github.com/axw/gocov/gocov
$ go get -u gopkg.in/matm/v1/gocov-html
For the usage:
$ gocov test | gocov-html > add.html
ok path/to/your/package 0.006s coverage: 50.0% of statements
The goal is to get code coverage close to 100%. Means that you have guaranteed that your code runs as you’re desired, not afraid of someone will change your function behavior. It is really worth your time. Happy coding!