go install golang.org/x/tools/cmd/present
present -notes 2016-09-13-subtests.slide
Subtests (and benchmarks) in 1.7 | |
September 13, 2016 | |
Tags: go, golang, testing, subtest, benchmark | |
Peter Hellberg | |
@peterhellberg | |
[email protected] | |
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)_ |
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 |
Thanks for sharing!