mirror of
https://github.com/alrayyes/wiki.git
synced 2024-11-25 21:16:21 +00:00
205 lines
4.5 KiB
Markdown
205 lines
4.5 KiB
Markdown
|
---
|
||
|
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/>
|