Table Driven Unit Testing
Software Engineer with an entrepreneurship gene. Love talking about code, products, UI/UX and how to build startups.
Search for a command to run...
Software Engineer with an entrepreneurship gene. Love talking about code, products, UI/UX and how to build startups.
No comments yet. Be the first to comment.
Goroutines are one of the best features of Go language. It is very easy to run a function as a goroutine, you just have to use the keyword go before the function call. Here is a very simple function which sleeps for n seconds and prints a line. pa...

In a previous post, we saw how we can use Regular expressions in Go to match and replace patterns in a string. While regexps are very useful, you might not want to them for every time you want to do some basic string manipulation. The strings package...

Go is a modern language, but doesn't use try-except blocks to handle errors. Errors are simple values that can be returned from functions and it is common to check for errors before proceeding with your next steps. Let's see the different ways to cre...

In the previous post, we saw how to write a simple unit test case and also how to write table driven test cases. In this post we will quickly see how to write benchmarks, as go supports running benchmarks on your functions as part of the standard tes...
Pro Golang Dev
14 posts
Go has built-in support for writing test cases for your code. And it is important to write as much test cases as possible to make sure you cover all possible conditions. You have to use the testing standard package to write your unit test cases.
Let's first see the simplest way to write test cases for your functions, then see how to test multiple conditions.
For this example, we will use a simple function which tests whether a given number is prime or not. The code itself isn't optimized, but we will ignore that for now.
package prime
func Prime(number uint) bool {
switch number {
case 0:
return false
case 1:
return false
}
var i uint = 2
for i < number {
if number%i == 0 {
return false
}
i++
}
return true
}
Go expects your test cases to be in a file which ends in _test.go. We will create a file named prime_test.go in the same package as our original function. In this we can have our test cases in a function which starts with Test, which allows the go test command to run these functions as test cases.
Here is a simple function to test our Prime number checker.
func TestPrime(t *testing.T) {
res := Prime(2)
if res != true {
t.Errorf("Prime(2) = %t, expected true", res)
}
}
We are checking if 2 is a prime or not. We are testing the result from the Prime function and returning an error, if the result doesn't match the expected value.
If we want to test a decent set of test cases for numbers like 0, 1, 4, 5, 6, etc., we have to write all those as separate function calls and check for the result individually.
Or we can use Table Driven Unit Testing.
If we have a list of possible test cases to test, we can create a slice of those test cases, with the expected output to avoid rewriting most of the boilerplate testing code.
First, lets create a slice of structs that we can use to store our test cases and the expected result.
var tests = []struct {
n uint
exp bool
}{
{0, false},
{1, false},
{2, true},
{3, true},
{4, false},
{5, true},
{6, false},
{49, false},
{97, true},
{997, true},
}
Since this Prime function is very simple, takes in one argument and returns a boolean, our struct matches the same data types. Now we have a list of test cases that we can iterate through and test one by one.
To do that, let's replace the TestPrime function with this new TestPrime function.
func TestPrime(t *testing.T) {
for _, e := range tests {
res := Prime(e.n)
if res != e.exp {
t.Errorf("Prime(%d) = %t, expected %t",
e.n, res, e.exp)
}
}
}
We are iterating through the tests slice, calling Prime with the number and storing the result back in a variable. Then we check the obtained result with the expected result from the struct. If they don't match, we use the Errorf function of the t object to print a nice error message.
Since the checking code and error printing code is the same, we are avoiding repeating a lot of code. Now whenever we need to add more test cases, you just append to the tests slice. This is a common pattern of writing unit test cases you would see in most Go code base.