blob: 878bdebf14d89a3f9d26731beef21e99064d7021 [file] [log] [blame]
/*Package subtest provides a TestContext to subtests which handles cleanup, and
provides a testing.TB, and context.Context.
This package was inspired by
package subtest // import ""
import (
type testcase struct {
ctx context.Context
cleanupFuncs []cleanupFunc
type cleanupFunc func()
func (tc *testcase) Ctx() context.Context {
if tc.ctx == nil {
var cancel func()
tc.ctx, cancel = context.WithCancel(context.Background())
return tc.ctx
// Cleanup runs all cleanup functions. Functions are run in the opposite order
// in which they were added. Cleanup is called automatically before Run exits.
func (tc *testcase) Cleanup() {
for _, f := range tc.cleanupFuncs {
// Defer all cleanup functions so they all run even if one calls
// t.FailNow() or panics. Deferring them also runs them in reverse order.
defer f()
tc.cleanupFuncs = nil
func (tc *testcase) AddCleanup(f func()) {
tc.cleanupFuncs = append(tc.cleanupFuncs, f)
func (tc *testcase) Parallel() {
tp, ok := tc.TB.(parallel)
if !ok {
panic("Parallel called with a testing.B")
type parallel interface {
// Run a subtest. When subtest exits, every cleanup function added with
// TestContext.AddCleanup will be run.
func Run(t *testing.T, name string, subtest func(t TestContext)) bool {
return t.Run(name, func(t *testing.T) {
tc := &testcase{TB: t}
defer tc.Cleanup()
// TestContext provides a testing.TB and a context.Context for a test case.
type TestContext interface {
// AddCleanup function which will be run when before Run returns.
AddCleanup(f func())
// Ctx returns a context for the test case. Multiple calls from the same subtest
// will return the same context. The context is cancelled when Run
// returns.
Ctx() context.Context
// Parallel calls t.Parallel on the testing.TB. Panics if testing.TB does
// not implement Parallel.
var _ TestContext = &testcase{}