Documentation
¶
Overview ¶
Package testo is a modular testing framework built on top of testing.T. It is focused on suite based tests and has an extensive plugin system.
Quick Start ¶
A minimal working example looks like this:
package main
import (
"testing"
"github.com/ozontech/testo"
)
func Test(t *testing.T) {
testo.RunTest(t, func(t *testo.T) {
t.Log("Hello, Testo!")
})
}
Plugins ¶
Plugins are the core feature of Testo. Plugins can generate reports, add custom methods to T, override built-in methods, plan test execution and more.
Plugins are installed by defining our own T with embedded T and plugins:
type T struct {
*testo.T
*myplugin.PluginFoo
*myplugin.PluginBar
}
func Test(t *testing.T) {
testo.RunTest(t, func(t T) {
t.Log("Hello, Testo!")
})
}
Notice that we now use our T instead of *testo.T in a test.
Suites & Standalone Tests ¶
Testo supports several ways to run tests.
Suites:
type Suite struct { testo.Suite[T] }
func (Suite) TestFoo(t T) { t.Log("Foo") }
func (Suite) TestBar(t T) { t.Log("Bar") }
func Test(t *testing.T) {
testo.RunSuite(t, new(Suite))
}
Standalone:
func TestFoo(t *testing.T) {
testo.RunTest(t, func(t T) {
t.Log("Foo")
})
}
func Test(t *testing.T) {
t.Run("Foo", testo.Test(func(t T) {
t.Log("Foo")
}))
t.Run("Bar", testo.Test(func(t T) {
t.Log("Bar")
}))
}
Index ¶
- func For[Suite suite[T], T CommonT](test func(Suite, T), options ...testoplugin.Option) struct{}
- func ForEach[Suite suite[T], T CommonT, P any](test func(Suite, T, P), options ...testoplugin.Option) struct{}
- func Options(options ...testoplugin.Option) struct{}
- func Reflect(t CommonT) testoreflect.Reflection
- func Run[T CommonT](t T, name string, f func(t T), options ...testoplugin.Option) bool
- func RunSubSuite[Suite suite[Sub], Parent, Sub CommonT](t Parent, suite Suite, options ...testoplugin.Option) bool
- func RunSuite[Suite suite[T], T CommonT](testingT TestingT, suite Suite, options ...testoplugin.Option) bool
- func RunTest[T CommonT](testingT TestingT, f func(t T), options ...testoplugin.Option) bool
- func Test[T CommonT](f func(t T), options ...testoplugin.Option) func(*testing.T)
- type CommonT
- type Suite
- type T
- func (t *T) Chdir(dir string)
- func (t *T) Cleanup(f func())
- func (t *T) Context() context.Context
- func (t *T) Deadline() (time.Time, bool)
- func (t *T) Error(args ...any)
- func (t *T) Errorf(format string, args ...any)
- func (t *T) Fail()
- func (t *T) FailNow()
- func (t *T) Failed() bool
- func (t *T) Fatal(args ...any)
- func (t *T) Fatalf(format string, args ...any)
- func (t *T) Log(args ...any)
- func (t *T) Logf(format string, args ...any)
- func (t *T) Name() string
- func (t *T) Parallel()
- func (*T) Plugin(testoplugin.Plugin, ...testoplugin.Option) testoplugin.Spec
- func (t *T) Setenv(key, value string)
- func (t *T) Skip(args ...any)
- func (t *T) SkipNow()
- func (t *T) Skipf(format string, args ...any)
- func (t *T) Skipped() bool
- func (t *T) TempDir() string
- type TestingT
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func For ¶
func For[Suite suite[T], T CommonT]( test func(Suite, T), options ...testoplugin.Option, ) struct{}
For applies static options (annotations) for a given test.
Test annotations is a slice of options to be passed for this test. Test annotations are available to plugins before running an actual test, thus enhancing test planning features.
Multiple annotation calls to the same test will append options.
testo.For(MySuite.TestFoo, someplugin.WithThis(...), otherplugin.WithThat(...)) testo.For((*Suite).TestFoo, myplugin.WithRetry())
NOTE: it returns an empty struct{} value to enable the following usage:
var _ = testo.For(MySuite.TestFoo, someplugin.WithSomeOption(true))
func (MySuite) TestFoo(t T) { ... }
That "var _ =" construction would not be possible otherwise. This is slightly less verbose than using an init function:
func init() {
testo.For(MySuite.TestFoo, someplugin.WithSomeOption(true))
}
func ForEach ¶
func ForEach[Suite suite[T], T CommonT, P any]( test func(Suite, T, P), options ...testoplugin.Option, ) struct{}
ForEach applies static options (annotations) for each case of a given parametrized test.
Similar to For, but for parametrized tests.
testo.ForEach(MySuite.TestFoo, someplugin.WithThis(...), otherplugin.WithThat(...)) testo.ForEach((*Suite).TestFoo, myplugin.WithRetry())
func Options ¶
func Options(options ...testoplugin.Option) struct{}
Options appends given options to the global options.
Global options are prepended to each RunSuite & RunTest call.
func init() {
testo.Options(myplugin.OutputDir("..."))
}
It returns an empty struct to enable the following usage:
var _ = testo.Options(...)
This is similar to For and slightly more concise than using init.
func Reflect ¶
func Reflect(t CommonT) testoreflect.Reflection
Reflect returns meta information about given t.
You can reflect over any test by accessing its T instance:
func (Suite) TestFoo(t T) {
r := testo.Reflect(t)
// r stores Reflection struct.
}
Same logic applies for plugins. If a plugin embeds `*testo.T` it can call the same testo.Reflect function:
type Plugin struct{ *testo.T }
func (p *Plugin) Plugin(parent testoplugin.Plugin, options ...testoplugin.Options) testoplugin.Spec {
return testoplugin.Spec{
Hooks: testoplugin.Hooks{
BeforeEach: testoplugin.Hook{
Func: func() { testo.Reflect(p) }
}
}
}
}
func Run ¶
func Run[T CommonT]( t T, name string, f func(t T), options ...testoplugin.Option, ) bool
Run runs f as a subtest of t called name. It runs f in a separate goroutine and blocks until f returns or calls t.Parallel to become a parallel test. Run reports whether f succeeded (or at least did not fail before calling t.Parallel).
Run may be called simultaneously from multiple goroutines, but all such calls must return before the outer test function for t returns.
WARN: Running this function during t.Cleanup panics.
func RunSubSuite ¶ added in v1.2.0
func RunSubSuite[Suite suite[Sub], Parent, Sub CommonT]( t Parent, suite Suite, options ...testoplugin.Option, ) bool
RunSubSuite runs a sub-suite.
This is similar to RunSuite but designed to be called from other suites.
RunSubSuite reports whether all sub-suite tests succeeded.
NOTE: this function may cause infinite loop if called within the same suite as passed to it.
func RunSuite ¶
func RunSuite[Suite suite[T], T CommonT]( testingT TestingT, suite Suite, options ...testoplugin.Option, ) bool
RunSuite runs tests under a suite.
Test is defined as a suite method in the form of "TestXXX" or "Test" which accepts a single parameter of the same type as T passed to this function.
It also accepts options for the plugins which can be used to configure those plugins. See testoplugin.Option.
RunSuite reports whether suite succeeded.
func RunTest ¶ added in v1.3.0
func RunTest[T CommonT]( testingT TestingT, f func(t T), options ...testoplugin.Option, ) bool
RunTest runs a single test without a suite.
Under the hood it constructs a special singleton suite with one test, named as the parent test, and calls RunSuite.
Examples ¶
func TestFoo(t *testing.T) {
testo.RunTest(t, func(t T) {
t.Log("Hi")
})
}
In the example above plugins would see this test as a suite with a single TestFoo method.
See also Test as a syntax sugar to run a named test:
func TestFoo(t *testing.T) {
t.Run("named-test", testo.Test(func(t T) {
t.Log("Hi")
}))
}
Options ¶
This function accepts plugin options, see testoplugin.Option. Passed options are treated as test scoped, not suite scoped.
Note ¶
Running this function more than once inside the same test means rerunning the same test, not running several different tests. If you want to run several suite-less tests from a single test see Test.
RunTest reports whether f succeeded.
func Test ¶ added in v1.3.0
func Test[T CommonT](f func(t T), options ...testoplugin.Option) func(*testing.T)
Test constructs a new test ready to run as a native testing test.
Examples ¶
func Test(t *testing.T) {
t.Run("My awesome test", testo.Test(func(t T) {
// your test goes here
}))
}
This is syntactic sugar for a more verbose RunTest API:
func Test(t *testing.T) {
t.Run("My awesome test", func(t *testing.T) {
testo.RunTest(t, func(t T) {
// your test goes here
})
})
}
Options ¶
This function accepts plugin options, see testoplugin.Option. Passed options are treated as test scoped, not suite scoped.
Types ¶
type CommonT ¶
type CommonT interface {
// contains filtered or unexported methods
}
CommonT is the interface common for all T derivatives.
type Suite ¶
type Suite[T CommonT] struct { // contains filtered or unexported fields }
Suite is the base suite that all user-defined suites must embed.
Suite may optionally provide hooks by implementing their methods:
- BeforeAll(T) - is called before all suite tests once.
- BeforeEach(T) - is called before each suite test. T is shared with an actual test.
- AfterEach(T) - is called after each suite test. T is shared with an actual test.
- AfterAll(T) - is called after all suite tests once.
Example:
type MySuite struct {
testo.Suite[MyT]
}
type T ¶
type T struct {
// contains filtered or unexported fields
}
T is a wrapper for testing.T. This is a core entity in testo and used as a testing.T replacement.
The common pattern is to embed it into new struct type:
type MyT struct {
*testo.T
*SomePlugin
}
Plugins can also optionally embed it - testo will automatically initialize it by sharing the same value as an actual currently running test's T.
type SomePlugin struct { *testo.T }
func (*T) Chdir ¶ added in v1.1.0
Chdir calls os.Chdir and uses Cleanup to restore the current working directory to its original value after the test. On Unix, it also sets PWD environment variable for the duration of the test.
Because Chdir affects the whole process, it cannot be used in parallel tests or tests with parallel ancestors.
func (*T) Cleanup ¶ added in v1.1.0
func (t *T) Cleanup(f func())
Cleanup registers a function to be called when the test (or subtest) and all its subtests complete. Cleanup functions will be called in last added, first called order.
func (*T) Context ¶
Context returns a context that is canceled just before Cleanup-registered functions are called.
Cleanup functions can wait for any resources that shut down on context.Context.Done before the test completes.
func (*T) Deadline ¶
Deadline reports the time at which the test binary will have exceeded the timeout specified by the -timeout flag.
By default, the ok result is false if the -timeout flag indicates "no timeout" (0).
func (*T) Fail ¶
func (t *T) Fail()
Fail marks the function as having failed but continues execution.
func (*T) FailNow ¶
func (t *T) FailNow()
FailNow marks the function as having failed and stops its execution by calling runtime.Goexit (which then runs all deferred calls in the current goroutine). Execution will continue at the next test or benchmark. FailNow must be called from the goroutine running the test or benchmark function, not from other goroutines created during the test. Calling FailNow does not stop those other goroutines.
func (*T) Log ¶
Log formats its arguments using default formatting, analogous to Println, and records the text in the error log. For tests, the text will be printed only if the test fails or the -test.v flag is set. For benchmarks, the text is always printed to avoid having performance depend on the value of the -test.v flag.
func (*T) Logf ¶
Logf formats its arguments according to the format, analogous to Printf, and records the text in the error log. A final newline is added if not provided. For tests, the text will be printed only if the test fails or the -test.v flag is set. For benchmarks, the text is always printed to avoid having performance depend on the value of the -test.v flag.
func (*T) Name ¶
Name returns the name of the running (sub-) test or benchmark.
The name will include the name of the test along with the names of any nested sub-tests. If two sibling sub-tests have the same name, Name will append a suffix to guarantee the returned name is unique.
func (*T) Parallel ¶
func (t *T) Parallel()
Parallel signals that this test is to be run in parallel with (and only with) other parallel tests. When a test is run multiple times due to use of -test.count or -test.cpu, multiple instances of a single test never run in parallel with each other.
func (*T) Plugin ¶
func (*T) Plugin(testoplugin.Plugin, ...testoplugin.Option) testoplugin.Spec
Plugin implements testoplugin.Plugin.
This is a placeholder to prevent other .Plugin methods being promoted.
func (*T) Setenv ¶
Setenv calls os.Setenv(key, value) and uses Cleanup to restore the environment variable to its original value after the test.
Because Setenv affects the whole process, it cannot be used in parallel tests or tests with parallel ancestors.
func (*T) SkipNow ¶
func (t *T) SkipNow()
SkipNow marks the test as having been skipped and stops its execution by calling runtime.Goexit. If a test fails (see Error, Errorf, Fail) and is then skipped, it is still considered to have failed. Execution will continue at the next test or benchmark. See also FailNow. SkipNow must be called from the goroutine running the test, not from other goroutines created during the test. Calling SkipNow does not stop those other goroutines.
func (*T) TempDir ¶
TempDir returns a temporary directory for the test to use. The directory is automatically removed when the test and all its subtests complete. Each subsequent call to t.TempDir returns a unique directory; if the directory creation fails, TempDir terminates the test by calling Fatal.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
pragma
Package pragma provides types that can be embedded into a struct to statically enforce or prevent certain language properties.
|
Package pragma provides types that can be embedded into a struct to statically enforce or prevent certain language properties. |
|
testnamer
Package testnamer copies test naming functionality as seen in go test.
|
Package testnamer copies test naming functionality as seen in go test. |
|
Package testocache provides caching primitives to be used by external plugins.
|
Package testocache provides caching primitives to be used by external plugins. |
|
Package testoplugin provides plugin primitives for using plugins in testo.
|
Package testoplugin provides plugin primitives for using plugins in testo. |
|
Package testoreflect provides reflection primitives for T.
|
Package testoreflect provides reflection primitives for T. |
