Unit Test: Is It Worth Your Time ?

 • 

Source: Unit Test.

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 is nobody can write bug free code. Unit test help us speed up time to identify bug because it will refer to specific unit of code.

Lucky for us, we use Go as our main language. 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:

  • The file must be named xxx_test.go, xxx is your filename that you want to test..
  • The function name begin with Test and followed by the function name you want to test start with capital letter.
  • The only parameter needed is t *testing.T

Source: testing - The Go Programming Language.

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. In Tokopedia, we pause the builds if coverage is below 30%.

Source: Qasim Zaidi.

Source: Code Coverage.

Code coverage in Go is 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!