From b5bae7b65d6af6ff80dfd1a5d972084cba4e10a9 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Mon, 22 Nov 2021 03:07:18 +0000 Subject: [PATCH] docs(godoc): improve godoc comments for all *T methods --- t.go | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 114 insertions(+), 16 deletions(-) diff --git a/t.go b/t.go index 0fa4de9..97631f2 100644 --- a/t.go +++ b/t.go @@ -18,14 +18,20 @@ type TestingT interface { Fatal(args ...interface{}) } -// T is a fake/mock implementation of testing.T. It records all actions -// performed via all methods on the testing.T interface, so they can be -// inspected and asserted. +// T is a fake/mock implementation of *testing.T. All methods available on +// *testing.T are available on *T with the exception of Run(), which has a +// slightly different func type. // -// It is specifically intended for testing test helpers which accept a -// *testing.T or *testing.B, so you can verify that the helpers call Fatal(), -// Error(), etc, as they need. +// All method calls against *T are recorded, so they can be inspected and +// asserted later. To be able to pass in *testing.T or *mocktesting.T, functions +// will need to use an interface instead of *testing.T explicitly. +// +// For basic use cases, the testing.TB interface should suffice. For more +// advanced use cases, create a custom interface that exactly specifies the +// methods of *testing.T which are needed, and then freely pass *testing.T or +// *mocktesting.T. type T struct { + // Settings - These fields control the behavior of T. name string abort bool baseTempdir string @@ -33,6 +39,7 @@ type T struct { deadline time.Time timeout bool + // State - Fields which record how T has been modified via method calls. mux sync.RWMutex skipped bool failed int @@ -179,47 +186,71 @@ func (t *T) internalError(err error) { } } +// Name returns the name given to the *T instance. func (t *T) Name() string { return t.name } +// Name returns the time at which the *T instance is set to timeout. If no +// timeout is set, the bool return value is false, otherwise it is true. func (t *T) Deadline() (time.Time, bool) { return t.deadline, t.timeout } +// Error logs the given args with Log(), and then calls Fail() to mark the *T +// instance as failed. func (t *T) Error(args ...interface{}) { t.Log(args...) t.Fail() } +// Errorf logs the given format and args with Logf(), and then calls Fail() to +// mark the *T instance as failed. func (t *T) Errorf(format string, args ...interface{}) { t.Logf(format, args...) t.Fail() } +// Fail marks the *T instance as having failed. You can check if the *T instance +// has been failed with Failed(), or how many times it has been failed with +// FailedCount(). func (t *T) Fail() { t.failed++ } +// FailNow marks the *T instance as having failed, and also aborts the current +// goroutine with runtime.Goexit(). If the WithNoAbort() option was used when +// initializing the *T instance, runtime.Goexit() will not be called. func (t *T) FailNow() { t.Fail() t.goexit() } +// Failed returns true if the *T instance has been marked as failed. func (t *T) Failed() bool { return t.failed > 0 } +// Fatal logs the given args with Log(), and then calls FailNow() to fail the *T +// instance and abort the current goroutine. +// +// See FailNow() and WithNoAbort() for details about how abort works. func (t *T) Fatal(args ...interface{}) { t.Log(args...) t.FailNow() } +// Fatalf logs the given format and args with Logf(), and then calls FailNow() +// to fail the *T instance and abort the current goroutine. +// +// See FailNow() and WithNoAbort() for details about how abort works. func (t *T) Fatalf(format string, args ...interface{}) { t.Logf(format, args...) t.FailNow() } +// Log renders given args to a string with fmt.Sprintln() and stores the result +// in a string slice which can be accessed with Output(). func (t *T) Log(args ...interface{}) { t.mux.Lock() defer t.mux.Unlock() @@ -227,6 +258,8 @@ func (t *T) Log(args ...interface{}) { t.output = append(t.output, fmt.Sprintln(args...)) } +// Logf renders given format and args to a string with fmt.Sprintf() and stores +// the result in a string slice which can be accessed with Output(). func (t *T) Logf(format string, args ...interface{}) { t.mux.Lock() defer t.mux.Unlock() @@ -237,29 +270,51 @@ func (t *T) Logf(format string, args ...interface{}) { t.output = append(t.output, fmt.Sprintf(format, args...)) } +// Parallel marks the *T instance to indicate Parallel() has been called. +// Use Paralleled() to check if Parallel() has been called. func (t *T) Parallel() { t.parallel = true } +// Skip logs the given args with Log(), and then uses SkipNow() to mark the *T +// instance as skipped and aborts the current goroutine. +// +// See SkipNow() for more details about aborting the current goroutine. func (t *T) Skip(args ...interface{}) { t.Log(args...) t.SkipNow() } +// Skipf logs the given format and args with Logf(), and then uses SkipNow() to +// mark the *T instance as skipped and aborts the current goroutine. +// +// See SkipNow() for more details about aborting the current goroutine. func (t *T) Skipf(format string, args ...interface{}) { t.Logf(format, args...) t.SkipNow() } +// SkipNow marks the *T instance as skipped, and then aborts the current +// goroutine with runtime.Goexit(). If the WithNoAbort() option was used when +// initializing the *T instance, runtime.Goexit() will not be called. func (t *T) SkipNow() { t.skipped = true t.goexit() } +// Skipped returns true if the *T instance has been marked as skipped, otherwise +// it returns false. func (t *T) Skipped() bool { return t.skipped } +// Helper marks the function that is calling Helper() as a helper function. +// Within *T it simply stores a reference to the function. +// +// The list of functions which have called Helper() can be inspected with +// HelperNames(). The names are resolved using runtime.FuncForPC(), meaning they +// include the absolute Go package path to the function, along with the function +// name itself. func (t *T) Helper() { pc, _, _, ok := runtime.Caller(1) if !ok { @@ -274,6 +329,9 @@ func (t *T) Helper() { t.helpers = append(t.helpers, fnName) } +// Cleanup registers a cleanup function. *T does not run cleanup functions, it +// simply records them for the purpose of later inspection via CleanupFuncs() or +// CleanupNames(). func (t *T) Cleanup(f func()) { t.mux.Lock() defer t.mux.Unlock() @@ -281,8 +339,23 @@ func (t *T) Cleanup(f func()) { t.cleanups = append(t.cleanups, f) } +// TempDir creates an actual temporary directory on the system using +// ioutil.TempDir(). This actually does perform a action, rather than just +// recording the fact the method was called list most other *T methods. +// +// This is because returning a string that is not the path to a real directory, +// would most likely be useless. Hence it does create a real temporary +// directory. +// +// It is important to note that the temporary directory is not cleaned up by +// mocktesting. But it is created via ioutil.TempDir(), so the operating system +// should eventually clean it up. +// +// A string slice of temporary directory paths created by calls to TempDir() can +// be accessed with TempDirs(). func (t *T) TempDir() string { - // Allow setting MkdirTemp function for testing purposes. + // Allow setting MkdirTemp function for the purpose of testing mocktesting + // itself.. f := t.mkdirTempFunc if f == nil { f = ioutil.TempDir @@ -301,6 +374,22 @@ func (t *T) TempDir() string { return dir } +// Run allows running sub-tests just very much like *testing.T. The one +// difference is that the function argument accepts a testing.TB instead of +// *testing.T type. This is to allow passing a *mocktesting.T to the sub-test +// function instead of a *testing.T. +// +// Sub-test functions are executed in a separate blocking goroutine, so calls to +// SkipNow() and FailNow() abort the new goroutine that the sub-test is running +// in, rather than the gorouting which is executing Run(). +// +// The sub-test function will receive a new instance of *T which is a sub-test, +// which name and other attributes set accordingly. +// +// If any sub-test *T is marked as failed, the parent *T instance will also +// be marked as failed. +// +// The list of sub-test *T instances can be accessed with Subtests(). func (t *T) Run(name string, f func(testing.TB)) bool { name = t.newSubTestName(name) fullname := name @@ -357,8 +446,8 @@ func (t *T) newSubTestName(name string) string { // Inspection Methods which are not part of the testing.TB interface. // -// Output returns a string slice of all output produced by calls to Log(), -// Logf(), Error(), Errorf(), Fatal(), Fatalf(), Skip(), and Skipf(). +// Output returns a string slice of all output produced by calls to Log() and +// Logf(). func (t *T) Output() []string { t.mux.RLock() defer t.mux.RUnlock() @@ -374,7 +463,10 @@ func (t *T) CleanupFuncs() []func() { return t.cleanups } -// CleanupNames returns a string slice of function names given to Cleanup(). +// CleanupNames returns a string slice of function names given to Cleanup(). The +// names are resolved using runtime.FuncForPC(), meaning they include the +// absolute Go package path to the function, along with the function name +// itself. func (t *T) CleanupNames() []string { r := make([]string, 0, len(t.cleanups)) for _, f := range t.cleanups { @@ -385,19 +477,25 @@ func (t *T) CleanupNames() []string { return r } -// FailedCount returns the number of times Error(), Errorf(), Fail(), Failf(), -// FailNow(), Fatal(), and Fatalf() were called. +// FailedCount returns the number of times the *T instance has been marked as +// failed. func (t *T) FailedCount() int { return t.failed } -// Aborted returns true if the TB instance aborted the current goroutine via +// Aborted returns true if the *T instance aborted the current goroutine via // runtime.Goexit(), which is called by FailNow() and SkipNow(). +// +// This returns true even if *T was initialized using the WithNoAbort() option. +// Because the test was still instructed to abort, which is a separate matter +// than that *T was specifically set to not abort the current goroutine. func (t *T) Aborted() bool { return t.aborted } -// HelperNames returns a list of function names which called Helper(). +// HelperNames returns a list of function names which called Helper(). The names +// are resolved using runtime.FuncForPC(), meaning they include the absolute Go +// package path to the function, along with the function name itself. func (t *T) HelperNames() []string { t.mux.RLock() defer t.mux.RUnlock() @@ -410,8 +508,8 @@ func (t *T) Paralleled() bool { return t.parallel } -// Subtests returns a list map of *TB instances for any subtests executed via -// Run(). +// Subtests returns a slice of *T instances created for any subtests executed +// via Run(). func (t *T) Subtests() []*T { if t.subtests == nil { t.mux.Lock()