This page looks best with JavaScript enabled

Go unit tests

 ·  🎃 kr0m

As important as programming new features is ensuring that nothing is broken with each change; fortunately, Go includes everything necessary to run tests quickly and conveniently.


If you’re new to the world of Go, I recommend the following previous articles:


To achieve reproducible builds, Go recommends using similar versions of Go. To do this, we can either manually generate the go.mod file or initialize a module, and Go will generate it for us:

mkdir go_testing
cd go_testing/
go mod init name

We can view the contents of the go.mod file by running:

cat go.mod

module name

go 1.20

Reproducible Build:

Including a go.mod file in your project can contribute to reproducible builds. This means that, when sharing your code with other developers or running tests in different environments, the same versions of dependencies will be used, helping to avoid compatibility issues.

As an example, we will simply have a function called Hello. If it receives a non-empty string as input, it will return [string, nil]. If an empty input is provided, it will return ["", ERROR].

vi name.go

package main

import (
    "errors"
    "fmt"
)

func Hello(name string) (string, error) {
    if name == "" {
        return name, errors.New("Empty name")
    }
    return name, nil
}

func main() {
    name, error := Hello("Kr0m")
    if error != nil {
        fmt.Println("ERROR:", error)
    } else {
        fmt.Println("Name:", name)
    }
}

We create the unit test that will verify that calling the Hello() function returns the correct value:

vi name_test.go

package main

import (
    "testing"
    "regexp"
)

// Test Hello() function with input string:
func TestHelloName(t *testing.T) {
    name := "Kr0m" // Function input
    want := regexp.MustCompile(`\b`+name+`\b`) // What we expect
    msg, err := Hello(name) // Save Hello() returned values
    // Compare want with msg OR an error has ocurred:
    if !want.MatchString(msg) || err != nil {
        t.Fatalf(`Hello(name) = %q, %v, want match for %#q, nil`, msg, err, want)
    }
}

// Test Hello() function with empty string:
func TestHelloEmpty(t *testing.T) {
    msg, err := Hello("") // Save Hello() returned values
    // If msg is not empty string OR err NULL:
    if msg != "" || err == nil {
        t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
    }
}

We pass the tests without any issues:

go test

PASS
ok  	name	0.002s

We introduce a bug, causing the function to always return [AAAA, nil]:

vi name.go

package main

import (
    "errors"
    "fmt"
)

func Hello(name string) (string, error) {
    if name == "" {
        return name, errors.New("Empty name")
    }
    name = "AAAA"           // BUG
    return name, nil
}

func main() {
    name, error := Hello("Kr0m")
    if error != nil {
        fmt.Println("ERROR:", error)
    } else {
        fmt.Println("Name:", name)
    }
}

We pass the tests again:

go test

We can see that this time it has failed:

--- FAIL: TestHelloName (0.00s)
    name_test.go:15: Hello(name) = "AAAA", <nil>, want match for `\bKr0m\b`, nil
FAIL
exit status 1
FAIL	name	0.002s

Line 15 at name_test:

if !want.MatchString(msg) || err != nil {
    t.Fatalf(`Hello(name) = %q, %v, want match for %#q, nil`, msg, err, want)
}

Indeed, want doesn’t match msg:

name := "Kr0m"
want := regexp.MustCompile(`\b`+name+`\b`)
// msg content: AAAA
If you liked the article, you can treat me to a RedBull here