Skip to content

Testing

In Go, the testing package provides support for automated testing of Go packages. It is intended to be used in concert with the go test command, which automates execution of any function of the form func TestXxx(*testing.T) where Xxx does not start with a lowercase letter. The function name serves to identify the test routine. Within these functions, use the Error, Fail or related methods to signal failure.

To write a new test suite, create a file that contains the TestXxx functions and give that file a name ending in _test.go. The file will be excluded from regular package builds but will be included when the "go test" command is run. The test file can be in the same package as the one being tested, or in a corresponding package with the suffix _test.

Example

package tax

func CalculateTax(amount float64) float64 {
    if amount <= 0 {
        return 0
    }

    if amount >= 1000 && amount < 20000 {
        return 10.0
    }

    if amount >= 20000 {
        return 20.0
    }
    return 5.0
}
package tax

import (
    "testing"

    "github.com/stretchr/testify/assert"
)

func TestCalculateTax(t *testing.T) {
    result := CalculateTax(500.0)
    assert.Equal(t, result, 5.0)
}

func TestCalculateTaxBatch(t *testing.T) {
    type calcTax struct {
        amount, expect float64
    }

    table := []calcTax{
        {500.0, 5.0},
        {1000.0, 10.0},
        {1500.0, 10.0},
    }

    for _, item := range table {
        result := CalculateTax(item.amount)
        if result != item.expect {
            t.Errorf("Expected %f but got %f", item.expect, result)
        }
    }
}

Measure Test Coverage

In Go, you can measure test coverage using the command go test -coverprofile flag. This flag instructs the command to write a coverage profile to a file after running the tests. The coverage profile includes information about which parts of your code were executed during the tests.

Example

This command will run all tests in the current directory and write the coverage profile to a file named coverage.out.

go test -coverprofile=coverage.out
output
PASS
coverage: 80.0% of statements
ok      tax     0.003s

Analyze the coverage profile using the go tool cover command. This command can generate a report in different formats, such as text or HTML.

Example

This command will generate an HTML report and write it to a file named coverage.html. You can then open this file in a web browser to view the coverage report.

go tool cover -html=coverage.out -o coverage.html
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>tax: Go Coverage Report</title>
        <style>
            body {
                background: black;
                color: rgb(80, 80, 80);
            }
            body, pre, #legend span {
                font-family: Menlo, monospace;
                font-weight: bold;
            }
            #topbar {
                background: black;
                position: fixed;
                top: 0; left: 0; right: 0;
                height: 42px;
                border-bottom: 1px solid rgb(80, 80, 80);
            }
            #content {
                margin-top: 50px;
            }
            #nav, #legend {
                float: left;
                margin-left: 10px;
            }
            #legend {
                margin-top: 12px;
            }
            #nav {
                margin-top: 10px;
            }
            #legend span {
                margin: 0 5px;
            }
            .cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }

        </style>
    </head>
    <body>
        <div id="topbar">
            <div id="nav">
                <select id="files">

                <option value="file0">tax/tax.go (80.0%)</option>

                </select>
            </div>
            <div id="legend">
                <span>not tracked</span>

                <span class="cov0">not covered</span>
                <span class="cov8">covered</span>

            </div>
        </div>
        <div id="content">

        <pre class="file" id="file0" style="display: none">package tax

func CalculateTax(amount float64) float64 <span class="cov8" title="1">{
        if amount == 0 </span><span class="cov0" title="0">{
                return 0
        }</span>

        <span class="cov8" title="1">if amount &gt;= 1000 </span><span class="cov8" title="1">{
                return 10.0
        }</span>
        <span class="cov8" title="1">return 5.0</span>
}
</pre>

        </div>
    </body>
    <script>
    (function() {
        var files = document.getElementById('files');
        var visible;
        files.addEventListener('change', onChange, false);
        function select(part) {
            if (visible)
                visible.style.display = 'none';
            visible = document.getElementById(part);
            if (!visible)
                return;
            files.value = part;
            visible.style.display = 'block';
            location.hash = part;
        }
        function onChange() {
            select(files.value);
            window.scrollTo(0, 0);
        }
        if (location.hash != "") {
            select(location.hash.substr(1));
        }
        if (!visible) {
            select("file0");
        }
    })();
    </script>
</html>

Benchmarking

You can also write benchmark tests in Go, which measure the performance of a function or program. It allows you to identify potential areas for optimization and understand the impact of the changes you make to your code. The testing package in Go provides support for benchmarking, which is done by creating functions with a specific signature that takes a pointer to testing.B as its only argument.

To run the benchmark, use the go test command with the -bench flag.

Example

This command will generate an HTML report and write it to a file named coverage.html. You can then open this file in a web browser to view the coverage report.

go test -bench=.
# OR go test -bench=. -count 5 -run=^# -benchtime=5s
package tax

import "time"

func CalculateTaxB(amount float64) float64 {
    if amount == 0 {
        return 0
    }

    if amount >= 1000 {
        return 10.0
    }
    return 5.0
}

func CalculateTaxB2(amount float64) float64 {
    if amount == 0 {
        return 0
    }
    time.Sleep(time.Millisecond)
    if amount >= 1000 {
        return 10.0
    }
    return 5.0
}
package tax

import "testing"

func BenchmarkCalculateTax(b *testing.B) {
    for i := 0; i < b.N; i++ {
        CalculateTaxB(500.0)
    }
}

func BenchmarkCalculateTax2(b *testing.B) {
    for i := 0; i < b.N; i++ {
        CalculateTaxB2(500.0)
    }
}
output
goos: linux
goarch: amd64
pkg: tax
cpu: Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz
BenchmarkCalculateTax-8         1000000000               0.2814 ns/op
BenchmarkCalculateTax2-8            1084           1109872 ns/op
PASS
ok      tax     1.629s

Fuzzing

Fuzzing is a type of automated testing which continuously manipulates inputs to a program to find bugs. Fuzzing uses coverage guidance to intelligently walk through the code being fuzzed to find and report failures to the user. It can reach edge cases which humans often miss, making fuzz testing particularly valuable for finding security exploits and vulnerabilities.

```bash
go test -fuzz=. -fuzztime=5s
```
```go
package tax

func CalculateTax(amount float64) float64 {
    if amount <= 0 {
        return 0
    }

    if amount >= 1000 && amount < 20000 {
        return 10.0
    }

    if amount >= 20000 {
        return 20.0
    }
    return 5.0
}
```
```go
package tax

import "testing"

func FuzzCalculateTax(f *testing.F) {
    seed := []float64{-1, -2, -2.5, 500.0, 1000.0, 1501.0}
    for _, amount := range seed {
        f.Add(amount)
    }
    f.Fuzz(func(t *testing.T, amount float64) {
        result := CalculateTax(amount)
        if amount <= 0 && result != 0 {
            t.Errorf("Reveived %f but expected 0", result)
        }
        if amount > 20000 && result != 20 {
            t.Errorf("Reveived %f but expected 20", result)
        }
    })
}
```
```bash title="output"
fuzz: elapsed: 0s, gathering baseline coverage: 0/8 completed
fuzz: elapsed: 0s, gathering baseline coverage: 8/8 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 89077 (29688/sec), new interesting: 0 (total: 8)
fuzz: elapsed: 5s, execs: 150973 (29952/sec), new interesting: 0 (total: 8)
PASS
ok      tax     5.073s
```

Reference

  1. Go testing
  2. How To Write Unit Tests in Go
  3. Go Fuzzing