Go(lang) Unit Testing for Absolute Beginners

unit-testing-go-golang

Unit testing with Go-lang is very simple and the testing library is actually included by default. For test driven development or just for covering the functionality of your code afterwards, unit tests are pretty essential.

This article is about the smallest possible effort we can make to get started with testing. No fancy algorithms or too language specific things, just the bare minimum, so we understand what’s going on and can extend from there on.

Go is a language I almost have had nothing to do with, apart from my little teaching-related program about file system permissions in Go.

Setting up your First Golang Unit Test

Let’s say we are supposed to have a function. It should output a certain text. Let’s pretend the unlikely case that the output should be: Hello Go!.

To get started let’s create a directory and create the following files:

  • hello.go
  • hello_test.go

Now we need to specify the test case for our go function:
Content of hello_test.go

package hello

import "testing"

func TestHello(t *testing.T){
  expected := "Hello Go!"
  actual := hello()
  if actual != expected {
    t.Error("Test failed")
  }
}

Firstly we name the Go package (read more about go packages) and import the testing package.

After that we define a function that is called TestHello, which has the testing package included. For each test, we would define a separate function, but since we only have one, let’s ignore that for now.

Inside the function we’re using the variables expeced and actual. The premise of a test is, that we know what a function we’re going to write is about to do. Like when we have a keyboard and an ENTER key on it, we expect that to start a new line in a text document. We’ll set the string of the expected value to "Hello Go!".

In case the actual value, that will later be returned from our fucnction, does not equal what we expect, we will display: “Test failed”.

Let’s go ahead and run go test in that directory.

The output should be something like:

hello-test $ go test
can't load package: package .:
hello.go:1:1: expected 'package', found 'EOF'

It doesn’t say our test failed, but actually just that it couldn’t process the hello.go file. Fine, let’s go ahead and write a function that returns the desired value:

package hello

func hello() string {
  return "Hello Go"
}

First of all, we make sure that the function is part of the same package and write a simple function that returns a string. Let’s run go test again.

Now we should be getting some more output:

hello-test $ go test
--- FAIL: TestHello (0.00 seconds)
    hello_test.go:9: Test failed
FAIL
exit status 1
FAIL    _/home/geronimo/projects/go/hello-test    0.001s

SUCCESS! Technically, failure, but you now have successfully run your first test in Go! The only problem is, that our error message is really bad. We would have to open up the hello_test.go file to see what might have failed.

Let’s modify that and make that a lot more informative by changing our hello_test.go files contents to:

package hello

import "testing"

func TestHello(t *testing.T){
  expected := "Hello Go!"
  actual := hello()
  if actual != expected {
    t.Errorf("Test failed, expected: '%s', got:  '%s'", expected, actual)
  }
}

Now it should output the expected and the actual value:

hello-test $ go test
--- FAIL: TestHello (0.00 seconds)
    hello_test.go:9: Test failed, expected: 'Hello Go!', got:  'Hello Go'
FAIL
exit status 1
FAIL    _/home/geronimo/projects/go/hello-test    0.001s

That is much better! We can see what the test, according to our expectations should show and what the function passes back to it with the t.Errorf function, that outputs a formatted string with the values included. Great! Now we can fix the missing exclamation mark in hello.go.

After fixing our bug, we should get the following output:

hello-test $ go test
PASS
ok      _/home/geronimo/projects/go/hello-test    0.001s

Now you know how to unit test in Go(lang) and use them to your advantage!

Testing a Function in Go with Arguments

Typically you though would want to test something that takes an argument and returns something else.

If we now want to make our hello() function a bit more universal to, let’s say, include Perl programmers, we can edit the files as in the following example to unit test functions with parameters:

content of hello_test.go:

package hello

import "testing"

func TestHello(t *testing.T){
  expected := "Hello Go!"
  actual := hello("Perl")
  if actual != expected {
    t.Errorf("Test failed, expected: '%s', got:  '%s'", expected, actual)
  }
}

content of hello.go:

package hello

func hello(input string) string {
  output := "Hello ";
  output += input;
  output += "!";
  return output
}

This will enable us to have some function arguments and thereby dynamic input, that we can model our tests around. Now we could also create an array of different words or characters to pass to our function and they should all come back prefixed with “Hello ” and ending with “!”.

Summary

So this is the simplest example in the world, for getting started writing tests for your Go project. I was annoyed with some other posts existing around this topic, because they are not very intuitive to newcomers to the language, but touch many other topics, not just the first baby step basics. A tutorial I can recommend is the piglatin one by Mat Ryer!

I hope you enjoyed this and let me know if I should improve on this post. As usual you can find me on various social networks and I’d love your feedback!

7 thoughts on “Go(lang) Unit Testing for Absolute Beginners”

Leave a Reply

Your email address will not be published. Required fields are marked *