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.go b/t.go index 640d269..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() @@ -424,3 +522,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_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 +} diff --git a/t_test.go b/t_test.go index 56f5519..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() @@ -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) + }) + } +}