# Ben's Corner

Site: Repo | Blog | RSS

# Go Notes

These are things I want to remember in Go

## Notes on Testing

Some notes on testing in Go. A lot of these notes came from Advanced Testing in Go (Hashimoto) (transcipt).

### Testing Private and Public APIs

Test files for a package's publicly visible API should be named <package>_ext_test.go and start with package <package>_test.

Test files for a package's internal API should be named <package>_int_test.go and start with package <package>

### Comparing Values Naming Convention

Go's testing library wants you to do a lot of comparisons. The naming convention I want to use for these comparisons (taken from testify) is to call the value that I expect expectedXXX and always put it on the left side of the comparison, and the value that I actually got actualXXX, and always put it on the right side of the comparison:

if expectedThing != actualThing {
t.Fatalf("Oopsie")
}


### Degrees of Failure

The testing library has a couple ways to fail:

Fail marks the current test as failed, but continues the execution of the current test.

Error/Errorf is equivalent to Log/Logf followed by Fail.

FailNow marks the current test as failed, and stops execution of the current test.

Fatal/Fatalf is equivalent to Log/Logf followed by FailNow.

### testify

testify is super nice for comparing things because it writes most of my if statements for me. I can do:

// Mark current test as failed, but continue current test
assert.Equal(t, expectedValue, actualValue)

// Mark current test as failed, exit current test
require.Equal(t, expectedValue, actualValue)


### Table Driven Tests

Test the same logic on different data!

Here's a simple example - note that in lieu of testing what's in the potential error, I simply assert that it's nil or not nil. For this particular, this is "enough" to satisfy me. Other tests might require more detailed comparisons.

package main

import (
"errors"
"testing"

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

func AddOne(a int) (int, error) {
if a == 3 {
return 0, errors.New("We don't like the number 3")
}
return a + 1, nil
}

tests := []struct {
name        string
a           int
expectedSum int
expectedErr bool
}{
{name: "first", a: 1, expectedSum: 2, expectedErr: false},
{name: "second", a: 2, expectedSum: 4, expectedErr: false},
{name: "third", a: 3, expectedSum: 0, expectedErr: true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.expectedErr {
require.NotNil(t, actualErr)
} else {
require.Nil(t, actualErr)
}
require.Equal(t, tt.expectedSum, actualSum)
})
}
}


When run, we see that the function is incorrect for the test data provided (or, more likely in this case, we need to correct the test data). testify gives us a super helpful error message.

$go test ./... --- FAIL: TestAddOne (0.00s) --- FAIL: TestAddOne/second (0.00s) main_test.go:37: Error Trace: main_test.go:37 Error: Not equal: expected: 4 actual : 3 Test: TestAddOne/second FAIL FAIL github.com/bbkane/hello_testing 0.173s FAIL  ### Data Files When a test depends on a data file, Go will read it from a file path relative to the test. I like to stick my files for a test in a testdata/TestName.xxx file right next to the test. Then, within the test, I can ioutil.Readfile('testdata/TestName.xxx') to get the data. If each sub test in a table-driven test, needs a file, then I use testdata/TestName/SubTestName.xxx. ### Golden Files Sometimes, I want to test that a function outputs the correct bytes - like a file, an HTTP response, or the --help output from the CLI parsing library I'm writing. In these cases, it's helpful to make the tests take an -update flag, and write the bytes to a file when it is passed. Then I can manually read those files to ensure correctness, commit them, and make the test check the bytes generated to the file when the -update flag is not passed. An example: package main_test import ( "bytes" "flag" "io" "io/ioutil" "os" "path/filepath" "testing" "github.com/stretchr/testify/require" ) func Write(w io.Writer) error { _, err := w.Write([]byte("hola\n")) return err } var update = flag.Bool("update", false, "update golden files") func TestWrite(t *testing.T) { var actualBuffer bytes.Buffer actualErr := Write(&actualBuffer) require.Nil(t, actualErr) golden := filepath.Join("testdata", t.Name()+".golden.txt") if *update { mkdirErr := os.MkdirAll("testdata", 0700) require.Nil(t, mkdirErr) writeErr := ioutil.WriteFile(golden, actualBuffer.Bytes(), 0600) require.Nil(t, writeErr) t.Logf("Wrote: %v\n", golden) } expectedBytes, readErr := ioutil.ReadFile(golden) require.Nil(t, readErr) require.Equal(t, expectedBytes, actualBuffer.Bytes()) }  When running this test the first time, I get the following error: $ go test golden_test.go
--- FAIL: TestWrite (0.00s)
golden_test.go:37:
Error Trace:	golden_test.go:37
Error:      	Expected nil, but got: &fs.PathError{Op:"open", Path:"testdata/TestWrite.golden.txt", Err:0x2}
Test:       	TestWrite
FAIL
FAIL	command-line-arguments	0.095s
FAIL


However, I can then use the -update flag to write the file (I'm also using the -test.v flag to show the log I have)

$go test golden_test.go -test.v -update === RUN TestWrite golden_test.go:32: Wrote: testdata/TestWrite.golden.txt --- PASS: TestWrite (0.00s) PASS ok command-line-arguments 0.090s  Then manually inspect it to ensure the function is correct: $ cat testdata/TestWrite.golden.txt
hola


Then run the test again to see it pass:

\$ go test golden_test.go
ok  	command-line-arguments	0.096s


## Code examples

Code examples can be added to tests and also show up in the docs! See the Go blog for more details, or see my example below:

package main_test

import "fmt"

func ExampleExample() {
fmt.Println("hello!")
// Output: hello!
}