Skip to content

Instantly share code, notes, and snippets.

@peterhellberg
Last active July 22, 2017 22:25
Show Gist options
  • Save peterhellberg/2f2f98739a990dc5c5969b00e9e4fef9 to your computer and use it in GitHub Desktop.
Save peterhellberg/2f2f98739a990dc5c5969b00e9e4fef9 to your computer and use it in GitHub Desktop.
Slides from my presentation on Subtests (and benchmarks) in 1.7 at the Go Stockholm September meetup.
Subtests (and benchmarks) in 1.7
September 13, 2016
Tags: go, golang, testing, subtest, benchmark
Peter Hellberg
@peterhellberg
peter@c7.se
https://c7.se
* Go < 1.7
* In the past
We used to create a new test function for each test case, then we switched
over to [[https://github.com/golang/go/wiki/TableDrivenTests][table-driven]] tests, and that was just fine…
* Go 1.7 🎉
* Now
We got a new way to write our tests.
* Subtests and Sub-benchmarks
The Run methods of T and B allow defining subtests and sub-benchmarks, without
having to define separate functions for each.
This enables uses like table-driven benchmarks and creating hierarchical
tests. It also provides a way to share common setup and tear-down code:
func TestFoo(t *testing.T) {
// <setup code>
t.Run("A=1", func(t *testing.T) { ... })
t.Run("A=2", func(t *testing.T) { ... })
t.Run("B=1", func(t *testing.T) { ... })
// <tear-down code>
}
* Unique name
Each subtest and sub-benchmark has a unique name: the combination of the name
of the top-level test and the sequence of names passed to Run, separated by
slashes, with an optional trailing sequence number for disambiguation.
The argument to the -run and -bench command-line flags is a slash-separated
list of regular expressions that match each name element in turn. For
example:
go test -run Foo # Run top-level tests matching "Foo".
go test -run Foo/A= # Run subtests of Foo matching "A=".
go test -run /A=1 # Run all subtests of a top-level test matching "A=1".
* Control parallelism
Subtests can also be used to control parallelism. A parent test will only complete once all of its subtests complete. In this example, all tests are run in parallel with each other, and only with each other, regardless of other top-level tests that may be defined:
func TestGroupedParallel(t *testing.T) {
for _, tc := range tests {
tc := tc // capture range variable
t.Run(tc.Name, func(t *testing.T) {
t.Parallel()
...
})
}
}
* Cleanup after parallel tests
Run does not return until parallel subtests have completed, providing a way to clean up after a group of parallel tests:
func TestTeardownParallel(t *testing.T) {
// This Run will not return until the parallel tests finish.
t.Run("group", func(t *testing.T) {
t.Run("Test1", parallelTest1)
t.Run("Test2", parallelTest2)
t.Run("Test3", parallelTest3)
})
// <tear-down code>
}
* Example
.code example_test.go
* UltiSnips trun & brun
.code go.snippets
.link https://github.com/peterhellberg/snippets/blob/master/UltiSnips/go.snippets
_(I_have_talked_with_@fatih_about_possibly_including_these_in_vim-go_directly)_

Subtests (and benchmarks) in 1.7

Install present

go install golang.org/x/tools/cmd/present

Run the presentation

present -notes 2016-09-13-subtests.slide

package main
import (
"fmt"
"testing"
)
func add(a, b int) int {
return a + b
}
func TestAdd(t *testing.T) {
for _, tt := range []struct{ a, b, want int }{
{1, 1, 2},
{2, 3, 5},
} {
t.Run(fmt.Sprintf("%d+%d", tt.a, tt.b), func(t *testing.T) {
if got := add(tt.a, tt.b); got != tt.want {
t.Fatalf("add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
priority -10
# sub test function
snippet trun "t.Run(name, func(t *testing.T) { ... })" bA
t.Run("${1}", func(t *testing.T) {
${2:${VISUAL}}
})
endsnippet
# sub benchmark function
snippet brun "b.Run(name, func(b *testing.B) { ... })" bA
b.Run("${1}", func(b *testing.B) {
${2:${VISUAL}}
})
endsnippet
@aggrolite
Copy link

Thanks for sharing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment