---
date: 2020-08-26
id: d8edb970-fe2c-4774-b325-2f932b262ef5
title: Tests in Golang
---

# Rules

Test writing rules:

-   It needs to be in a file with a name like \`xxx~test~.go\`
-   The test funciton must start with the word \`Test\`
-   The test function takes one argument only \`t \*testing.T\`

``` go
package main

import "testing"

func TestHello(t *testing.T) {
    assertCorrectMessage := func(t *testing.T, got, want string) {
        t.Helper()
        if got != want {
            t.Errorf("got %q want %q", got, want)
        }
    }

    t.Run("saying hello to people", func(t *testing.T) {
        got := Hello("Chris", "")
        want := "Hello, Chris"
        assertCorrectMessage(t, got, want)
    })

    t.Run("empty string defaults to 'World'", func(t *testing.T) {
        got := Hello("", "")
        want := "Hello, World"
        assertCorrectMessage(t, got, want)
    })

    t.Run("in Spanish", func(t *testing.T) {
        got := Hello("Elodie", "Spanish")
        want := "Hola, Elodie"
        assertCorrectMessage(t, got, want)
    })

    t.Run("in French", func(t *testing.T) {
        got := Hello("Jean Pierre", "French")
        want := "Bonjour, Jean Pierre"
        assertCorrectMessage(t, got, want)
    })

    t.Run("in Dutch", func(t *testing.T) {
        got := Hello("Frans", "Dutch")
        want := "Hoi, Frans"
        assertCorrectMessage(t, got, want)
    })

}
```

# Examples

Examples can also be added to ~test~.go files.

``` go
func ExampleAdd() {
    sum := Add(1, 5)
    fmt.Println(sum)
    // Output: 6
}
```

Example function will not be execute if the comment is removed

# Benchmarking

[Benchmarks](https://golang.org/pkg/testing/#hdr-Benchmarks) are a
first-class feature of Go, fantastic stuff!

``` go
func BenchmarkRepeat(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Repeat("a")
    }
}
```

Following command runs benchmarks:

``` shell
go test -bench=.
```

# Tools

## Coverage

[Coverage](https://blog.golang.org/cover) is built in as well:

``` shell
go test -cover
```

## Race conditions

In Go you can detect race conditions by adding the `-race` argument:

``` shell
go test -race
```

# DeepEqual

For \`slices\` & friends you can use \`reflect.DeepEqual\` to compare
variables in tests

``` go
func TestSumAll(t *testing.T) {

    got := SumAll([]int{1, 2}, []int{0, 9})
    want := []int{3, 9}

    if !reflect.DeepEqual(got, want) {
        t.Errorf("got %v want %v", got, want)
    }
}
```

# TableDrivenTests

Writing good tests is not trivial, but in many situations a lot of
ground can be covered with [table-driven
tests](https://github.com/golang/go/wiki/TableDrivenTests): Each table
entry is a complete test case with inputs and expected results, and
sometimes with additional information such as a test name to make the
test output easily readable. If you ever find yourself using copy and
paste when writing a test, think about whether refactoring into a
table-driven test or pulling the copied code out into a helper function
might be a better option.

Given a table of test cases, the actual test simply iterates through all
table entries and for each entry performs the necessary tests. The test
code is written once and amortized over all table entries, so it makes
sense to write a careful test with good error messages.

``` go
var flagtests = []struct {
    in  string
    out string
}{
    {"%a", "[%a]"},
    {"%-a", "[%-a]"},
    {"%+a", "[%+a]"},
    {"%#a", "[%#a]"},
    {"% a", "[% a]"},
    {"%0a", "[%0a]"},
    {"%1.2a", "[%1.2a]"},
    {"%-1.2a", "[%-1.2a]"},
    {"%+1.2a", "[%+1.2a]"},
    {"%-+1.2a", "[%+-1.2a]"},
    {"%-+1.2abc", "[%+-1.2a]bc"},
    {"%-1.2abc", "[%-1.2a]bc"},
}
func TestFlagParser(t *testing.T) {
    var flagprinter flagPrinter
    for _, tt := range flagtests {
        t.Run(tt.in, func(t *testing.T) {
            s := Sprintf(tt.in, &flagprinter)
            if s != tt.out {
                t.Errorf("got %q, want %q", s, tt.out)
            }
        })
    }
}
```

# Fatals

Sometimes you want to throw fatal errors in tests to prevent problems,
for example in case \`nil\` is returned and you need to do stuff with
the return value in later tests:

``` go
func assertError(t *testing.T, got error, want error) {
    t.Helper()
    if got == nil {
        t.Fatal("didn't get an error but wanted one")
    }

    if got != want {
        t.Errorf("got %q, want %q", got, want)
    }
}
```

# Handy packages

-   httptest[^1]
-   quick[^2]

# Footnotes

[^1]: <https://golang.org/pkg/net/http/httptest/>

[^2]: <https://golang.org/pkg/testing/quick/>
