From 108ae0ec9a7d26e214bb81057d3fac30b5b24325 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Mon, 22 Nov 2021 02:02:42 +0000 Subject: [PATCH 1/4] fix(mocktesting): add missing TempDirs() function Without this function there was no way to inspect the list of temporary directories created by calls to TempDir(), also making it impossible to verify that TempDir() has been called. --- t.go | 15 +++++++++++++++ t_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/t.go b/t.go index 640d269..0fa4de9 100644 --- a/t.go +++ b/t.go @@ -424,3 +424,18 @@ func (t *T) Subtests() []*T { return t.subtests } + +// TempDirs returns a string slice of temporary directories created by +// TempDir(). +func (t *T) TempDirs() []string { + if t.tempdirs == nil { + t.mux.Lock() + t.tempdirs = []string{} + t.mux.Unlock() + } + + t.mux.RLock() + defer t.mux.RUnlock() + + return t.tempdirs +} diff --git a/t_test.go b/t_test.go index 56f5519..118b801 100644 --- a/t_test.go +++ b/t_test.go @@ -2458,3 +2458,46 @@ func TestT_Subtests(t *testing.T) { }) } } + +func TestT_TempDirs(t *testing.T) { + type fields struct { + tempdirs []string + } + tests := []struct { + name string + fields fields + want []string + }{ + { + name: "nil", + fields: fields{tempdirs: nil}, + want: []string{}, + }, + { + name: "empty", + fields: fields{tempdirs: []string{}}, + want: []string{}, + }, + { + name: "one dir", + fields: fields{tempdirs: []string{"/tmp/foo"}}, + want: []string{"/tmp/foo"}, + }, + { + name: "many dirs", + fields: fields{ + tempdirs: []string{"/tmp/foo", "/tmp/foo", "/tmp/nope"}, + }, + want: []string{"/tmp/foo", "/tmp/foo", "/tmp/nope"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mt := &T{tempdirs: tt.fields.tempdirs} + + got := mt.TempDirs() + + assert.Equal(t, tt.want, got) + }) + } +} From a892f8eea7425e8eb2a98f19057ced5c55cedb39 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Mon, 22 Nov 2021 02:03:49 +0000 Subject: [PATCH 2/4] chore(test): fix typo in test function name and var/field names The Aborted() method was originally called Halted(), the rename didn't make it to the test function it seems. --- t_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/t_test.go b/t_test.go index 118b801..54d9bd2 100644 --- a/t_test.go +++ b/t_test.go @@ -2371,9 +2371,9 @@ func TestT_HelperNames(t *testing.T) { } } -func TestT_Halted(t *testing.T) { +func TestT_Aborted(t *testing.T) { type fields struct { - halted bool + aborted bool } tests := []struct { name string @@ -2381,19 +2381,19 @@ func TestT_Halted(t *testing.T) { want bool }{ { - name: "not halted", - fields: fields{halted: false}, + name: "not aborted", + fields: fields{aborted: false}, want: false, }, { - name: "halted", - fields: fields{halted: true}, + name: "aborted", + fields: fields{aborted: true}, want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mt := &T{aborted: tt.fields.halted} + mt := &T{aborted: tt.fields.aborted} got := mt.Aborted() From b5bae7b65d6af6ff80dfd1a5d972084cba4e10a9 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Mon, 22 Nov 2021 03:07:18 +0000 Subject: [PATCH 3/4] 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() From 822d6e10aeaf2c324dcf0e2d10778131c4fccad6 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Mon, 22 Nov 2021 03:07:59 +0000 Subject: [PATCH 4/4] docs(examples): update and flesh out example functions --- README.md | 24 ++- example_test.go | 189 ----------------- t_example_test.go | 520 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 534 insertions(+), 199 deletions(-) delete mode 100644 example_test.go create mode 100644 t_example_test.go diff --git a/README.md b/README.md index e554e23..60851c7 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ import "github.com/jimeh/go-mocktesting" ## Usage ```go -func Example_basic() { +func ExampleT_Error() { assertTrue := func(t testing.TB, v bool) { if v != true { t.Error("expected false to be true") @@ -58,7 +58,7 @@ func Example_basic() { ``` ```go -func Example_fatal() { +func ExampleT_Fatal() { requireTrue := func(t testing.TB, v bool) { if v != true { t.Fatal("expected false to be true") @@ -66,20 +66,20 @@ func Example_fatal() { } mt := mocktesting.NewT("TestMyBoolean1") - requireTrue(mt, true) fmt.Printf("Name: %s\n", mt.Name()) + requireTrue(mt, true) fmt.Printf("Failed: %+v\n", mt.Failed()) fmt.Printf("Aborted: %+v\n", mt.Aborted()) mt = mocktesting.NewT("TestMyBoolean2") + fmt.Printf("Name: %s\n", mt.Name()) mocktesting.Go(func() { requireTrue(mt, false) fmt.Println("This is never executed.") }) - fmt.Printf("Name: %s\n", mt.Name()) fmt.Printf("Failed: %+v\n", mt.Failed()) fmt.Printf("Aborted: %+v\n", mt.Aborted()) - fmt.Printf("Output: %s\n", strings.Join(mt.Output(), "")) + fmt.Printf("Output:\n - %s\n", strings.Join(mt.Output(), "\n - ")) // Output: // Name: TestMyBoolean1 @@ -88,12 +88,13 @@ func Example_fatal() { // Name: TestMyBoolean2 // Failed: true // Aborted: true - // Output: expected false to be true + // Output: + // - expected false to be true } ``` ```go -func Example_subtests() { +func ExampleT_Run() { requireTrue := func(t testing.TB, v bool) { if v != true { t.Fatal("expected false to be true") @@ -101,10 +102,10 @@ func Example_subtests() { } mt := mocktesting.NewT("TestMyBoolean") + fmt.Printf("Name: %s\n", mt.Name()) mt.Run("true", func(t testing.TB) { requireTrue(t, true) }) - fmt.Printf("Name: %s\n", mt.Name()) fmt.Printf("Failed: %+v\n", mt.Failed()) fmt.Printf("Sub1-Name: %s\n", mt.Subtests()[0].Name()) fmt.Printf("Sub1-Failed: %+v\n", mt.Subtests()[0].Failed()) @@ -118,7 +119,9 @@ func Example_subtests() { fmt.Printf("Sub2-Name: %s\n", mt.Subtests()[1].Name()) fmt.Printf("Sub2-Failed: %+v\n", mt.Subtests()[1].Failed()) fmt.Printf("Sub2-Aborted: %+v\n", mt.Subtests()[1].Aborted()) - fmt.Printf("Sub2-Output: %s\n", strings.Join(mt.Subtests()[1].Output(), "")) + fmt.Printf("Sub2-Output:\n - %s\n", + strings.Join(mt.Subtests()[1].Output(), "\n - "), + ) // Output: // Name: TestMyBoolean @@ -130,7 +133,8 @@ func Example_subtests() { // Sub2-Name: TestMyBoolean/false // Sub2-Failed: true // Sub2-Aborted: true - // Sub2-Output: expected false to be true + // Sub2-Output: + // - expected false to be true } ``` diff --git a/example_test.go b/example_test.go deleted file mode 100644 index 58942e1..0000000 --- a/example_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package mocktesting_test - -import ( - "fmt" - "strings" - "testing" - - "github.com/jimeh/go-mocktesting" -) - -func Example_basic() { - assertTrue := func(t testing.TB, v bool) { - if v != true { - t.Error("expected false to be true") - } - } - - mt := mocktesting.NewT("TestMyBoolean1") - assertTrue(mt, true) - fmt.Printf("Name: %s\n", mt.Name()) - fmt.Printf("Failed: %+v\n", mt.Failed()) - fmt.Printf("Aborted: %+v\n", mt.Aborted()) - - mt = mocktesting.NewT("TestMyBoolean2") - assertTrue(mt, false) - fmt.Printf("Name: %s\n", mt.Name()) - fmt.Printf("Failed: %+v\n", mt.Failed()) - fmt.Printf("Aborted: %+v\n", mt.Aborted()) - fmt.Printf("Output: %s\n", strings.Join(mt.Output(), "")) - - // Output: - // Name: TestMyBoolean1 - // Failed: false - // Aborted: false - // Name: TestMyBoolean2 - // Failed: true - // Aborted: false - // Output: expected false to be true -} - -func Example_fatal() { - requireTrue := func(t testing.TB, v bool) { - if v != true { - t.Fatal("expected false to be true") - } - } - - mt := mocktesting.NewT("TestMyBoolean1") - requireTrue(mt, true) - fmt.Printf("Name: %s\n", mt.Name()) - fmt.Printf("Failed: %+v\n", mt.Failed()) - fmt.Printf("Aborted: %+v\n", mt.Aborted()) - - mt = mocktesting.NewT("TestMyBoolean2") - mocktesting.Go(func() { - requireTrue(mt, false) - fmt.Println("This is never executed.") - }) - fmt.Printf("Name: %s\n", mt.Name()) - fmt.Printf("Failed: %+v\n", mt.Failed()) - fmt.Printf("Aborted: %+v\n", mt.Aborted()) - fmt.Printf("Output: %s\n", strings.Join(mt.Output(), "")) - - // Output: - // Name: TestMyBoolean1 - // Failed: false - // Aborted: false - // Name: TestMyBoolean2 - // Failed: true - // Aborted: true - // Output: expected false to be true -} - -func Example_subtests() { - requireTrue := func(t testing.TB, v bool) { - if v != true { - t.Fatal("expected false to be true") - } - } - - mt := mocktesting.NewT("TestMyBoolean") - mt.Run("true", func(t testing.TB) { - requireTrue(t, true) - }) - fmt.Printf("Name: %s\n", mt.Name()) - fmt.Printf("Failed: %+v\n", mt.Failed()) - fmt.Printf("Sub1-Name: %s\n", mt.Subtests()[0].Name()) - fmt.Printf("Sub1-Failed: %+v\n", mt.Subtests()[0].Failed()) - fmt.Printf("Sub1-Aborted: %+v\n", mt.Subtests()[0].Aborted()) - - mt.Run("false", func(t testing.TB) { - requireTrue(t, false) - fmt.Println("This is never executed.") - }) - fmt.Printf("Failed: %+v\n", mt.Failed()) - fmt.Printf("Sub2-Name: %s\n", mt.Subtests()[1].Name()) - fmt.Printf("Sub2-Failed: %+v\n", mt.Subtests()[1].Failed()) - fmt.Printf("Sub2-Aborted: %+v\n", mt.Subtests()[1].Aborted()) - fmt.Printf("Sub2-Output: %s\n", strings.Join(mt.Subtests()[1].Output(), "")) - - // Output: - // Name: TestMyBoolean - // Failed: false - // Sub1-Name: TestMyBoolean/true - // Sub1-Failed: false - // Sub1-Aborted: false - // Failed: true - // Sub2-Name: TestMyBoolean/false - // Sub2-Failed: true - // Sub2-Aborted: true - // Sub2-Output: expected false to be true -} - -func Example_subtests_in_subtests() { - assertGreaterThan := func(t testing.TB, got int, min int) { - if got <= min { - t.Errorf("expected %d to be greater than %d", got, min) - } - } - - mt := mocktesting.NewT("TestMyBoolean") - mt.Run("positive", func(t testing.TB) { - subMT, _ := t.(*mocktesting.T) - - subMT.Run("greater than", func(t testing.TB) { - assertGreaterThan(t, 5, 4) - }) - subMT.Run("equal", func(t testing.TB) { - assertGreaterThan(t, 5, 5) - }) - subMT.Run("less than", func(t testing.TB) { - assertGreaterThan(t, 4, 5) - }) - }) - fmt.Printf("Name: %s\n", mt.Name()) - fmt.Printf("Failed: %+v\n", mt.Failed()) - fmt.Printf("Sub1-Name: %s\n", mt.Subtests()[0].Name()) - fmt.Printf("Sub1-Failed: %+v\n", mt.Subtests()[0].Failed()) - fmt.Printf("Sub1-Aborted: %+v\n", mt.Subtests()[0].Aborted()) - fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[0].Name()) - fmt.Printf( - "Sub1-Sub1-Failed: %+v\n", mt.Subtests()[0].Subtests()[0].Failed(), - ) - fmt.Printf( - "Sub1-Sub1-Aborted: %+v\n", mt.Subtests()[0].Subtests()[0].Aborted(), - ) - fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[1].Name()) - fmt.Printf( - "Sub1-Sub2-Failed: %+v\n", mt.Subtests()[0].Subtests()[1].Failed(), - ) - fmt.Printf( - "Sub1-Sub2-Aborted: %+v\n", mt.Subtests()[0].Subtests()[1].Aborted(), - ) - fmt.Printf( - "Sub1-Sub3-Output: %s\n", strings.TrimSpace( - strings.Join(mt.Subtests()[0].Subtests()[1].Output(), ""), - ), - ) - fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[2].Name()) - fmt.Printf( - "Sub1-Sub3-Failed: %+v\n", mt.Subtests()[0].Subtests()[2].Failed(), - ) - fmt.Printf( - "Sub1-Sub3-Aborted: %+v\n", mt.Subtests()[0].Subtests()[2].Aborted(), - ) - fmt.Printf( - "Sub1-Sub3-Output: %s\n", strings.TrimSpace( - strings.Join(mt.Subtests()[0].Subtests()[2].Output(), ""), - ), - ) - - // Output: - // Name: TestMyBoolean - // Failed: true - // Sub1-Name: TestMyBoolean/positive - // Sub1-Failed: true - // Sub1-Aborted: false - // Sub1-Sub1-Name: TestMyBoolean/positive/greater_than - // Sub1-Sub1-Failed: false - // Sub1-Sub1-Aborted: false - // Sub1-Sub1-Name: TestMyBoolean/positive/equal - // Sub1-Sub2-Failed: true - // Sub1-Sub2-Aborted: false - // Sub1-Sub3-Output: expected 5 to be greater than 5 - // Sub1-Sub1-Name: TestMyBoolean/positive/less_than - // Sub1-Sub3-Failed: true - // Sub1-Sub3-Aborted: false - // Sub1-Sub3-Output: expected 4 to be greater than 5 -} diff --git a/t_example_test.go b/t_example_test.go new file mode 100644 index 0000000..169d646 --- /dev/null +++ b/t_example_test.go @@ -0,0 +1,520 @@ +package mocktesting_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/jimeh/go-mocktesting" +) + +func ExampleT_Error() { + assertTrue := func(t testing.TB, v bool) { + if v != true { + t.Error("expected false to be true") + } + } + + mt := mocktesting.NewT("TestMyBoolean1") + assertTrue(mt, true) + fmt.Printf("Name: %s\n", mt.Name()) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Aborted: %+v\n", mt.Aborted()) + + mt = mocktesting.NewT("TestMyBoolean2") + assertTrue(mt, false) + fmt.Printf("Name: %s\n", mt.Name()) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Aborted: %+v\n", mt.Aborted()) + fmt.Printf("Output: %s\n", strings.Join(mt.Output(), "")) + + // Output: + // Name: TestMyBoolean1 + // Failed: false + // Aborted: false + // Name: TestMyBoolean2 + // Failed: true + // Aborted: false + // Output: expected false to be true +} + +func ExampleT_Errorf() { + assertGreaterThan := func(t testing.TB, got int, min int) { + if got <= min { + t.Errorf("expected %d to be greater than %d", got, min) + } + } + + mt := mocktesting.NewT("TestMyBoolean1") + assertGreaterThan(mt, 6, 5) + fmt.Printf("Name: %s\n", mt.Name()) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Aborted: %+v\n", mt.Aborted()) + + mt = mocktesting.NewT("TestMyBoolean2") + assertGreaterThan(mt, 4, 5) + fmt.Printf("Name: %s\n", mt.Name()) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Aborted: %+v\n", mt.Aborted()) + fmt.Printf("Output:\n - %s\n", strings.Join(mt.Output(), "\n - ")) + + // Output: + // Name: TestMyBoolean1 + // Failed: false + // Aborted: false + // Name: TestMyBoolean2 + // Failed: true + // Aborted: false + // Output: + // - expected 4 to be greater than 5 +} + +func ExampleT_Fail() { + assertTrue := func(t testing.TB, v bool) { + if v != true { + t.Fail() + } + } + + mt := mocktesting.NewT("TestMyBoolean1") + fmt.Printf("Name: %s\n", mt.Name()) + assertTrue(mt, false) + fmt.Printf("Failed: %+v\n", mt.Failed()) + + // Output: + // Name: TestMyBoolean1 + // Failed: true +} + +func ExampleT_FailNow() { + requireTrue := func(t testing.TB, v bool) { + if v != true { + t.FailNow() + } + } + + mt := mocktesting.NewT("TestMyBoolean1") + fmt.Printf("Name: %s\n", mt.Name()) + halted := true + mocktesting.Go(func() { + requireTrue(mt, false) + halted = false + }) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Halted: %+v\n", halted) + + // Output: + // Name: TestMyBoolean1 + // Failed: true + // Halted: true +} + +func ExampleT_Fatal() { + requireTrue := func(t testing.TB, v bool) { + if v != true { + t.Fatal("expected false to be true") + } + } + + mt := mocktesting.NewT("TestMyBoolean1") + fmt.Printf("Name: %s\n", mt.Name()) + requireTrue(mt, true) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Aborted: %+v\n", mt.Aborted()) + + mt = mocktesting.NewT("TestMyBoolean2") + fmt.Printf("Name: %s\n", mt.Name()) + mocktesting.Go(func() { + requireTrue(mt, false) + fmt.Println("This is never executed.") + }) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Aborted: %+v\n", mt.Aborted()) + fmt.Printf("Output:\n - %s\n", strings.Join(mt.Output(), "\n - ")) + + // Output: + // Name: TestMyBoolean1 + // Failed: false + // Aborted: false + // Name: TestMyBoolean2 + // Failed: true + // Aborted: true + // Output: + // - expected false to be true +} + +func ExampleT_Fatalf() { + requireGreaterThan := func(t testing.TB, got int, min int) { + if got <= min { + t.Fatalf("expected %d to be greater than %d", got, min) + } + } + + mt := mocktesting.NewT("TestMyGT1") + fmt.Printf("Name: %s\n", mt.Name()) + halted := true + mocktesting.Go(func() { + requireGreaterThan(mt, 6, 5) + halted = false + }) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Halted: %+v\n", halted) + + mt = mocktesting.NewT("TestMyGT2") + fmt.Printf("Name: %s\n", mt.Name()) + halted = true + mocktesting.Go(func() { + requireGreaterThan(mt, 4, 5) + halted = false + }) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Halted: %+v\n", halted) + fmt.Printf("Output:\n - %s\n", strings.Join(mt.Output(), "\n - ")) + + // Output: + // Name: TestMyGT1 + // Failed: false + // Halted: false + // Name: TestMyGT2 + // Failed: true + // Halted: true + // Output: + // - expected 4 to be greater than 5 +} + +func ExampleT_Log() { + logHello := func(t testing.TB) { + t.Log("hello world") + } + + mt := mocktesting.NewT("TestMyLog") + fmt.Printf("Name: %s\n", mt.Name()) + logHello(mt) + fmt.Printf("Output:\n - %s\n", strings.Join(mt.Output(), "\n - ")) + + // Output: + // Name: TestMyLog + // Output: + // - hello world +} + +func ExampleT_Logf() { + logHello := func(t testing.TB, name string) { + t.Logf("hello, %s", name) + } + + mt := mocktesting.NewT("TestMyLogf") + fmt.Printf("Name: %s\n", mt.Name()) + logHello(mt, "Abel") + fmt.Printf("Output:\n - %s\n", strings.Join(mt.Output(), "\n - ")) + + // Output: + // Name: TestMyLogf + // Output: + // - hello, Abel +} + +func ExampleT_Parallel() { + logHello := func(t testing.TB, name string) { + mt, _ := t.(*mocktesting.T) + mt.Parallel() + mt.Logf("hello, %s", name) + } + + mt := mocktesting.NewT("TestMyLogf") + fmt.Printf("Name: %s\n", mt.Name()) + fmt.Printf("Parallel (before): %+v\n", mt.Paralleled()) + logHello(mt, "Abel") + fmt.Printf("Parallel (after): %+v\n", mt.Paralleled()) + + // Output: + // Name: TestMyLogf + // Parallel (before): false + // Parallel (after): true +} + +func ExampleT_Skip() { + logHello := func(t testing.TB, name string) { + if name == "" { + t.Skip("no name given to say hello to") + } + t.Logf("hello, %s", name) + } + + mt := mocktesting.NewT("TestMyLog1") + fmt.Printf("Name: %s\n", mt.Name()) + halted := true + mocktesting.Go(func() { + logHello(mt, "Abel") + halted = false + }) + fmt.Printf("Skipped: %+v\n", mt.Skipped()) + fmt.Printf("Halted: %+v\n", halted) + + mt = mocktesting.NewT("TestMyLog2") + fmt.Printf("Name: %s\n", mt.Name()) + halted = true + mocktesting.Go(func() { + logHello(mt, "") + halted = false + }) + fmt.Printf("Skipped: %+v\n", mt.Skipped()) + fmt.Printf("Halted: %+v\n", halted) + fmt.Printf("Output:\n - %s\n", strings.Join(mt.Output(), "\n - ")) + + // Output: + // Name: TestMyLog1 + // Skipped: false + // Halted: false + // Name: TestMyLog2 + // Skipped: true + // Halted: true + // Output: + // - no name given to say hello to +} + +func ExampleT_Skipf() { + logHello := func(t testing.TB, name string) { + if name != "Jane" { + t.Skipf("I only say hello to Jane, you are %s", name) + } + } + + mt := mocktesting.NewT("TestMyLog1") + fmt.Printf("Name: %s\n", mt.Name()) + halted := true + mocktesting.Go(func() { + logHello(mt, "Jane") + halted = false + }) + fmt.Printf("Skipped: %+v\n", mt.Skipped()) + fmt.Printf("Halted: %+v\n", halted) + + mt = mocktesting.NewT("TestMyLog2") + fmt.Printf("Name: %s\n", mt.Name()) + halted = true + mocktesting.Go(func() { + logHello(mt, "John") + halted = false + }) + fmt.Printf("Skipped: %+v\n", mt.Skipped()) + fmt.Printf("Halted: %+v\n", halted) + fmt.Printf("Output:\n - %s\n", strings.Join(mt.Output(), "\n - ")) + + // Output: + // Name: TestMyLog1 + // Skipped: false + // Halted: false + // Name: TestMyLog2 + // Skipped: true + // Halted: true + // Output: + // - I only say hello to Jane, you are John +} + +func ExampleT_SkipNow() { + logHello := func(t testing.TB, name string) { + if name == "" { + t.SkipNow() + } + } + + mt := mocktesting.NewT("TestMyLog1") + fmt.Printf("Name: %s\n", mt.Name()) + halted := true + mocktesting.Go(func() { + logHello(mt, "Abel") + halted = false + }) + fmt.Printf("Skipped: %+v\n", mt.Skipped()) + fmt.Printf("Halted: %+v\n", halted) + + mt = mocktesting.NewT("TestMyLog2") + fmt.Printf("Name: %s\n", mt.Name()) + halted = true + mocktesting.Go(func() { + logHello(mt, "") + halted = false + }) + fmt.Printf("Skipped: %+v\n", mt.Skipped()) + fmt.Printf("Halted: %+v\n", halted) + + // Output: + // Name: TestMyLog1 + // Skipped: false + // Halted: false + // Name: TestMyLog2 + // Skipped: true + // Halted: true +} + +func ExampleT_Helper() { + assertTrue := func(t testing.TB, v bool) { + t.Helper() + + if v != true { + t.Error("expected false to be true") + } + } + + mt := mocktesting.NewT("TestMyBoolean1") + assertTrue(mt, true) + assertTrue(mt, true) + fmt.Printf("Name: %s\n", mt.Name()) + fmt.Printf("Helpers:\n - %s\n", strings.Join(mt.HelperNames(), "\n - ")) + + // Output: + // Name: TestMyBoolean1 + // Helpers: + // - github.com/jimeh/go-mocktesting_test.ExampleT_Helper.func1 + // - github.com/jimeh/go-mocktesting_test.ExampleT_Helper.func1 +} + +func ExampleT_Cleanup() { + cleanup1 := func() { + fmt.Println("running cleanup1") + } + cleanup2 := func() { + fmt.Println("running cleanup2") + } + + mt := mocktesting.NewT("TestMyCleanup") + mt.Cleanup(cleanup1) + mt.Cleanup(cleanup2) + fmt.Printf("Name: %s\n", mt.Name()) + fmt.Printf( + "CleanupNames:\n - %s\n", strings.Join(mt.CleanupNames(), "\n - "), + ) + mt.CleanupFuncs()[1]() + mt.CleanupFuncs()[0]() + + // Output: + // Name: TestMyCleanup + // CleanupNames: + // - github.com/jimeh/go-mocktesting_test.ExampleT_Cleanup.func1 + // - github.com/jimeh/go-mocktesting_test.ExampleT_Cleanup.func2 + // running cleanup2 + // running cleanup1 +} + +func ExampleT_Run() { + requireTrue := func(t testing.TB, v bool) { + if v != true { + t.Fatal("expected false to be true") + } + } + + mt := mocktesting.NewT("TestMyBoolean") + fmt.Printf("Name: %s\n", mt.Name()) + mt.Run("true", func(t testing.TB) { + requireTrue(t, true) + }) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Sub1-Name: %s\n", mt.Subtests()[0].Name()) + fmt.Printf("Sub1-Failed: %+v\n", mt.Subtests()[0].Failed()) + fmt.Printf("Sub1-Aborted: %+v\n", mt.Subtests()[0].Aborted()) + + mt.Run("false", func(t testing.TB) { + requireTrue(t, false) + fmt.Println("This is never executed.") + }) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Sub2-Name: %s\n", mt.Subtests()[1].Name()) + fmt.Printf("Sub2-Failed: %+v\n", mt.Subtests()[1].Failed()) + fmt.Printf("Sub2-Aborted: %+v\n", mt.Subtests()[1].Aborted()) + fmt.Printf("Sub2-Output:\n - %s\n", + strings.Join(mt.Subtests()[1].Output(), "\n - "), + ) + + // Output: + // Name: TestMyBoolean + // Failed: false + // Sub1-Name: TestMyBoolean/true + // Sub1-Failed: false + // Sub1-Aborted: false + // Failed: true + // Sub2-Name: TestMyBoolean/false + // Sub2-Failed: true + // Sub2-Aborted: true + // Sub2-Output: + // - expected false to be true +} + +func ExampleT_Run_nested() { + assertGreaterThan := func(t testing.TB, got int, min int) { + if got <= min { + t.Errorf("expected %d to be greater than %d", got, min) + } + } + + mt := mocktesting.NewT("TestMyBoolean") + mt.Run("positive", func(t testing.TB) { + subMT, _ := t.(*mocktesting.T) + + subMT.Run("greater than", func(t testing.TB) { + assertGreaterThan(t, 5, 4) + }) + subMT.Run("equal", func(t testing.TB) { + assertGreaterThan(t, 5, 5) + }) + subMT.Run("less than", func(t testing.TB) { + assertGreaterThan(t, 4, 5) + }) + }) + fmt.Printf("Name: %s\n", mt.Name()) + fmt.Printf("Failed: %+v\n", mt.Failed()) + fmt.Printf("Sub1-Name: %s\n", mt.Subtests()[0].Name()) + fmt.Printf("Sub1-Failed: %+v\n", mt.Subtests()[0].Failed()) + fmt.Printf("Sub1-Aborted: %+v\n", mt.Subtests()[0].Aborted()) + fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[0].Name()) + fmt.Printf( + "Sub1-Sub1-Failed: %+v\n", mt.Subtests()[0].Subtests()[0].Failed(), + ) + fmt.Printf( + "Sub1-Sub1-Aborted: %+v\n", mt.Subtests()[0].Subtests()[0].Aborted(), + ) + fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[1].Name()) + fmt.Printf( + "Sub1-Sub2-Failed: %+v\n", mt.Subtests()[0].Subtests()[1].Failed(), + ) + fmt.Printf( + "Sub1-Sub2-Aborted: %+v\n", mt.Subtests()[0].Subtests()[1].Aborted(), + ) + fmt.Printf( + "Sub1-Sub3-Output:\n - %s\n", strings.TrimSpace( + strings.Join(mt.Subtests()[0].Subtests()[1].Output(), "\n - "), + ), + ) + fmt.Printf("Sub1-Sub1-Name: %s\n", mt.Subtests()[0].Subtests()[2].Name()) + fmt.Printf( + "Sub1-Sub3-Failed: %+v\n", mt.Subtests()[0].Subtests()[2].Failed(), + ) + fmt.Printf( + "Sub1-Sub3-Aborted: %+v\n", mt.Subtests()[0].Subtests()[2].Aborted(), + ) + fmt.Printf( + "Sub1-Sub3-Output:\n - %s\n", strings.TrimSpace( + strings.Join(mt.Subtests()[0].Subtests()[2].Output(), "\n - "), + ), + ) + + // Output: + // Name: TestMyBoolean + // Failed: true + // Sub1-Name: TestMyBoolean/positive + // Sub1-Failed: true + // Sub1-Aborted: false + // Sub1-Sub1-Name: TestMyBoolean/positive/greater_than + // Sub1-Sub1-Failed: false + // Sub1-Sub1-Aborted: false + // Sub1-Sub1-Name: TestMyBoolean/positive/equal + // Sub1-Sub2-Failed: true + // Sub1-Sub2-Aborted: false + // Sub1-Sub3-Output: + // - expected 5 to be greater than 5 + // Sub1-Sub1-Name: TestMyBoolean/positive/less_than + // Sub1-Sub3-Failed: true + // Sub1-Sub3-Aborted: false + // Sub1-Sub3-Output: + // - expected 4 to be greater than 5 +}