test

package module
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2025 License: MIT Imports: 33 Imported by: 0

README ΒΆ

logger
build-status go report MIT License coverage docs

blugnu/test

Provides a concise, fluent, type-safe API over the standard testing framework, simplifying common tests in an extensible fashion whilst maintaining compatibility with the standard testing package.

Friends don't let friends write tests that are hard to read, hard to maintain, or that don't fail when they should.

Don't do this:

func TestDoSomething(t *testing.T) {
  // act
  err := DoSomething()

  // assert
  result, err := DoSomething()
  if err != nil {
    t.Errorf("unexpected error: %v", err)
  }

  expected := 42
  if result != expected {
    t.Errorf("expected result %v, got %v", expected, result)
  }
}

Do this instead:

func TestDoSomething(t *testing.T) {
  With(t)

  // act
  result, err := DoSomething()

  // assert
  Expect(err).IsNil()
  Expect(result, "result").To(Equal(42))
}

Features

  • Clean: No more constantly referencing a *testing.T;
  • Concise: Provides a fluent API that is concise and easy to read, reducing boilerplate code;
  • Type-Safe: Uses Go's type system to ensure that valid tests are performed, reducing runtime errors and false positives (or negatives);
  • Compatible: Compatible with the standard library testing package;
  • Matchers: Provides a rich set of matchers for common assertions, making it easy to express expectations;
  • Runners: Provides a rich set of runners for common testing needs, including table-driven tests, testing helpers, flaky tests, and more;
  • Extensible: Supports custom matchers and runners, enabling functionality to be extended as needed;
  • Panic Testing: Provides a way to test for expected panics, ensuring that code behaves correctly under error conditions;
  • Mocking Utilities: Provides types to assist with implementing mock functions and replacing dependencies in tests, allowing for isolated testing of components;
  • Console Recording: Supports recording console output (stdout and stderr), to facilitate testing of log messages and other output;
  • Meta-Testing: Provides a test for testing a test (used by the package to test itself).

πŸ‘· Β  Under Construction

This package is not yet considered stable.

Feel free to use this package if you find it useful, but be aware that the API may change without notice.

Having said that, the API has just been through a major overhaul to address all of the shortcomings and annoyances that existed in the previous version, providing a stronger foundation for future development.

The API will remain as stable as possible, but until the package hits v1.0 this should still be considered an aspiration, not a commitment.

⚠   Goroutine IDs

This module uses runtime.Stack() to determine the ID of the current goroutine. This is required to maintain a reliable per-goroutine stack of *testing.T values (a.k.a 'test frames').

This mechanism is not guaranteed to be stable and may change in future versions of Go.

If you are using this module in a production environment, be aware that changes in future versions of Go may require break this mechanism for determining a goroutine id, requiring changes. This may hamper the ability of dependent code to update to a later go version until those changes have been made.

πŸƒ Β  Quick Start

Installation

go get github.com/blugnu/test

Read This First

  1. dot-importing the blugnu/test package
  2. Test Frames
Writing a Test: With(t)

The With() function is used to set up a test frame for the current test. This function is typically called at the start of a test function, passing the *testing.T value from the test function as an argument:

  func TestDoSomething(t *testing.T) {
     With(t) // establishes the initial test frame
     // ...
  }

πŸ’‘ Calling Parallel(t) is equivalent to calling With(t) followed by Parallel() or t.Parallel().

There is no cleanup required after calling With(t); the test frame is automatically cleaned up when the test completes.

If you use the blugnu/test package functions for running table-driven tests or explicit subtests the test frame stack is managed for you:

  func TestDoSomething(t *testing.T) {
     With(t) // establishes the initial test frame

     Run(Test("subtest", func() {
        // no need to call With(t) here; it is managed automatically
        // ...
     }))
  }

If a new test frame is created outside of the test package, then the With(t) function must be called again to push that test frame onto the stack. For example, if you choose to create a subtest using *testing.T.Run() and want to use the blugnu/test functions in that subtest:

  func TestDoSomething(t *testing.T) {
     With(t) // establishes the initial test frame

     // using the testing.T.Run method...
     t.Run("subtest", func(t *testing.T) {
        With(t) // .. the new test frame must be established explicitly
        // ...
     })
  }

Generally speaking it is much easier to use the blugnu/test package functions to avoid having to use With(t) (or Parallel(t)) for anything other than establishing the initial test frame for each test function.

⚠ Neither With(t) nor Parallel(t) should be called unless required to establish a test frame for a new *testing.T value.

Writing a Test: Expect

Almost all tests are written using Expect to create an expectation over some value (the subject). Expect returns an expectation with methods for testing the subject.

Some expectation methods test the value directly, such as IsEmpty(), IsNil() and IsNotNil():

  err := DoSomething()
  Expect(err).IsNil()
Using Matchers

The To method of an expectation delegates the evaluation of a test to a type-safe matcher, usually provided by a factory function where the factory function itself is named to fluently express the expected outcome, e.g.:

  Expect(got).To(Equal(expected))

In this example, the Equal() function is a factory function that returns an equal.Matcher, used to test that the subject is equal to some expected value.

The Should method provides the same functionality as To(), but accepts matchers that are not type-safe, referred to as any-matchers as they accept any as the subject type. This is necessary for matchers which do not accept any arguments or where the compatible arguments cannot be expressed as a generic type constraint.

An example of an any-matcher is BeEmpty(), which can be used to test whether a slice, map, channel or string is empty:

  Expect(got).Should(BeEmpty())

Further information on any-matchers is provided in the section on Type-Safety: Any-Matchers

Type-Safety: Matcher Compatibility

Expect() is a generic function, where the type T is inferred from the subject value; the To() function will only accept matchers that are compatible with the type of the subject value.

For example, in the previous example, the equal.Matcher uses the == operator to determine equality, so is constrained to types that satisfy comparable. As a result, values of non-comparable type cannot be tested using this matcher:

  Expect(got).To(Equal([]byte("expected result"))) // ERROR: cannot use `Equal` with `[]byte`

In this case, two alternatives exist:

  1. DeepEqual() returns a equal.DeepMatcher that may be used with any type, using reflect.DeepEqual for equality;

  2. EqualBytes returns a bytes.EqualMatcher which provides test failure reports that are specific to []byte values.

  // DeepEqual can be used with any type; uses reflect.DeepEqual for equality
  // but can result in verbose failure reports since both expected and got
  // values are printed in full
  Expect(got).To(DeepEqual([]byte("expected result")))

  // EqualBytes is a type-safe matcher specifically for []byte values
  // providing failure reports that report and highlight differences between
  // byte slices accurately and concisely
  Expect(got).To(EqualBytes([]byte("expected result")))
Type-Safety: Any-Matchers

Not all matchers are constrained by types; some matchers accept any as the subject type, allowing them to be used with any value, referred to as "any-matchers".

There are two main use cases for any-matchers:

  • the matcher supports testing values of a variety of types that cannot be described in a generic type constraint

Why?: if a matcher is designed to work with a set of types that cannot be described in a generic type constraint, it must accept any as the subject type.

For example, the BeEmpty() matcher can be used to test whether a slice, map, channel or string is empty (and more), but there is no way to express that set of types in a type constraint.

  • the type of the expected value is implicit in the test, rather than explicit

Why?: without an explicit expected value, it is not possible to infer the corresponding subject type; the matcher must accept any as the subject type

Whilst any-matchers can be used with To(), by casting the subject to any, this can be cumbersome and disrupts the fluency of the test:

  Expect(any(got)).To(BeEmpty())

As an alternative, the Should() and ShouldNot() methods provide the same functionality as To()/ToNot(), but accepting any-matchers rather than type-safe matchers:

  Expect(got).Should(BeEmpty())
  Expect(got).ShouldNot(BeEmpty())
Type-Safety: Invalid Tests fail as Invalid

If an expectation method is called inappropriately on a subject, the test will often fail as an invalid test. For example, if the IsNil() method is called on a value of a type that does not support a meaningful nil value the test will fail, not because the value is not nil but because the test itself is invalid:

  Expect(42).IsNil() // <== INVALID TEST: `int` is not nilable

In general, expectation tests will attempt to provide a meaningful test consistent with the intent, only failing as invalid if a meaningful test is not possible.

Guides

Basic Usage

Test Runners

Advanced Usage

In addition to performing common, basic tests, the test package also provides support for more advanced testing scenarios:

Category Description
Mocking Functions mock functions for testing
Recording Console Output record output of a function that writes to stdout and/or stderr
Test for an Expected Type test that a value is of an expected type
Testing Test Helpers test your own test helper functions


Setting Expectations

Almost all tests start with setting an expectation over some value (the subject).

The Expect function returns an expectation with methods for testing the subject:

  Expect(got)  // returns an expectation for the value `got`

In addition to a subject, the Expect() function accepts options to configure the expectation, passed as variadic arguments. Currently the only option is a name for the subject, which is used in test failure reports to identify the subject being tested:

  Expect(result, "result")  // returns an expectation named "result"

An expectation alone does not perform any tests; it simply provides a way to express an expectation over a value. The expectation is evaluated when a test method is called on the expectation, such as IsNil(), IsNotNil(), IsEmpty(), To() or DidOccur().

Short-Circuit Evaluation

If an expectation is critical to the test, it can be useful to short-circuit the test execution if the expectation fails. For example, if a value is expected to not be nil and further tests on that value will panic or be guaranteed to fail:

  Expect(value).ShouldNot(BeNil())
  Expect(value.Name).To(Equal("some name"))  // this test will panic if value is nil

To short-circuit the test execution if the expectation fails, the opt.IsRequired(true) option can be passed to the expectation method:

  Expect(value).ShouldNot(BeNil(), opt.IsRequired(true))
  Expect(value.Name).To(Equal("some name"))  // this test will not be executed if value is nil

Alternatively, the Require() function can be used to create the expectation:

  Require(value).ShouldNot(BeNil())
  Expect(value.Name).To(Equal("some name"))  // this test will not be executed if value is nil

In both cases, if the expectation fails the current test exits without evaluating any further expectations. Execution continues with the next test.

Testing Nil/Not Nil

A nilness matcher is provided which may be used with the Should() or ShouldNot() methods:

  Expect(value).Should(BeNil())      // fails if value is not nil or of a non-nilable type
  Expect(value).ShouldNot(BeNil())   // fails if value is nil

Since these tests are common, IsNil() and IsNotNil() convenience methods are also provided on expectations:

    Expect(value).IsNil()      // fails if value is not nil or of a non-nilable type
    Expect(value).IsNotNil()   // fails if value is nil

πŸ’‘ If IsNil()/Should(BeNil()) is used on a subject of a type that does not have a meaningful nil value, the test will fail as invalid.

Types that may be tested for nil are: chan, func, interface, slice, map, and pointer.

IsNotNil()/ShouldNot(BeNil()) will NOT fail on a non-nilable subject.

  var got 42
  Expect(got).IsNil()      // <== INVALID TEST: `int` is not nilable
  Expect(got).IsNotNil()   // <== VALID TEST: `int` is not nilable, so this test passes

Testing Errors

Testing that an Error did not occur

There are two ways to explicitly test that an error did not occur:

  Expect(err).DidNotOccur()
  Expect(err).IsNil()

A third way to test that an error did not occur is to use the Is() method, passing nil as the expected error:

  Expect(err).Is(nil)

This is most useful when testing an error in a table driven test where each test case may have an expected error or nil:

  Expect(err).Is(tc.err)  // tc.err may be nil or an expected error

Testing that an Error occurred (any error)

  Expect(err).DidOccur()
  Expect(err).IsNotNil()

Testing that a Specific Error Occurred

  Expect(err).Is(expectedError) // passes if `errors.Is(err, expectedError)` is true

If nil is passed as the expected error, the test is equivalent to IsNil().

Testing for Panics

Panics can be tested to ensure that an expected panic did (or did not) happen. Since panic tests rely on the recovery mechanism in Go, they must be deferred to ensure that the panic is captured and tested correctly.

⚠ There must be at most ONE panic test per function; multiple panic tests (or other calls to recover() in general) in the same function will not work as expected.

When testing panics, recovered values may be significant or they may be ignored.

For example, if testing only that a panic occurred without caring about the recovered value:

  defer Expect(Panic()).DidOccur()

By contrast, if the recovered value is significant, it can be tested by specifying the expected panic value. The following tests will pass if a panic occurs and the recovered value is equal to the expected string:

  defer Expect(Panic("expected panic")).DidOccur()

⚠ Panic(nil) is a special case. see: Panic(nil) vs NilPanic()

Testing a Panic with a Recovered Error

When testing for a panic that recovers an error and the expected recovered value is specified, the test will pass if the recovered value is an error and errors.Is(recovered, expectedErr) is true:

  defer Expect(Panic(expectedErr)).DidOccur()

Testing that a Panic did NOT occur

It is also possible to explicitly test that a panic did not occur:

  defer Expect(Panic()).DidNotOccur()

πŸ’‘ Expect(Panic(nil)).DidOccur() is a special case that is exactly equivalent to the above test for no panic. See: Panic(nil) vs NilPanic()

Again, if the recovered value is significant, it can be tested by specifying the expected panic value.

⚠ If a value is recovered from a panic that is different to that expected, the test will fail as an unexpected panic.

Panic(nil) vs NilPanic()

Prior to go 1.21, recover() could return nil if a panic(nil) had been called, making it impossible to distinguish from no panic having occurred.

From go 1.21 onwards, a panic(nil) call is now transformed such that a specific runtime error will be recovered.

Panic(nil) is treated as a special case that is used to test that a panic did NOT occur. i.e. the following are exactly equivalent:

  // test that a panic did NOT occur
  defer Expect(Panic(nil)).DidNotOccur()

and

  // also test that a panic did NOT occur
  defer Expect(Panic(nil)).DidOccur()

This may seem counter-intuitive, but there is a good reason for this.

The motivation is to simplify table-driven tests where each test case may expect a panic or not. Without this special case, the test would require a conditional to determine whether to test for a panic or not:

  if tc.expectPanic {
    defer Expect(Panic(tc.expectedPanic)).DidOccur()
  } else {
    defer Expect(Panic()).DidNotOccur()
  }

With the special case of Panic(nil), the test can be simplified to:

  defer Expect(Panic(tc.expectedPanic)).DidOccur()

Where tc.expectedPanic may be nil (panic not expected to occur) or an expected value to be recovered from a panic.

In the unlikely event that you need to test specifically for a panic(nil) having occured, the go 1.21+ runtime error can be tested for:

  defer Expect(Panic(&runtime.PanicNilError{})).DidOccur()

To make even this unlikely case easier, the NilPanic() function is provided, so the above can be simplified to:

  defer Expect(NilPanic()).DidOccur()
Testing Emptiness

The BeEmpty() and BeEmptyOrNil() matchers are provided to test whether a value is considered empty, or not. These are any-matchers for use with the Should() or ShouldNot() methods.

  Expect(value).Should(BeEmpty())         // fails if value is not empty or nil
  Expect(value).Should(BeEmptyOrNil())    // fails if value is not empty and not nil
  Expect(value).ShouldNot(BeEmpty())      // fails if value is empty

BeEmpty() and BeEmptyOrNil() are provided to differentiate between empty and nil values where useful.

For example, if testing a slice, IsEmpty() will pass if the slice is empty but will fail if the slice is nil, while IsEmptyOrNil() will pass in both cases.

Emptiness tests will fail as invalid if emptiness of the value cannot be determined.

Emptiness is defined as follows:

  • for string, slice, map, chan and array types, emptiness is defined as len(value) == 0
  • for all other types, emptiness is determine by the implementation of a Count(), Len() or Length() method returning 0 (zero) of type int, int64, uint or uint64
  • if testing a value for this emptiness cannot be determined, the test will fail as invalid.

Testing With Matchers

The To(), ToNot(), Should() and ShouldNot() methods delegate the evaluation of a test to a matcher, usually provided by a factory function. Matcher factory functions are typically named to describe the expected outcome in a fluent fashion as part of the test expression, e.g.:

  Expect(got).To(Equal(expected))  // uses an equal.Matcher{} from the 'blugnu/test/matchers/equal' package

"Matching" Methods: For brevity, the To(), ToNot(), Should() and ShouldNot() methods are referred to generically as Matching Methods.

Type-Safe Matchers

A type-safe matcher is a matcher that is compatible with a specific type of subject value. A type-safe matcher may be constrained to a single, explicit formal type, or it may be a generic matcher where type compatability is expressed through the constraints on the generic type parameter.

For example:

  • HasContextKey(): is explicitly compatible only with context.Context values
  • Equal(): uses a generic matcher that is compatible with any type T that satisfies the comparable constraint

By contrast:

  • BeEmpty() is NOT type-safe: it is compatible with (literally) any type

Any-Matchers

An any-matcher is a matcher that accepts any as the subject type, allowing it to be used with literally any value. Any-matchers are used with the Should() or ShouldNot() matching methods.

Any-matchers may also be used with the To() or ToNot() matching methods if the formal type of the subject is any, but this is not recommended.

Built-In Matchers

A number of matchers are provided in the test package, including:

Factory Function Subject Type Description
BeEmpty() any Tests that the subject is empty but not nil
BeEmptyOrNil() any Tests that the subject is empty or nil
BeGreaterThan(T) T cmp.Ordered Tests that the subject is greater than the expected value using the > operator
BeLessThan(T) T cmp.Ordered Tests that the subject is less than the expected value using the < operator
BeNil() any Tests that the subject is nil
Equal(T) T comparable Tests that the subject is equal to the expected value using the == operator
DeepEqual(T) T any Tests that the subject is deeply equal to the expected value using reflect.DeepEqual
EqualBytes([]byte) []byte Tests that []byte slices are equal, with detailed failure report highlighting different bytes
EqualMap(map[K,V]) map[K,V] Tests that the subject is equal to the expected map
ContainItem(T) []T Tests that the subject contains an expected item
ContainItems([]T) []T Tests that the subject contain the expected items (in any order, not necessarily contiguously)
ContainMap(map[K,V]) map[K,V] Tests that the subject contains the expected map (keys and values must match)
ContainMapEntry(K,V) map[K,V] Tests that the subject contains the expected map entry
ContainSlice([]T) []T Tests that the subject contains the expected slice (items must be present contiguously and in order)
ContainString(expected T) T ~string Tests that the subject contains an expected substring
HaveContextKey(K) context.Context Tests that the context contains the expected key
HaveContextValue(K,V) context.Context Tests that the context contains the expected key and value

Matchers are used by passing the matcher to one of th expectation matching methods together with options to control the behaviour of the expectation or the matcher itself.

A matcher is typically constructed by a factory function accepting any arguments required by the matcher. It is worth repeating that options supported by the matcher are passed as arguments to the matching method, not the matcher factory:

  // the opt.OnFailure option replaces the default error report
  // with the custom "failed!" message
  Expect(got).To(Equal(expected),
    opt.OnFailure("failed!"),
  )

  // override the use of the `==` operator with a custom comparison function
  // where the subject type is a hypothetical `MyStruct` type
  Expect(got).ToNot(Equal(expected),
    func(exp, got MyStruct) bool { return /* custom comparison logic */ },
  )

Matcher Options

Matching methods accept options as variadic arguments following the matcher.

The matching methods themselves support options for customising the test error report in the event of failure.

  Expect(got).To(Equal(expected),
    opt.OnFailure(fmt.Sprintf("expected %v, got %v", expected, got)),
  )

Options supported by matching methods (and therefore all matchers) include:

Option Description
opt.FailureReport(func) a function that returns a custom error report for the test failure; the function must be of type func(...any) []string
opt.OnFailure(string) a string to use as the error report for the test failure; this overrides the default error report for the matcher
opt.AsDeclaration(bool) a boolean to indicate whether values (other than strings) in test failure reports should be formatted as declarations (%#v rather than %v)
opt.QuotedStrings(bool) a boolean to indicate whether string values should be quoted in failure reports; defaults to true
opt.IsRequired(bool) a boolean to indicate whether the expectation is required; defaults to false

opt.OnFailure() is a convenience function that returns an opt.FailureReport with a function that returns the specified string in the report.

opt.FailureReport and opt.OnFailure() are mutually exclusive; if both are specified, only the first in the options list will be used.

The ...any argument to an opt.FailureReport function is used to pass any options supplied to the matcher, so that the error report can respect those options where appropriate.

See the Custom Failure Report Guide for details.

Matchers may support options to modify their behaviour. The specific options supported by a matcher are documented on the relevant matcher factory function.

Examples of other options supported by some matchers include:

Option Description Default
opt.ExactOrder(bool) a boolean to indicate whether the order of items in a collection is significant false
opt.CaseSensitive(bool) a boolean to indicate whether string comparisons should be case-insensitive true
opt.AsDeclaration(bool) a boolean to indicate whether values other than strings should be formatted as declarations (%#v vs %v) false
opt.QuotedStrings(bool) a boolean to indicate whether string values should be quoted in failure reports true
func(T, T) bool a type-safe custom comparison function; the type T is the type of the subject value
func(any, any) bool a custom comparison function accepting expected and subject values as any

type-safe custom comparison functions are preferred over any comparisons. Only one should be specified; if multiple comparison functions are specified, the first type-safe function will be used in preference over the first any function.

Custom Matchers

Custom matchers may be implemented by defining a type that implements a Match(T, ...any) bool method.

T may be:

  • an explicit, formal type
  • a generic type parameter with constraints
  • any if the matcher is not type-safe

Refer to the Custom Matchers Implementation Guide for details.



Testing Maps

The test package provides matchers for testing maps, including the ability to test for equality, containment of items or the existence of a specific key:value entry.

Matcher Subject Type Description
EqualMap(map[K,V]) map[K,V] tests that the subject is equal to the expected map
ContainMap(map[K,V]) map[K,V] tests that the subject contains the expected map (keys and values must match, order is not significant)
ContainMapEntry(K,V) map[K,V] tests that the subject contains the expected map entry (key and value must match)

These matchers accept either a map or a pair of key and value parameters; type inference ensures type-compatibility with expectation subjects that are maps.

Testing Keys or Values in Isolation

When testing keys or values in isolation (a key without a value or vice versa), any matcher would not have enough information to determine the type of both key and value to provide compatibility with a map subject without explicitly instantiating with declared type information.

To avoid this, functions are provided to extract keys or values as slices, enabling the use of slice matchers in fluent fashion, e.g.:

  Expect(KeysOfMap(m)).To(ContainItem("key"))     // tests that the map contains the key "key"
  Expect(ValuesOfMap(m)).To(ContainItem("value")) // tests that the map contains a key having the value "value"


Testing Slices

The test package provides matchers for testing slices, including the ability to test for equality, containment of items or the existence of a specific item in a slice.

Matcher Subject Type Description
EqualSlice([]T) []T tests that the subject is equal to the expected slice (items must be present contiguously and in order); options supported include opt.ExactOrder(false) or opt.AnyOrder(), to allow equality of slices where order is not significant
ContainItem(T) []T tests that the subject contains an expected item
ContainItems([]T) []T tests that the subject contains the expected items (in any order, not necessarily contiguously)
ContainSlice([]T) []T tests that the subject contains the expected slice (items must be present contiguously and in order); options supported include opt.ExactOrder(false) or opt.AnyOrder(), to allow containment of slices where order is not significant

To test for an expected length of a slice, use len(slice) as the subject:

  Expect(len(slice)).To(Equal(expectedLength)) // tests that the slice has the expected length

For emptiness tests where precise length is not significant (other than zero, for emptiness), the IsEmpty() and IsNotEmpty() methods can be used on any slice subject:

  Expect(slice).IsEmpty()      // tests that the slice is empty
  Expect(slice).IsNotEmpty()   // tests that the slice is not empty


Testing Context

Passing values in a context.Context is a common pattern in Go, and the test package provides a way to test that a context contains the expected values.

The HaveContextKey(K) and HaveContextValue(K,V) matchers can be used to test that a context contains a specific key or key-value pair.

  Expect(ctx).To(HaveContextKey("key"))            // tests that the context contains the key "key"
  Expect(ctx).To(HaveContextValue("key", "value")) // tests that the context contains the key "key" with value "value"

The type of the key is determined by the type parameter K of the matcher, which must be the type of the key used in the context, not just compatible.

For example, if a custom string type has been used for the key, the value of any key expected by the matcher must be cast to that type:

  // e.g. where MyPackageContextKey is the type used for context keys:
  Expect(ctx).To(HaveContextKey(MyPackageContextKey("key")))   


Test Runner Functions

The testing.T type is the standard test runner in Go. This type also provides a way to run subtests using the test.Run() function, which accepts a function that performs the test using a testing.T value to report the outcome of the test.

The test package simplifies and extends this by providing the Run() function which accepts a test runner.

Test runners are provided to:

  • run an individual, named subtest (directly equivalent to t.Run() in the standard library) using the Test() runner

  • run an individual, named subtest in parallel (equivalent to t.Run() with a call to t.Parallel() in the subtest) using the ParallelTest() runner

  • run a suite of tests defined in a table-driven test, using the Testcases() runner

  • run a suite of parallel tests defined in a table-driven test, using the ParallelCases() runner

  • run a suite of Test Helper scenarios defined in a table-driven test, using the HelperTests() runner

  • run a flaky test repeatedly until (hopefully) it passes, using the FlakyTest() runner

Subtests

To run a named sub-test:

  Run(Test("sub-test name", func() {
    // test code here
  }))

To run a named sub-test in parallel:

  Run(ParallelTest("sub-test name", func() {
    // test code here
  }))

Table-Driven Tests

Table-driven tests are a common pattern in Go, allowing multiple test cases to be defined in a single test function with a common test execution function iterating over a slice of test cases.

The test package provides a way to define and run table-driven tests using the Testcases[T]() test runner. This is a generic test runner that runs tests defined by a set of test cases of type T.

The Testcases[T]) runner requires a test executor to be provided using either For() or ForEach() functions:

  • the For() executor function accepts a test case name and a testcase, allowing the test code to refer to the test case by name in the test output or to vary the test behaviour based on the name, if required;

  • the ForEach() executor function accepts only a test case, without a name, allowing a simpler declaration if the name is not required.

Following the executor function, a variadic list of test cases is provided, using the following functions:

  • Case(name string, tc T) to define a single, named test case
  • Cases(cases []T) to define multiple test cases
  • ParallelCase(name string, tc T) to define a single, named test case that runs in parallel
  • ParallelCases(cases []T) to define multiple test cases that run in parallel
Debug and Skip Test Cases

When debugging tests, it can be useful to skip some test cases or to run only a subset of test cases.

When a test case is skipped, it will not be run and the test case is reported as skipped in the test output.

When one or more test cases are, only the debug test cases are run by the runner.

πŸ’‘ even if all test cases pass, the test runner will fail with a warning when any test cases are skipped or debugged

There are two ways to skip/debug test cases:

  • if the test case is added using the Case() function, simply change this to Skip() or Debug(); this approach works without requiring any changes to the test runner or test case type

  • add boolean fields to the test case type to indicate whether the test case should be skipped or debugged; these must be named skip/Skip or debug/Debug respectively;

bulb: the Skip() and Debug() functions override any skip or debug fields when adding a test case

  type TestCase struct {
    // fields...
    skip bool   // set true to skip this test case
    debug bool  // set true to debug this test case
  }

  // skip a test case
  Run(Testcases(
     ForEach(func(tc TestCase) {
        // test code here
     }),
     Skip("first case", TestCase{...debug: true}),  // this test case will be skipped; the `debug` field is overridden by the Skip() function
     Case("second case", TestCase{...}),
  ))

  // debug a test case
  Run(Testcases(
     ForEach(func(tc TestCase) {
        // test code here
     }),
     Debug("first case", TestCase{... skip: true}), // ONLY this test case will be run; the 'skip' field is overridden by the Debug() function
     Case("second case", TestCase{...}),
  ))

Flaky Tests

Flaky tests are tests that may fail intermittently, often due to timing issues or other non-deterministic factors. The test package provides a way to run flaky tests using the FlakyTest() runner.

The FlakyTest() runner accepts a test function and options to configure the retry attempts. If the test fails, the runner will repeat the test up to a maximum number of attempts and/or until a maximum elapsed time has passed (whichever occurs first).

If the test passes before the maximum number of attempts or elapsed time is reached, the test passes; any failed attempts are ignored.

If the test fails on all attempts, the test fails, and the outcome of each attempt is reported in the test output.



Mocking Functions

When testing functions that call other functions, it is often necessary to mock the functions being called to ensure that the tests are isolated and that the functions being tested are not dependent on the behaviour of the functions being called.

The test.MockFn[A, R] type provides a way to mock a function accepting arguments of type A and returning a result of type R.

All test.MockFn values support an optional error value which may simply be ignored/not used if the mocked function does not return an error.

If the function being mocked does not return any value other than an error, the result type R should be any and ignored. Similarly if the function being mocked does not require any arguments, the argument type A should be any and ignored.

Fake Function Results

The test.MockFn type can provide fake results for a mocked function. Fake results may be setup in two different ways:

  • expected calls mode. ExpectCall() is used configure an expected call; this returns a value with a WillReturn method to setup a result to be returned for that call. In this mode, calls to the mocked function that do not match the expected calls will cause the test to fail.

  • mapped result more. WhenCalledWith(args A) is used to setup a result to be returned when the mocked function is called with the specified arguments. In this mode, calls to the mocked function that do not match any of the mapped results will cause the test to fail.

Multiple Arguments/Result Values

If a function being mocked accepts multiple arguments and/or returns multiple result values (in addition to an error), the types A and/or R should be a struct type with fields for the arguments and result values required:

type fooArgs struct {
  A int
  B string
}

type fooResult struct {
  X int
  Y string
}

type mockFoo struct {
  foo test.MockFn[fooArgs, fooResult]
}

func (mock *mockFoo) Foo(A int, B string) (int, string, error) {
  result, err := mock.foo.RecordCall(fooArgs{A, B})
  return result.X, result.Y, err
}

Recording Console Output

The test.Record function records the output of a function that writes to stdout and/or stderr, returning the output as a pair of []string values for stdout and stderr respectively.

πŸ’‘ The function does not return an error; it will panic if the redirection fails. This is an intentional design choice to ensure that a test fails if the mechanism is not working correctly, avoiding incorrect results without requiring a test to handle any error.

Since failed tests will write to stdout, the output will include any test failures that occur during execution of the captured function. You may wish to structure your code to perform tests outside of the recorded functions to avoid this and simplify testing of the output:

func TestDoSomething(t *testing.T) {
  With(t)

  // ACT
  var (
    err error
    result string
  )
  stdout, stderr := Record(func () {
    result, err := DoSomething()
    return err
  })

  // ASSERT
  Expect(err).IsNil()
  Expect(result).To(Equal("foo"))
  Expect(stdout).To(ContainItem("DoSomething wrote this to stdout"))
}

Testing a Test Helper

If you write your own test helpers (or matchers), you should of course test them. A TestHelper() function is provided to enable you to do just that.

TestHelper() accepts a function that executes your test helper; it is performed using a separate test runner, independent of the current test. This allows the helper being tested to fail without affecting the outcome of the test that is testing it.

The TestHelper() function returns a test.R value that contains information about the outcome of the test. You could test the information in this R value directly, but the R type provides methods to make this easier.

Testing the Outcome

To test the outcome of a test, without considering any output, you can pass the expected outcome as an argument to the R.Expect() method:

  result := TestHelper(func() {
    /* your test code here */
  })

  result.Expect(TestPassed)

Testing Test Helper Output

It is recommended to test the output of your test helper or matcher when it fails. You can do this by passing the expected lines of test output as strings to the R.Expect() method:

  result := TestHelper(func() {
    /* your test code here */
  })

  result.Expect(
    "expected output line 1",
    "expected output line 2",
  )

πŸ’‘ By testing the output, the test is implicitly expected to fail, so the R.Expect() method in this case will also test that the outcome is TestFailed.

Running Multiple Test Scenarios

A specialised version of RunScenarios() is provided to test a test helper or custom matcher: RunTestScenarios(). This accepts a slice of TestScenario values, where each scenario is a test case to be run against your test helper or matcher.

RunTestScenarios() implements a test runner function for you, so all you need to do is provide a slice of scenarios, with each scenario consisting of:

  • Scenario: a name for the scenario (scenario); each scenario is run in a subtest using this name;
  • Act: a function that contains the test code for the scenario; this function has the signature func();
  • Assert: a function that tests the test outcome; this function has the signature func(*R) where R is the result of the test scenario.

The Assert function is optional; if not provided the scenario is one where the test is expected to pass without any errors or failures.

Debugging and Skipping Scenarios

When you have a large number of scenarios it is sometimes useful to focus on a subset, or specific test, or to ignore scenarios that are not yet implemented or known to be failing with problems which you wish to ignore while focussing on other scenarios.

The RunTestScenarios() function and TestScenario type support this by providing a Skip and Debug field on each TestScenario.

⚠   When setting either Debug or Skip to true, it is important to remember to remove those settings when you are ready to move on to the next focus of your testing.

  scenarios := []TestScenario{
    {
      Scenario: "test scenario 1",
      Act: func() { /* test code */ },
      Assert: func(r *R) { /* assertions */ },
    },
    {
      Scenario: "test scenario 2",
      Skip: true, //                              <== this scenario won't run
      Act: func() { /* test code */ },
      Assert: func(r *R) { /* assertions */ },
    },
  }

Setting Skip to true may be impractical if you have a large number of scenarios and want to run only a few of them. In this case, you can use the Debug field to focus on a single scenario or a subset of scenarios.

Debugging Scenarios

πŸ’‘ Β  Setting Debug does not invoke the debugger or subject a test scenario to any special treatment, beyond selectively running it. The name merely reflects that it most likely to be of use when debugging.

When Debug is set to true on any one or more scenarios, the test runner will run ONLY those scenarios, skipping all other scenarios:

  scenarios := []TestScenario{
    {
      Scenario: "test scenario 1",
      Debug: true, //                              <== only this scenario will run
      Act: func() { /* test code */ },
      Assert: func(r *R) { /* assertions */ },
    },
    {
      Scenario: "test scenario 2",
      Act: func() { /* test code */ },
      Assert: func(r *R) { /* assertions */ },
    },
    {
      Scenario: "test scenario 3",
      Act: func() { /* test code */ },
      Assert: func(r *R) { /* assertions */ },
    },
  }
⚠   If both Debug and Skip are set true the scenario is skipped

Test for an Expected Type

You can test that some value is of an expected type using the ExpectType function.

This function returns the value as the expected type and true if the test passes; otherwise the zero-value of the expected type is returned, with false.

A common pattern when this type of test is useful is to assert the type of some value and then perform additional tests on that value appropriate to the type:

func TestDoSomething(t *testing.T) {
  With(t)

  // ACT
  result := DoSomething()

  // ASSERT
  if cust, ok := ExpectType[Customer](result); ok {
    // further assertions on cust (type: Customer) ...
  }
}

This test can only be used to test that a value is of a type that can be expressed through the type parameter on the generic function.

For example, the following test will fail as an invalid test:

  type Counter interface {
    Count() int
  }
  ExpectType[Counter](result) // INVALID TEST: cannot be used to test for interfaces

Testing Test Helpers

If you write your own test helper functions you should of course test them. This is problematic when using *testing.T since when your test helper fails the test that is testing your helper will also fail.

blugnu/test addresses this by providing a TestHelper() function that runs your test helper in a separate test runner, capturing the outcome and any report. This allows the test helper to fail without affecting the outcome of the test that is testing it, allowing that test to then assert the expected outcome.

The TestHelper() function accepts a function that executes your test helper, and returns a test.R value that contains information about the outcome of the test.

The R type provides methods to test the outcome of the test, primarily this will involve testing the helper (correctly) failed and produced an expected report. This is accomplished by calling the R.Expect() method with the expected test report; when an expected report is passed, the test is implicitly expected to fail, so the R.Expect() method will also test that the outcome is TestFailed:

func TestMyTestHelper(t *testing.T) {
  With(t)

  result := TestHelper(func() {
    MyTestHelper() // this is the test helper being tested
  })

  result.Expect(
    "expected failure message", // the expected failure message
  )
}

To test that the test helper passed, you can call the R.Expect() method with the expected outcome:

func TestMyTestHelper(t *testing.T) {
  With(t)

  result := TestHelper(func() {
    MyTestHelper() // this is the test helper being tested
  })

  result.Expect(TestPassed) // the test helper is expected to pass
}

Testing a Test Helper with Scenarios

A test runner is provided to test a test helper in a variety of scenarios. The runner defines a HelperScenario type for each scenario, which contains the following fields:

  • Scenario: a name for the scenario (scenario); each scenario is run in a subtest using this name;
  • Act: a function that contains the test code for the scenario; this function has the signature func();
  • Assert: a function that tests the test outcome; this function has the signature func(*R) where R is the result of the test scenario.
  • Skip: a boolean to indicate whether the scenario should be skipped; defaults to false
  • Debug: a boolean to indicate whether the scenario is a debug scenario; defaults to false

πŸ’‘ If the Debug field is set to true on any scenario(s), the test runner will run only those scenarios, and these will be run even if Skip is also true.

For scenarios where the helper is expected to pass, the Assert function is optional; if the Assert function is nil, the test runner will assert that the test passed without any errors or failures.

The HelperTests() test runner accepts a variadic list of HelperScenario values, where each scenario is a test case to be run against your test helper:

func TestMyTestHelper(t *testing.T) {
  With(t)
  
  Run(HelperTests([]test.HelperScenario{
    {Scenario: "provided with an empty string",
       Act: func() {
         MyTestHelper("")
       },
       Assert: func(r *test.R) {
         r.ExpectInvalid("input string cannot be empty")
       },
    },
    {Scenario: "this is expected to pass",
       Act: func() {
         MyTestHelper("some input")
       },
    },
  }...))
}

πŸ’‘ In the example above, the scenarios are provided as a slice literal using the ... operator to expand the slice into a variadic argument list.

Documentation ΒΆ

Index ΒΆ

Examples ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

View Source
var (
	// general errors
	ErrInvalidArgument  = errors.New("invalid argument")
	ErrInvalidOperation = errors.New("invalid operation")

	// mock and fake errors
	ErrExpectationsNotMet = errors.New("expectations not met")
	ErrExpectedArgs       = errors.New("arguments were expected but not recorded")
	ErrNoResultForArgs    = errors.New("no result for arguments")
	ErrUnexpectedArgs     = errors.New("the arguments recorded did not match those expected")
	ErrUnexpectedCall     = errors.New("unexpected call")
	ErrResultNotUsed      = errors.New("result not used")

	// recording errors
	ErrRecordingFailed                 = errors.New("recording failed")
	ErrRecordingStdout                 = errors.New("error recording stdout")
	ErrRecordingStderr                 = errors.New("error recording stderr")
	ErrRecordingUnableToRedirectLogger = errors.New("unable to redirect logger output")
)

Functions ΒΆ

func BeBetween ΒΆ added in v0.11.0

func BeBetween[T cmp.Ordered](v T) ordered.IsBetweenInitializer[T]

BeBetween returns a matcher that will fail if the matched value is not between the limits of a defined interval. The type T must be ordered (i.e. it must support the comparison operators <, <=, >, >=).

BeBetween accepts a single value as an argument providing one limit of the interval. The second limit is provided by calling the [And] method on the returned initializer:

Expect(n).To(BeBetween(10).And(20))

By default the matcher compares the matched value to the closed interval defined by the specified values.

Supported Options ΒΆ

The interval can be changed by providing an opt.IntervalClosure option. The default interval is opt.IntervalClosed. Other options are:

func BeEmpty ΒΆ added in v0.9.0

func BeEmpty() *emptiness.Matcher

BeEmpty returns a matcher that checks if the value is empty.

The returned matcher is an `AnyMatcher` that may only be used with the `Should()` method, or with `To()` where the subject is of formal type any.

NOTE: A nil value is not considered empty and will fail this test. To test for an empty value that may be nil, use BeEmptyOrNil() instead.

i.e. an empty slice will pass this test, but a nil slice will not.

This test may be used to check for empty strings, arrays, slices, channels, maps and any type that implement a Count(), Len() or Length() function returning int, int64, uint, or uint64.

If a value of any other type is tested, the test fails with a message similar to:

emptiness.Matcher: requires a value of type string, array, slice, channel or map,
                   or a type that implements a Count(), Len(), or Length() function
                   returning int, int64, uint, or uint64.

Supported Options ΒΆ

opt.FailureReport(func)     // a function that returns a custom test
                            // failure report if the test fails.

opt.OnFailure(string)       // a string to output as the failure
                            // report if the test fails.

func(v T) bool              // a function that returns true if the value
                            // is empty; the value argument must be of
                            // the same type as the expectation subject.

func BeEmptyOrNil ΒΆ added in v0.9.0

func BeEmptyOrNil() *emptiness.Matcher

BeEmptyOrNil returns a matcher that checks if a value is empty or nil.

Compatible Methods and Subjects ΒΆ

Expect(any(subject)).To(...)       // i.e. where 'subject' is of type `any`
Expect(subject).Should(...)        // for any 'subject'

This matcher may be used to check for empty strings and arrays as well as empty or nil slices, channels, maps and any type that implements a Count(), Len() or Length() method returning int, int64, uint, or uint64.

If the subject is of any other type is tested, the test fails with a message similar to:

emptiness.Matcher: requires a value of type string, array, slice, channel or map,
                   or a type that implements a Count(), Len(), or Length() function
                   returning int, int64, uint, or uint64.

Supported Options ΒΆ

opt.FailureReport(func)     // a function that returns a custom test
                            // failure report if the test fails.

opt.OnFailure(string)       // a string to output as the failure
                            // report if the test fails.

func(v T) bool              // a function that returns true if the value
                            // is empty or nil; the value argument must be of
                            // the same type as the subject.

func BeFalse ΒΆ added in v0.8.0

func BeFalse() bools.BooleanMatcher

BeFalse returns a matcher that will fail if the matched value is not false.

Supported Options ΒΆ

opt.FailureReport(...)   // a function returning a custom failure report
                         // in the event that the test fails
Example ΒΆ
test.Example()

Expect(true).To(BeFalse())
Output:

expected false, got true

func BeGreaterThan ΒΆ added in v0.8.0

func BeGreaterThan[T cmp.Ordered](want T) ordered.RelativeMatcher[T]

BeGreaterThan returns a matcher that will fail if the matched value is not greater than the expected value. The type T must be ordered (i.e. it must support the comparison operators <, <=, >, >=).

By default the matcher uses the > operator to compare the values. This can be overridden by providing a custom comparison function as an option. The function must take two arguments of type T and return a boolean indicating whether the first argument is greater than the second argument.

To compare values using the <= operator, call the [OrEqual] modifier method on the returned matcher:

Expect(n).To(BeGreaterThan(10).OrEqual())

Supported Options ΒΆ

func(T, T) bool          // a custom comparison function to compare values
                         // (overriding the use of the > operator)

opt.QuotedStrings(bool)  // determines whether string values are quoted
                         // in test failure report (quoted by default);
                         // the option has no effect if the value is not
                         // a string type

opt.FailureReport(...)   // a function returning a custom failure report
                         // in the event that the test fails

opt.OnFailure(string)    // a string to output as the failure
                         // report if the test fails

func BeLessThan ΒΆ added in v0.8.0

func BeLessThan[T cmp.Ordered](want T) ordered.RelativeMatcher[T]

BeLessThan returns a matcher that will fail if the matched value is not less than the expected value. The type T must be ordered (i.e. it must support the comparison operators <, <=, >, >=).

By default the matcher uses the < operator to compare the values. This can be overridden by providing a custom comparison function as an option. The function must take two arguments of type T and return a boolean indicating whether the first argument is less than the second argument.

To compare values using the <= operator, call the [OrEqual] modifier method on the returned matcher:

Expect(n).To(BeLessThan(10).OrEqual())

Supported Options ΒΆ

func(T, T) bool          // a custom comparison function to compare values
                         // (overriding the use of the < operator)

opt.QuotedStrings(bool)  // determines whether string values are quoted
                         // in test failure report (quoted by default);
                         // the option has no effect if the value is not
                         // a string type

opt.FailureReport(...)   // a function returning a custom failure report
                         // in the event that the test fails

opt.OnFailure(string)    // a string to output as the failure
                         // report if the test fails

func BeNil ΒΆ added in v0.9.0

func BeNil() nilness.Matcher

BeNil returns a matcher that checks if the value is nil.

The returned matcher is an `AnyMatcher` that may only be used with the `Should()` method, or with `To()` where the subject is of formal type any.

If used in a To() or Should() test with a subject that is not nilable, the test fails with a message similar to:

test.BeNil: values of type '<type>' are not nilable

If used with ToNo() or ShouldNot() a non-nilable subject will pass the test.

Supported Options ΒΆ

opt.QuotedStrings(bool)     // determines whether any non-nil string
                            // values are quoted in any test failure
                            // report.  The default is false (string
                            // values are quoted).
                            //
                            // If the value is not a string type this
                            // option has no effect.

opt.FailureReport(func)     // a function that returns a custom test
                            // failure report if the test fails.

opt.OnFailure(string)       // a string to output as the failure
                            // report if the test fails.

func BeOfType ΒΆ added in v0.12.0

func BeOfType[T any]() typecheck.Matcher[T]

BeOfType returns a matcher that checks if the value is of the expected type.

If a test wishes to perform further tests on the value that rely on having a value of the expected type, consider using the ExpectType or RequireType functions instead, which will return the value as the expected type, or fail the test immediately.

func BeTrue ΒΆ added in v0.8.0

func BeTrue() bools.BooleanMatcher

BeTrue returns a matcher that will fail if the matched value is not true.

Supported Options ΒΆ

opt.FailureReport(...)   // a function returning a custom failure report
                         // in the event that the test fails
Example ΒΆ
test.Example()

Expect(false).To(BeTrue())
Output:

expected true, got false

func Case ΒΆ added in v0.10.0

func Case[T any](name string, tc T) testcase.Registration[T]

Case adds a test case to the runner. The name is used to identify the test case in the test output and any Before/Each scaffolding functions. The tc provides the data for the test case.

If the name is an empty or whitespace string, a name will be determined as follows:

  • if the test case data is a struct (or pointer to a struct) and has a string field named "Scenario", "scenario", "Name", or "name", with a non-empty or whitespace value, that will be used as the name. Otherwise a default name is derived in the format "testcase-NNN" where NNN is the 1-based index of the test case in the list of test cases.

If the test case data has a bool debug/Debug field that is set true, the test case will be marked as a debug test case.

If the test case data has a bool skip/Skip field that is set true, the test case will be marked as a skip test case.

func Cases ΒΆ added in v0.10.0

func Cases[T any](cases []T) testcase.Registration[T]

Cases creates a Runner to run a set of test cases.

func Cleanup ΒΆ added in v0.8.0

func Cleanup(fn func())

Cleanup adds a function to the list of functions to be called when the test finishes. The function will be called in reverse order of registration.

If a nil function is passed, it is ignored.

This is a wrapper around the *testing.T.Cleanup() method with the addition of accepting (but ignoring) a nil function.

func ContainItem ΒΆ added in v0.8.0

func ContainItem[T any](item T) slices.ContainsItemMatcher[T]

ContainItem returns a matcher for slices of comparable types that is satisfied if the slice contains at least one item that is equal to the expected item.

The Matcher supports the following options:

  • func(T, T) bool
  • func(any, any) bool

The options allow a test to supply a custom comparison function to be used to compare the expected item with the items in the slice. The comparison function must return true if the two arguments are equal.

If both types of functions are provided the matcher will panic with ErrInvalidOption.

The default comparison function is reflect.DeepEqual.

Example ΒΆ
test.Example()

sut := []string{"a", "b"}

// these tests will pass
Expect(sut).To(ContainItem("a"))
Expect(sut).To(ContainItem("A"), opt.CaseSensitive(false))

// this test will fail
Expect(sut).To(ContainItem("c"))
Output:

expected: []string containing: "c"
got:
| "a"
| "b"

func ContainItems ΒΆ added in v0.8.0

func ContainItems[T any](items []T) slices.ContainsItemsMatcher[T]

ContainItems returns a matcher for slices of comparable types that is satisfied if the slice contains all of the items in the expected slice. The items in the expected slice may occur in any order in the got slice and need not be contiguous.

The the following options are supported:

  • func(T, T) bool
  • func(any, any) bool

The options allow a test to supply a custom comparison function to be used to compare the expected item with the items in the slice. The comparison function must return true if the two arguments are equal.

If both types of functions are provided the matcher will panic with ErrInvalidOption.

The default comparison function is reflect.DeepEqual.

func ContainMap ΒΆ added in v0.8.0

func ContainMap[K comparable, V any](want map[K]V) maps.ContainsMatcher[K, V]

ContainMap returns a matcher that will match if a map contains an expected compatible map. The matcher will pass if the map contains all of the key-value pairs in the expected map.

Values are compared as follows, in order of preference:

  1. V.Equal(V) when V implements the Equal method
  2. a comparison function option of the form func(V, V) bool
  3. a comparison function option of the form func(any, any) bool
  4. reflect.DeepEqual

Supported Options ΒΆ

func(V, V) bool             // a custom comparison function to compare
                            // map values.

func(any, any) bool         // a custom comparison function to compare
                            // map values.

opt.CaseSensitive(bool)     // used to indicate whether the comparison of
                            // string values should be case-sensitive
                            // (default is true).
                            //
                            // This option is also applied to values that
                            // are slices or arrays of a string type.
                            //
                            // The option does NOT apply to string fields
                            // in structs or values that are maps.
                            //
                            // NOTE: string keys are ALWAYS case-sensitive.

opt.ExactOrder(bool)        // used to indicate whether the order of
                            // elements in values that are slices or arrays
                            // is significant (default is true).
                            //
                            // NOTE: order is never significant for map keys.

opt.QuotedStrings(bool)     // determines whether string keys or values are
                            // quoted in any test failure report.  Default
                            // is true.
                            //
                            // This option has no effect for keys or values
                            // that are not strings.

opt.FailureReport(func)     // a function that returns a custom test
                            // failure report if the test fails.

opt.OnFailure(string)       // a string to output as the failure
                            // report if the test fails.

func ContainMapEntry ΒΆ added in v0.8.0

func ContainMapEntry[K comparable, V any](key K, value V) maps.ContainsMatcher[K, V]

ContainMapEntry is a convenience function to test that a map contains a specific key-value pair.

i.e. the following are equivalent:

Expect(someMap).To(ContainMapEntry(key, value))
Expect(someMap).To(ContainMap(map[K]V{key: value}))

Values are compared as follows, in order of preference:

  1. V.Equal(V) when V implements the Equal method
  2. a comparison function option of the form func(V, V) bool
  3. a comparison function option of the form func(any, any) bool
  4. reflect.DeepEqual

Supported Options ΒΆ

// supported options are the same as for ContainMap()

func ContainSlice ΒΆ added in v0.8.0

func ContainSlice[T comparable](e []T) slices.ContainsSliceMatcher[T]

ContainSlice returns a matcher for a slice that is satisfied if the slice contains items that correspond to the items in a given other slice.

Comparison of items is performed using reflect.DeepEqual by default but may be overridden by supplying one of:

func(T, T) bool
func(any, any) bool

func ContainString ΒΆ added in v0.8.0

func ContainString(expected string) strings.ContainsMatch

func Debug ΒΆ added in v0.10.0

func Debug[T any](name string, tc T) testcase.Registration[T]

Debug adds a test case to the runner and marks it as a debug target.

When running test cases, if any cases are marked as debug only those cases will be run. This is useful for debugging specific test cases without running the entire suite.

Adding a test case using Debug() overrides any debug/skip fields that may be present in the test case data itself.

When any debug test cases are run, the test case runner itself will fail the test with a warning that only debug cases were evaluated. This is intended to ensure that Debug() cases are not accidentally left in the test suite.

func DeepEqual ΒΆ added in v0.3.0

func DeepEqual[T any](want T) equal.DeepMatcher[T]

DeepEqual returns a matcher that checks if a value of type T is equal to some expected value of type T.

Equality is always evaluated using reflect.DeepEqual. i.e. the matcher does not support the use of a custom comparison function and will not use any Equal(T) method implemented by type T.

To use a custom comparison function or a T.Equal(T) method, use the Equal() matcher.

Supported Options ΒΆ

opt.QuotedString(bool)     // determines whether string values in failure reports
                           // are quoted (default is true); the option has no
                           // effect on values that are not strings
                           //
                           // this option applies only to values being directly
                           // compared; it is not applied to string fields of
                           // struct types, for example

opt.FailureReport(func)    // a function returning a custom failure report
                           // when the values are not equal
Example ΒΆ
test.Example()

Expect([]byte{1, 2, 3}).To(DeepEqual([]byte{1, 2, 4}))
Expect([]uint8{1, 1, 2, 3, 5}).To(DeepEqual([]uint8{1, 2, 4, 8, 16}))

// this will not compile because the types are not the same:
// Expect([]uint8{1, 1, 2, 3, 5}).To(DeepEqual([]int{1,1,2,3,5}))
Output:

expected [1 2 4], got [1 2 3]

expected: [1 2 4 8 16]
got     : [1 1 2 3 5]

func Equal ΒΆ

func Equal[T comparable](want T) equal.Matcher[T]

Equal returns a matcher that checks if a value of type T is equal to some expected value of type T. The type T must be comparable.

Equality is determined using ONE of the following, in order of preference:

  1. any comparison function provided as an option
  2. a T.Equal(T) bool method, if it exists
  3. the == operator

If specified, a custom comparison function must take two arguments of type T, returning a boolean indicating whether the values are considered equal.

Supported Options ΒΆ

func(T, T) bool            // a function to compare the values
                           // (overriding the use of the == operator or
                           // T.Equal(T) method, if it exists)

opt.QuotedString(bool)     // determines whether string values in failure reports
                           // are quoted (default is true); the option has no
                           // effect on values that are not strings
                           //
                           // this option applies only to values being directly
                           // compared; it is not applied to string fields of
                           // struct types, for example

opt.FailureReport(func)    // a function returning a custom failure report
                           // when the values are not equal
Example ΒΆ
test.Example()

Expect(1).To(Equal(2))
Expect("the hobbit").To(Equal("the lord of the rings"))

// this will not compile because the types are not the same:
// Expect(42).To(Equal("the answer"))

// this will not compile because the types are not comparable:
// Expect([]int{1, 2, 3}).To(Equal([]int{1, 2, 3}))
//
// instead, use:
//   Expect(..).To(DeepEqual(..))
//
// or, for slices:
//   Expect(..).To(EqualSlice(..))
Output:

expected 2, got 1

expected: "the lord of the rings"
got     : "the hobbit"

func EqualBytes ΒΆ added in v0.8.0

func EqualBytes[T ~byte](want []T) *bytes.EqualMatcher[T]

EqualBytes returns a matcher that checks if a byte slice is equal to an expected byte slice.

The type T must be byte or a type that is assignable to byte. The matcher uses reflect.DeepEqual to compare the slices; this enables the matcher to be used with slices of custom byte types.

The matcher reports differences in human-readable format.

The failure report identifies the offsets of all differences and shows a portion of the expected and actual byte slices to highlight the first such difference. See the example for a demonstration.

Supported Options ΒΆ

This is a highly specialised matcher; the only supported option is a custom failure report function:

opt.FailureReport(func)  // a function returning a custom failure report
                         // in the event that the slices are not equal
Example ΒΆ
test.Example()

a := []byte{0x01, 0x02, 0x03}
b := []byte{0x01, 0x03, 0x02}

Expect(a).To(EqualBytes(b))
Output:

bytes not equal:
  differences at: [1, 2]
expected: 01 03 02
        |    ** **
got     : 01 02 03

func EqualMap ΒΆ added in v0.8.0

func EqualMap[K comparable, V any](want map[K]V) maps.EqualMatcher[K, V]

EqualMap returns a matcher that will match if the wanted map is equal to the actual map.

To be equal, the maps must have the same number of keys and each key must have a value that is equal to the value of the same key in both maps.

The order of the keys in the maps is not significant.

Values are compared as follows, in order of preference:

  1. V.Equal(V) when V implements the Equal method
  2. a comparison function option of the form func(V, V) bool
  3. a comparison function option of the form func(any, any) bool
  4. reflect.DeepEqual

This test is a convenience for separately testing length and content of two maps. i.e. the following are equivalent:

Expect(got).To(EqualMap(want))

and

Expect(len(got)).To(Equal(len(want)))
Expect(got).To(ContainMap(want))

However, although the above tests are equivalent in terms of test outcome, the test failure report for the EqualMap() test is more informative than the two tests combined, expressing the intent that the maps be equal.

Supported Options ΒΆ

// supported options are the same as for ContainMap()
Example ΒΆ
test.Example()

sut := map[string]int{
	"ford":   42,
	"arthur": 23,
}

// this test will pass
Expect(sut).To(EqualMap(map[string]int{
	"ford":   42,
	"arthur": 23,
}))

// this test will fail
sut = map[string]int{"marvin": 99}
Expect(sut).To(EqualMap(map[string]int{"trillian": 24}))
Output:

expected map:
  "trillian" => 24
got:
  "marvin" => 99

func EqualSlice ΒΆ added in v0.8.0

func EqualSlice[T comparable](e []T) slices.EqualMatcher[T]

EqualSlice compares the actual slice with an expected slice and fails the test if they are not equal.

By default, the order of elements in each slice is significant. That is, the nth each slice must be equal. If the order of elements is not significant, use the ExactOrder option to specify that the order of elements is not significant, e.g.:

got := []int{1, 2, 3}
expected := []int{3, 2, 1}
Expect(got).To(EqualSlice(expected))                         // will fail
Expect(got).To(EqualSlice(expected), opt.ExactOrder(false))  // will pass

func Error ΒΆ added in v0.3.0

func Error(msg string)

Error explicitly and unconditionally fails the current test with the given message.

This should not be confused with the `test.Error` function used to report an error condition in a test helper (from the blugnu/test/test package).

func Errorf ΒΆ added in v0.10.0

func Errorf(s string, args ...any)

Errorf explicitly and unconditionally fails the current test with the formatted message.

This should not be confused with the `test.Error` function used to report an error condition in a test helper (from the blugnu/test/test package).

func Expect ΒΆ added in v0.8.0

func Expect[T any](value T, opts ...any) *expectation[T]

Expect creates an expectation for the given value. The value may be of any type.

Supported Options ΒΆ

string    // a name for the expectation; the name is used in
          // the failure message if the expectation fails.

func ExpectFalse ΒΆ added in v0.8.0

func ExpectFalse[T ~bool](got T, opts ...any)

ExpectFalse fails a test if a specified bool is not false. An optional name (string) may be specified to be included in the test report in the event of failure.

This test is a convenience for these equivalent alternatives:

Expect(got).To(Equal(false))
Expect(got).To(BeFalse())

Supported Options ΒΆ

string                   // a name for the value, for use in any test
                         // failure report

opt.FailureReport(func)  // a function returning a custom failure report
                         // in the event that the test fails
Example ΒΆ
test.Example()

ExpectFalse(true)
Output:

expected false, got true

func ExpectTrue ΒΆ added in v0.8.0

func ExpectTrue[T ~bool](got T, opts ...any)

ExpectTrue fails a test if a specified bool is not true. An optional name (string) may be specified to be included in the test report in the event of failure.

This test is a convenience for these equivalent alternatives:

Expect(got).To(Equal(true))
Expect(got).To(BeTrue())

Supported Options ΒΆ

string                   // a name for the value, for use in any test
                         // failure report

opt.FailureReport(func)  // a function returning a custom failure report
                         // in the event that the test fails
Example ΒΆ
test.Example()

ExpectTrue(false)
Output:

expected true, got false

func ExpectType ΒΆ added in v0.8.0

func ExpectType[T any](got any, opts ...any) (T, bool)

ExpectType tests that a value is of an expected type. If the test passes, the value is returned as that type, with true. If the test fails the zero value of the specified type is returned, with false.

If the value being of the expected type is essential to the test, consider using the RequireType function instead, which will return the value or fail the test immediately, avoiding the need to check the ok value.

If a test does not use the returned value, consider using the BeOfType matcher instead, to avoid lint warnings about unused return values.

Example ΒΆ
test.Example()

// ExpectType returns the value as the expected type and true if the
// value is of that type
var got any = 1 / 2.0
result, ok := ExpectType[float64](got)

fmt.Printf("ok is %v\n", ok)
fmt.Printf("result: type is: %T\n", result)
fmt.Printf("result: value is: %v\n", result)

// ExpectType returns the zero value of the expected type and false if the
// value is not of that type (the return values can be ignored if the
// test is only concerned with checking the type)
got = "1 / 2.0"
ExpectType[float64](got)
Output:

ok is true
result: type is: float64
result: value is: 0.5

expected type: float64
got          : string

func ExpectationsWereMet ΒΆ added in v0.4.0

func ExpectationsWereMet(m Mock, opts ...any)

ExpectationsWereMet is a convenience function for testing and resetting expectations of a mock that implements the test.Mock interface:

type Mock interface {
	ExpectationsWereMet() error
	Reset()
}

The mock is guaranteed to be reset whether expecations were met or not.

If a test is not concerned with expectations being met and is using a mock simply to provide complex mocked responses, the mock can be reset without checking expectations by passing the mock to the Reset() helper.

Supported Options ΒΆ

string    // a name for the expectation; the name is used in
          // the failure message if the expectation fails.

// # Example

func TestSomething(t *testing.T) {
	With(t)

	// ARRANGE
	mock1 := NewMock()
	mock2 := NewMock()
	mock3 := NewMock()
	defer Reset(mock1, mock2, mock3)

	// .. configure expectations on mocks

	// ACT
	// .. call the code under test

	// ASSERT
	// .. additional assertions prior to the deferred testing
	test.ExpectationsWereMet(mock1, "mock 1")
	test.ExpectationsWereMet(mock2, "mock 2")
	test.ExpectationsWereMet(mock3, "mock 3")
}

func FlakyTest ΒΆ added in v0.11.0

func FlakyTest(name string, fn func(), opts ...FlakyOption) flakyRunner

FlakyTest creates a runner for a test that may fail intermittently. The test will be retried up to a specified number of MaxAttempts or until MaxDuration has passed (whichever occurs first).

If the test passes, no failure report is produced; reports from any failed attempts are discarded.

If the test does not pass on any attempt, a report is generated that includes the details of all failed attempts.

By default, the test will be attempted up to 3 times for up to 1 second.

You can configure the maximum number of attempts and the maximum duration using the MaxAttempts and MaxDuration options, respectively. Setting these options to 0 (zero) disables each limit. Setting both options to 0 (zero) allows the test to run indefinitely until it passes, the 'go test' timeout is reached, or the process is terminated.

Example usage:

// run a test for a maximum of 5 attempts over a 200 millisecond period
Run(FlakyTest("test that fails intermittently", func() {
	// .. test code ..
}, MaxDuration(200*time.Millisecond), MaxAttempts(5)))

func HaveContextKey ΒΆ added in v0.8.0

func HaveContextKey[K comparable](k K) *contexts.KeyMatcher[K]

HaveContextKey returns a matcher that checks if a context contains a specific key. The key type must be comparable. The matcher will fail if the key is not present in the context.

Supported Options ΒΆ

opt.QuotedStrings(bool)    // determines whether string values are quoted in test
                           // failure report (quoted by default); the option has
                           // has no effect if the key is not a string type

opt.FailureReport(func)    // a function returning a custom failure report
                           // in the event that the test fails
Example ΒΆ
test.Example()

type key int
ctx := context.WithValue(context.Background(), key(57), "varieties")

// these tests will pass
Expect(ctx).To(HaveContextKey(key(57)))
Expect(ctx).ToNot(HaveContextKey(key(58)))

// this test will fail
Expect(ctx).To(HaveContextKey(key(58)))
Output:

expected key: test.key(58)
  key not present in context

func HaveContextValue ΒΆ added in v0.8.0

func HaveContextValue[K comparable, V any](k K, v V) *contexts.ValueMatcher[K, V]

HaveContextValue returns a matcher that checks if a context contains a specific key-value pair.

The key type (K) must be comparable. The matcher will fail if the key is not present in the context or if the value does not match the expected value. The value type (V) can be any type.

The matcher uses reflect.DeepEqual to compare the expected value with any value in the context for the specified key; this may be overridden by supplying a custom comparison function in the options.

Supported Options ΒΆ

func(V, V) bool            // a custom comparison function to compare values
                           // (overriding the use of reflect.DeepEqual)

opt.QuotedStrings(bool)    // determines whether string keys or values are quoted
                           // in the test failure report (quoted by default);
                           // the option has no effect for keys or values that
                           // are not string type

opt.FailureReport(func)    // a function returning a custom failure report
                           // in the event that the test fails
Example ΒΆ
// this is needed to make the example work; this would be usually
// be `With(t)` where `t` is the *testing.T
test.Example()

type key int
ctx := context.WithValue(context.Background(), key(57), "varieties")

// these tests will pass
Expect(ctx).To(HaveContextValue(key(57), "varieties"))
Expect(ctx).ToNot(HaveContextValue(key(56), "varieties"))
Expect(ctx).ToNot(HaveContextValue(key(57), "flavours"))

// this test will fail
Expect(ctx).To(HaveContextValue(key(57), "flavours"))
Output:

context value: test.key(57)
  expected: "flavours"
  got     : "varieties"

func HaveLen ΒΆ added in v0.10.0

func HaveLen(n int) *length.Matcher

HaveLen returns a matcher that checks if the value has len() equal to n.

The returned matcher is an `AnyMatcher` that may only be used with values of a type that is compatible with the built-in len() function. That is:

- string - slice - array - channel - map

A nil value of any of these types is considered to have a length of 0.

If the value is of any other type, the test fails as an invalid test, with a message similar to:

length.Matcher: requires a value that is a string, slice, channel, or map: got <type>

func HelperTests ΒΆ added in v0.10.0

func HelperTests(scns ...HelperScenario) helpertestRunner

func IsParallel ΒΆ added in v0.8.0

func IsParallel() bool

IsParallel returns true if the current test is running in parallel or is a sub-test of a parallel test.

func KeysOfMap ΒΆ added in v0.8.0

func KeysOfMap[K comparable, V any](m map[K]V) []K

KeysOfMap returns the keys of a map as a slice, provided to enable map keys to be tests using slice matchers, written expressively as:

Expect(KeysOfMap(someMap)).To(ContainItem(expectedKey))

The order of the keys in the returned slice is not guaranteed.

func MatchRegEx ΒΆ added in v0.8.0

func MatchRegEx(regex string) strings.RegExMatch

func MeetExpectations ΒΆ added in v0.10.0

func MeetExpectations() *mocks.Matcher

MeetExpectations is a matcher that checks whether the expectations of a mock were met. It is used in conjunction with the Expect() function to assert

func NilPanic ΒΆ added in v0.8.0

func NilPanic() panics.Expected

NilPanic returns an expectation that a panic will occur that recovers a *runtime.PanicNilError.

Panic(nil) is syntactic sugar for "no panic expected", to simplify table-drive tests where each test case may or may not expect a panic, enabling the use of a single Expect() call.

i.e. instead of writing:

if testcase.expectedPanic == nil {
	defer Expect(Panic()).DidNotOccur()
} else {
	defer Expect(Panic(testcase.expectedPanic)).DidOccur()
}

you can write:

defer Expect(Panic(testcase.expectedPanic)).DidOccur()

When testcase.expectedPanic is nil, this is equivalent to:

defer Expect(Panic()).DidNotOccur()

Without having to write conditional code to handle the different expectations.

Testing for a nil panic ΒΆ

In the unlikely event that you specifically need to test for a panic(nil), you can use the NilPanic() function, which will create an expectation for a panic that recovers a *runtime.PanicNilError.

see: https://go.dev/blog/compat#expanded-godebug-support-in-go-121

func Original ΒΆ added in v0.9.0

func Original[T any](v *T) restorable[T]

Original is used to create an override for a variable of type T.

The argument v is a pointer to the variable that will be overridden. The function returns a value providing a ReplacedBy method, which can be used to replace the original value with a new one.

func Panic ΒΆ added in v0.2.0

func Panic(r ...any) panics.Expected

Panic returns an expectation subject that can be used to test whether a panic has occurred, optionally identifying a value that should match the value recovered from the expected panic.

NOTE: At most ONE panic test should be expected per function. In addition, extreme care should be exercised when combining panic tests with other deferred recover() calls as these will also interfere with a panic test (or vice versa).

Usage ΒΆ

  • If called with no arguments, any panic will satisfy the expectation, regardless of the value recovered.

  • If called with a single argument, it will expect to recover a panic that recovers that value (unless the argument is nil; see The Panic(nil) Special Case, below)

  • If called with > 1 argument, the test will be failed as invalid.

The Panic(nil) Special Case ΒΆ

Panic(nil) is a special case that is equivalent to "no panic expected". This is motivated by table-driven tests to avoid having to write conditional code to handle test cases where a panic is expected vs those where not.

Treating Panic(nil) as "no panic expected" allows you to write:

defer Expect(Panic(testcase.expectedPanic)).DidOccur()

When testcase.expectedPanic is nil, this is equivalent to:

defer Expect(Panic()).DidNotOccur()

Should you need to test for an actual panic(nil), use:

defer Expect(NilPanic()).DidOccur()

Or, in a table-driven test, specify an expected recovery value of &runtime.PanicNilError{}.

Example ΒΆ
test.Example()

// a stack trace is included by default, but is disabled for this
// example to avoid breaking the example output
defer Expect(Panic("some string")).DidOccur(opt.NoStackTrace())

panic("some other string")
Output:

unexpected panic:
  expected : string("some string")
  recovered: string("some other string")

func Parallel ΒΆ added in v0.8.0

func Parallel(t TestingT)

Parallel establishes a new test frame scheduled for parallel execution. It is intended to be used as an alternative to With(t) for a test that is intended to run entirely in parallel.

i.e. use:

func TestSomething(t *testing.T) {
  Parallel(t)
  // ... test code here ...
}

instead of:

func TestSomething(t *testing.T) {
  With(t)

  Run(ParallelTest("something", func() {
     // ... test code here ...
  }))
}

Parallel must not be called from a test that is already parallel or with a nil argument; in both cases the test will be failed as invalid.

func ParallelCase ΒΆ added in v0.10.0

func ParallelCase[T any](name string, tc T) testcase.Registration[T]

ParallelCase adds a test case to the runner for parallel execution.

Aside from the parallel execution of the test case subtests, this function is otherwise identical to Case().

func ParallelCases ΒΆ added in v0.10.0

func ParallelCases[T any](exec TestExecutor[T], cases ...testcase.Registration[T]) testcase.Runner[T]

ParallelCases creates a Runner to run a set of test cases in parallel.

Aside from the parallel execution of the test cases, this function is otherwise identical to Testcases().

func ParallelTest ΒΆ added in v0.10.0

func ParallelTest(name string, fn func()) testRunner

ParallelTest creates a test runner to run a function as a subtest with the provided name, running it in parallel.

If the current test is already parallel, this function will fail the test as invalid since it is not allowed to nest parallel tests.

func Record ΒΆ added in v0.8.0

func Record(fn func()) ([]string, []string)

Record captures the stdout and stderr output resulting from the execution of some function.

In the unlikely event that the mechanism fails, the function will panic to avoid returning misleading results or require error handling.

Example ΒΆ
// remove date and time from log output otherwise this
// example will fail because the date and time will be
// different each time it is run!
log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime))

// capture the output of a function that explicitly writes
// to stdout, stderr and emits logs (which also go to stderr,
// by default)
stdout, stderr := Record(func() {
	fmt.Println("to stdout")
	fmt.Fprintln(os.Stderr, "to stderr")
	log.Println("to log")
})

// write what was captured to stdout (for the Example to test)
// (in a test, you would use Expect() to test the output)

fmt.Println("captured stdout:")
for i, s := range stdout {
	fmt.Printf("  %d: %s\n", i+1, s)
}

fmt.Println("captured stderr:")
for i, s := range stderr {
	fmt.Printf("  %d: %s\n", i+1, s)
}
Output:

captured stdout:
  1: to stdout
captured stderr:
  1: to stderr
  2: to log

func Require ΒΆ added in v0.9.0

func Require[T any](value T, opts ...any) *expectation[T]

Require creates an expectation for the given value which is required to pass. If the expectation is not met, execution continues with the *next* test (if any); no further expectations in the current test will be evaluated.

This is a convenience function that is equivalent to passing the opt.Required() or opt.IsRequired(true) option to a matcher invoked using Expect(), i.e. the following are equivalent:

Expect(value).To(Equal(expected), opt.IsRequired(true))
Expect(value).To(Equal(expected), opt.Required())
Require(value).To(Equal(expected))

Supported Options ΒΆ

string    // a name for the expectation; the name is used in
          // the failure message if the expectation fails.
Example ΒΆ
test.Example()

// this test will fail
Require(true).To(Equal(false))

// this will not be executed because the previous expectation was
// required to pass and did not
Expect("apples").To(Equal("oranges"))
Output:

expected false, got true

func RequireType ΒΆ added in v0.9.1

func RequireType[T any](got any, opts ...any) T

RequireType tests that a value is of an expected type. If the test passes, the value is returned as that type otherwise the test fails immediately without evaluating any further expectations.

If a test does not use the returned value, consider using the BeOfType matcher instead, to avoid lint warnings about unused return values.

Example ΒΆ
test.Example()

// RequireType returns the value as the expected type when it
// is of that type
var got any = 1 / 2.0
result := RequireType[float64](got)

fmt.Printf("result: type is: %T\n", result)
fmt.Printf("result: value is: %v\n", result)

// RequireType terminates the current test if the value is not
// of the required type
got = "1 / 2.0"
RequireType[float64](got)
Expect(false, "this will not be evaluated").To(BeTrue())
Output:

result: type is: float64
result: value is: 0.5

expected type: float64
got          : string

func Reset ΒΆ added in v0.4.0

func Reset(r ...Resetter)

Reset calls the Reset method on each Resetter in the list. A Resetter is any type that implements the Resetter interface:

type Resetter interface {
	Reset()
}

Reset is a convenience function for resetting multiple Resetters, such as fakes and mocks.

Example ΒΆ

func TestSomething(t *testing.T) {
	// ARRANGE
	fake := NewFake()
	mock := NewMock()
	defer test.Reset(fake, mock)

	// .. set expectations on mock

	// ACT
	// .. call the code under test

	// ASSERT
	// .. assertions additional to testing mock expectations (if any)
	test.MockExpectations(t, mock)
}

func Restore ΒΆ added in v0.9.0

func Restore[T any](fn func(restorable[T]))

Restore is used to restore the original value of a variable after it has been temporarily changed for a test using the Original function and its ReplacedBy method.

It should be called in a defer statement to ensure that the original value is restored even if the test panics or fails.

Example ΒΆ

ExampleRestore demonstrates how to use Restore to temporarily replace a variable with a new value for the duration of a test

The example simulates a naive approach to mocking the time.Now function and demonstrates that Restore can be used to temporarily replace variables in a test, including variables that are function references.

To ensure predictable output for the example, instead of using the real time.Now function, we define a variable `now` that returns a fixed time value. This allows us to control the output of the example without relying on the current time, which would vary depending on when the example is run.

This is not a robust mechanism for mocking time.Now, but it serves as an illustration. For a more robust approach to mocking time, consider using a package like github.com/blugnu/time, or similar.

// establish "now" as a function that returns the release date of Go 1.0
// as the "current time".
var now = func() time.Time {
	return time.Date(2012, 3, 28, 0, 0, 0, 0, time.UTC)
}

// simulate a sub-test where the "current time" is temporarily
// replaced with a fixed time value.
func() {
	var fakeTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
	defer Restore(Original(&now).ReplacedBy(func() time.Time { return fakeTime }))

	fmt.Println("now() returns: ", now())
}()

fmt.Println("now() returns: ", now())
Output:

now() returns:  2020-01-01 00:00:00 +0000 UTC
now() returns:  2012-03-28 00:00:00 +0000 UTC

func Run ΒΆ added in v0.8.0

func Run(r Runnable)

func Skip ΒΆ added in v0.10.0

func Skip[T any](name string, tc T) testcase.Registration[T]

Skip adds a test case to the runner and marks it to be skipped.

Adding a test case using Skip() overrides any skip/debug fields that may be present in the test case data itself.

func SkipNow ΒΆ added in v0.12.0

func SkipNow(reason string)

SkipNow is a function that is used to skip the current test immediately. It is a wrapper around the SkipNow method of the current test frame.

If the current testframe is a *testing.T (the usual case), the supplied reason for skipping the test is logged. Otherwise, the reason is required for documentation purposes only.

func StopWatch ΒΆ added in v0.10.0

func StopWatch(f func()) time.Duration

StopWatch executes the provided function and returns the duration it took to execute.

func Test ΒΆ added in v0.8.0

func Test(name string, fn func()) testRunner

Test creates a named test runner that can be used to run a test function as a subtest in the current test frame. The name is used as the subtest name, and the function is the test code to be executed.

func Testcases ΒΆ added in v0.10.0

func Testcases[T any](exec TestExecutor[T], cases ...testcase.Registration[T]) testcase.Runner[T]

Testcases creates a Runner that can be used to run a set of test cases; each test case is performed in its own subtest using a test executor function that is provided as the first argument.

Test Executors ΒΆ

For tests where the test executor is consistent for all test cases, the ForEach() function provides a single test executor function that requires only the test case itself.

If test execution varies for some test cases, the For() function instead provides a test executor that will be called with the name of each test case in addition to the test case itself. The test executor function can then use the test case name to perform any test case specifica variations as required.

Test Cases ΒΆ

Test cases may be specified in two different ways:

  • by calling the Case(), ParallelCase, Debug() or Skip() fluent methods on the returned Runner; these functions require a name for each case;

  • as a variadic list of test cases following the test executor; these test cases are expected to be simple values or structs. If a struct type is used that has a name/Name/scenario/Scenario string field, that field will be used as the name for the test case. Otherwise a default name will be generated in the format "testcase-NNN" where NNN is the 1-based index of the test case in the list.

The first form is recommended for cases where the name/scenario of each test case is significant, whether for varying test execution or simply in identifying a failed test case.

The second form is useful for simple test cases where the test executor is the same for all test cases and the index-based identity for each test case is sufficient.

func ValuesOfMap ΒΆ added in v0.8.0

func ValuesOfMap[K comparable, V any](m map[K]V) []V

ValuesOfMap returns the values of a map as a slice, provided to enable map values to be tests using slice matchers, written expressively as:

Expect(ValuesOfMap(someMap)).To(ContainItem(expectedValue))

The order of the values in the returned slice is not guaranteed.

func With ΒΆ added in v0.8.0

func With(t TestingT)

With pushes the given TestingT onto the test frame stack; if the TestingT is not nil it will be popped from the stack when the test has completed.

This is used to set the current test frame for the test package, typically called as the first line of a Test...() function:

func TestSomething(t *testing.T) {
    With(t)

    // ... rest of the test code ...
}

If `blugnu/test` functions are used to run subtests etc, no further calls to With() are required in a test function; the test frame will be automatically managed by the test package.

If a new test frame is explicitly created, e.g. by calling t.Run(string, func(t *testing.T)), then With(t) must be called to push the new test frame onto the stack. Again, this will be automatically popped from the stack when the new test frame completes:

func TestSomething(t *testing.T) {
    With(t)

    // when using test package functions to run subtests you do not
    // need to call With() again

    Run(Test("subtest", func() {
        // ... rest of the subtest code ...
    })

    // but With() must be called if a new test frame is explicitly created

    t.Run("subtest", func(t *testing.T) {
        With(t)
        // ... rest of the subtest code ...
    })

    // ... rest of the test code ...
}

To simultaneously push a test frame and mark it for parallel execution, you can use the Parallel() function:

func TestSomething(t *testing.T) {
    Parallel(t)

    // ... rest of the test code ...
}

Types ΒΆ

type AnyMatcher ΒΆ added in v0.10.0

type AnyMatcher interface {
	Match(got any, opts ...any) bool
}

AnyMatcher is the interface implemented by matchers that can test any type of value. It is used to apply matchers to expectations that are not type-specific.

It is preferable to use the Matcher[T] interface for type-safe expectations; AnyMatcher is provided for situations where the compatible types for a matcher cannot be enforced at compile-time.

When implementing an AnyMatcher, it is important to ensure that the matcher fails a test if it is not used correctly, i.e. if the matcher is not compatible with the type of the value being tested.

An AnyMatcher must be used with the Expect().Should() matching function; they may also be used with Expect(got).To() where the got value is of type `any`, though this is not recommended.

type FakeResult ΒΆ added in v0.8.0

type FakeResult[R any] struct {
	Result R
	Err    error
}

FakeResult[R] is a generic type that can be used to help fake a function returning some result type R and/or an error. It can be useful for creating simple fakes for functions or interface methods.

The type does not provide any mechanism for providing an implementation of a function, but facilitates a consistent pattern for simple fakes and minimises the amount of boilerplate code required to create them.

Limitations ΒΆ

No mechanism is provided for configuring expected calls, capturing or testing for expected arguments, or returning different values for multiple calls. FakeResult[R] is for simple cases where a fake function or method consistently returns a specific result.

For cases requiring more advanced capabilities, consider using test.MockFn[A, R].

Example: Mocking an interface with a single method ΒΆ

type MyInterface interface {
	MyMethod() (int, error)
}

type fakeMyMethodInterface struct {
	FakeResult[int]
}

func (fake *MyFake) MyMethod() (int, error) {
	return fake.Result, fake.Err
}

// FakeResult[R] implements Reset(); embedding promotes the Reset method
// to the fakeMyMethodInterface struct itself.

If mocking an interface with multiple methods, the fakeMyMethodInterface struct would implement an explicit Reset() method to reset all FakeResult[R] fields individually.

Returning Multiple Values ΒΆ

When faking an interface method that returns multiple result values (in addition to an error), use a struct type with fields for each of the result values.

type MyInterface interface {
	MyMethod() (int, string, error)
}

type fakeMyMethod struct {
	fakeMyMethodFn FakeResult[struct{name string; age int}]
}

func (fake *fakeMyMethod) MyMethod() (int, string, error) {
	fn := fake.fakeMyMethodFn
	return fn.Result.age, fn.Result.name, fn.Err
}

Return Value(s) with No Error ΒΆ

When faking a function that returns only result values and no error, simply ignore the Err field.

Returning Only an Error ΒΆ

When faking a function that only returns an error, use FakeResult[error]. This provides a FakeResult with a Result type of error in addition to the Err field; the Result field should be ignored.

The use of the error type as the Result type clarifies the intent of the fake and avoids confusiion. Consider:

func (s MyStruct) SomeMethod() error {
	return s.SomeMethodFn.Err
}

It is clear at this point that the SomeMethodFn field is a FakeResult where only the error is relevant, and the Result field is ignored. This is a common pattern when faking methods that return only an error.

Now consider the possibilities when declaring the SomeMethodFn field:

s.SomeMethodFn := FakeResult[any]{} // does this fake a function returning any or is the Result ignored? s.SomeMethodFn := FakeResult[error]{} // this fake is clearly for a function returning (only) an error

The SomeMethodFn field is a FakeResult; since the Result field is ignored, the type parameter R can be any type, but using FakeResult[error] makes it clear that the fake is for a function that returns an error, even though the Result field, to which the type parameter 'error' relates, is ignored.

func (*FakeResult[R]) Reset ΒΆ added in v0.8.0

func (fake *FakeResult[R]) Reset()

Reset resets the fake to its zero value.

func (*FakeResult[R]) Returns ΒΆ added in v0.8.0

func (fake *FakeResult[R]) Returns(v ...any)

Returns sets the result value and/or error to be returned by the fake.

The first R value in the variadic parameter list is used to set the result value, and the first error value is used to set the error. nil values are ignored. A value of any other type will cause the current test to fail as invalid.

R and error values may be specified in any order, but for symmetry it is recommended that they are specified in the order they will be returned.

e.g. when faking a function:

func MyFunc() (int, error)

fakeMyFuncFn := &FakeResult[int]{}

fakeMyFuncFn.Returns(42, nil) // fakes a function returning (42, nil)

If multiple R or error values are provided, the function will fail the current test as invalid.

type FlakyOption ΒΆ added in v0.11.0

type FlakyOption func(*flakyRunner)

FlakyOption is an option function type for an option that modifies the behavior of a FlakyTest

func MaxAttempts ΒΆ added in v0.11.0

func MaxAttempts(n uint) FlakyOption

MaxAttempts sets the maximum number of attempts for a FlakyTest.

If the test does not pass within the specified number of attempts, it will fail with a report detailing all failed attempts.

The default is 3 attempts.

MaxAttempts is ignored if the MaxDuration is reached before the number of attempts reaches the maximum.

Setting MaxAttempts to 0 (zero) will allow the test to run indefinitely until it passes, MaxDuration is reached, or the 'go test' timeout is reached.

func MaxDuration ΒΆ added in v0.11.0

func MaxDuration(d time.Duration) FlakyOption

MaxDuration sets the maximum duration for a FlakyTest.

If the test does not pass within the specified duration, it will fail with a report detailing all failed attempts.

The default is 1 second.

MaxDuration is ignored if MaxAttempts is reached before the duration.

Setting MaxDuration to 0 (zero) will allow the test to run indefinitely until it passes, MaxAttempts is reached, or the 'go test' timeout is reached.

func WaitBetweenAttempts ΒΆ added in v0.11.0

func WaitBetweenAttempts(d time.Duration) FlakyOption

WaitBetweenAttempts sets the duration to wait between attempts for a FlakyTest.

The default is 10ms.

type HelperScenario ΒΆ added in v0.10.0

type HelperScenario struct {
	Scenario string
	Act      func()
	Assert   func(*R)
	Debug    bool
	Skip     bool
}

type Matcher ΒΆ added in v0.8.0

type Matcher[T any] interface {
	Match(got T, opts ...any) bool
}

Matcher[T] is the interface implemented by matchers that can test a value of type T. It is used to apply matchers to expectations that are type-specific and type-safe.

Note that not all type-safe matchers implement a generic interface; a matcher that implements Match(got X, opts ...any) bool, where X is a formal, literal type (i.e. not generic) is also a type-safe matcher.

Matcher[T] describes the general form of a type-safe matcher.

Generic matchers are able to leverage the type system to ensure that the matcher is used correctly with a variety of types, i.e. where the type of the Expect() value satisfies the constraints of the matcher type, T. The equals.Matcher[T comparable] uses this approach, for example, to ensure that the value being tested is comparable with the expected value (since the matcher uses the == operator for equality testing).

type Mock ΒΆ added in v0.4.0

type Mock interface {
	ExpectationsWereMet() error
	Resetter
}

Mock is an interface describing the methods required to be supported by a mock that can be tested using ExpectationsWereMet().

type MockFn ΒΆ added in v0.7.0

type MockFn[A comparable, R any] struct {
	// contains filtered or unexported fields
}

MockFn is a generic type that can be used to mock a function returning some result (type R) and/or an error, with a value (type A) to capture arguments.

MockFn has two modes of operation:

  • Expected Calls: calls are configured using a fluent configuration API starting with an ExpectCall method, optionally configuring any expected arguments and te result and/or error to be returned. In this mode, the mock will fail to meet expectations if arguments recorded with actual calls do not match the arguments for the corresponding expected call.

  • Mapped Results: results for a given set of arguments are configured using the WhenCalledWith method. In this mode, the mock will fail to meet expectations if all configured argument:result combinations are not used.

An implementation of the function being mocked must be provided by the test code to record calls to the mock function and to return the configured result and/or error using either ExpectedResult (expected calls) or ResultFor (mapped results) methods.

The type can be used in a variety of ways to suit the requirements of the mock in a particular test scenario.

TL;DR: Simple Fakes ΒΆ

When the following conditions apply, consider using the simpler test.Fake[R] type:

- the number and order of calls to the mocked function is not significant - the arguments used to call the mocked function are not significant - the return value (and/or error) of the mocked function is consistent across all calls

Mocking Return Values (Fake) ΒΆ

The R type parameter is used to specify the return type of the function being mocked. The WillReturn method can be used to specify the return value that the mock function should return for an expected call.

When mocking a function that returns only an error, specify a result type of any and ignore the Result field.

When mocking a function that returns multiple result values (in addition to an error), use a struct type to specify the return type, with fields for each of the result values.

Testing and Recording Arguments (Spy) ΒΆ

MockFn does not implement the function being mocked; the test code must provide the implementation, which should record each call to the MockFn using RecordCall(), specifying any arguments.

If the method being mocked accepts multiple arguments, they may be captured using a struct type for the A type parameter, with fields for each of the arguments to be captured.

Similarly, when mocking an interface method that returns multiple result values (in addition to an error), a struct type may be used for the R type parameter, with fields for each of the result values.

When mocking a method which accepts no arguments, or if not interested in the arguments, then you may use type any for the A type parameter, or consider using the simpler test.Fake[R] type instead.

Similarly, when mocking function that returns only result values and no error simply ignore the Err field. To fake a method that returns only an error, specify a result type of any and ignore the Result field.

Example ΒΆ

type MyInterface interface {
	MyMethod() (int, error)
	MyMethodWithArgs(id string, opt bool) (int, error)
}

type myMock struct {
	myMethod         test.Fake[int]
	myMethodWithArgs test.MockFn[struct{ID string; Opt bool}, int]
}

func (mock *myMock) MyMethod() (int, error) {
	return mock.myMethod.Result, mock.myMethod.Err
}

func (mock *myMock) MyMethodWithArgs(id string, opt bool) (int, error) {
	return mock.myMethodWithArgs.CalledWith(struct{ID string; Opt bool}{ID: id, Opt: opt})
}

func (*MockFn[A, R]) ExpectCall ΒΆ added in v0.7.0

func (mock *MockFn[A, R]) ExpectCall() *mockFnCall[A, R]

func (*MockFn[A, R]) ExpectationsWereMet ΒΆ added in v0.7.0

func (mock *MockFn[A, R]) ExpectationsWereMet() error

ExpectedResults returns an error if any expectations were not met; otherwise nil.

This method is typically called at the end of a test to ensure that all expected calls were made.

If the mock function is configured for mapped results, this method will return an error if any results were not used.

errors ΒΆ

ErrExpectationsNotMet      // one or more expectations were not met; the error is
                           // joined with errors for each unmet expectation

func (*MockFn[A, R]) RecordCall ΒΆ added in v0.7.0

func (mock *MockFn[A, R]) RecordCall(args ...A) (R, error)

RecordCall is used by a mock implementation to record a call to a mock function, optionally testing that arguments match those expected and returning the result and error configured for the expected call.

returns ΒΆ

Returns mock.expected.Result for an expected call; if there is no expected call the zero value of the R type parameter is returned with ErrUnexpectedCall.

If the arguments do not match the expected call, mock.expected.Result is returned with ErrUnexpectedArgs.

errors ΒΆ

ErrUnexpectedCall       the call has no corresponding expected call
ErrUnexpectedArgs       the arguments do not match the expected call
mock.expected.Err	    the error specified for the expected call (if any)

example ΒΆ

type MyInterface interface {
	MyMethod(int) (int, error)
}

type myMock struct {
	myMethod test.MockFn[int, int]
}

func (mock *myMock) MyMethod(arg int) (int, error) {
	return mock.myMethod.RecordCall(arg)
}

Multiple Return Values ΒΆ

When a mocked function returns multiple values (in addition to an error), the return value will typically be a struct with fields for each of the result values.

The struct fields must be returned as individual return values by the mock implementation:

func (mock *myMock) IntDiv(num, div int) (struct{Result, Remainder int}, error) {
	result, err := mock.intDiv.RecordCall(any)
	return result.Result, result.Remainder, err
}

func (*MockFn[A, R]) Reset ΒΆ added in v0.7.0

func (mock *MockFn[A, R]) Reset()

Reset sets the mock function to its zero value (no errors, no expected or recorded calls and no mapped results).

func (*MockFn[A, R]) ResultFor ΒΆ added in v0.7.0

func (mock *MockFn[A, R]) ResultFor(args A) FakeResult[R]

ResultFor returns the Fake[R] value configured for the specified arguments. This method is called by a mock implementation to return the result and/or error configured for a specific set of arguments.

errors ΒΆ

In the event of an error, the function will panic with one of the following errors:

ErrInvalidOperation     // when the mock function is configured for expected calls

ErrNoResultForArgs      // when no result is configured for the specified arguments

func (*MockFn[A, R]) WhenCalledWith ΒΆ added in v0.7.0

func (mock *MockFn[A, R]) WhenCalledWith(args A) *FakeResult[R]

WhenCalledWith is used to configure the result for a specific set of arguments. This is useful when configuring a test where the result of a mocked function call depends on the arguments passed to the function but the arguments themselves, the number of times the function is called, or the order in which calls to the function are made are not significant.

Only one results can be configured for a given set of arguments.

If the arguments, the number and/or order of calls are significant or if different results are required to be mocked for different calls with the same arguments, use ExpectCall to configure expected calls instead.

Mocked results mapped to arguments may not be combined with expected calls.

returns ΒΆ

Returns a Fake[R] value that can be used to configure the result and/or error for the specified arguments.

errors ΒΆ

In the event of an error, the function will panic with one of the following errors:

ErrInvalidOperation     // when the mock function already has one or more expected calls
                        // configured

ErrInvalidArgument      // when a result is already configured for the specified arguments

type R ΒΆ added in v0.8.0

type R struct {
	// value recovered if the test execution caused a panic
	Recovered any

	// captured stdout output from the test function (the test failure report)
	Report []string

	// captured stderr output from the test function (logs emitted by the test)
	Log []string

	// test outcome
	// (TestPassed, TestFailed, TestPanicked)
	Outcome TestOutcome

	// names of any tests that failed
	FailedTests []string

	// Stack is the stack trace captured when recovering from a panicked test
	// or nil if the test did not panic
	Stack []byte
	// contains filtered or unexported fields
}

R is a struct that contains the result of executing a test function.

func TestHelper ΒΆ added in v0.10.0

func TestHelper(f func()) R

TestHelper runs a function that executes a function in an internal test runner, independent of the current test, returning an R value that captures the following:

  • the test outcome (TestPassed, TestFailed, TestPanicked)
  • names of any tests that failed
  • stdout output
  • stderr output
  • any value recovered from a panic

This function is intended to be used to test helper functions. For example, it is used extensively in the blugnu/test package itself, to test the test framework.

func (*R) Expect ΒΆ added in v0.8.0

func (r *R) Expect(exp ...any)

Expect verifies that the test result (R) matches the expected outcome.

At least one argument must be provided to Expect() to specify the expected outcome of the test. The arguments can be:

- a TestOutcome value (TestPassed, TestFailed, TestPanicked) - a string or slice of strings that are expected to be present in the test report - a combination of the above, with options to control the assertion behavior

The function will check the test outcome, the test report, and any recovered value from a panic, and will fail the test if any of the expectations are not met.

Currently, any additional output to stdout produced by the test function is ignored and must not be specified in any expected test report.

If no arguments are provided, Expect() will fail the test with an error message indicating that at least one expected outcome or report line is required.

If the test outcome is expected to be TestPanicked, the first argument must be a TestOutcome value (TestPanicked) with a single string argument that is expected to match the string representation (%v) of the value recovered from the panic.

func (*R) ExpectInvalid ΒΆ added in v0.8.0

func (r *R) ExpectInvalid(report ...any)

ExpectInvalid verifies that the test result (R) indicates an invalid test; that is, the test.Invalid() function was called during evaluation of the test, indicating some problem that makes the test result unreliable or meaningless.

When called without arguments it verifies that the test outcome is failed and that the test report consists only of the '<== INVALID TEST' label. i.e. if any additional report lines are present in the test report then the ExpectInvalid() call will itself fail.

If the test report is not significant to a test, it must be explicitly ignored by passing the opt.IgnoreReport(true) option:

result.ExpectInvalid(opt.IgnoreReport(true))

Invalid Tests ΒΆ

The Go standard library testing framework does not provide a way to mark a test as invalid; the test.Invalid() function fails a test and emits a message labelled with '<== INVALID TEST'.

When testing for an invalid test:

- the test outcome is expected to be TestFailed - the test report is expected to start with the '<== INVALID TEST' label - the test report is expected to contain any report lines specified

func (*R) ExpectWarning ΒΆ added in v0.10.0

func (r *R) ExpectWarning(msg string)

ExpectWarning verifies that the test result (R) contains a warning; that is, the test.Warning() function was called during evaluation of the test.

type Resetter ΒΆ added in v0.4.0

type Resetter interface {
	Reset()
}

Resetter is an interface that describes the Reset method. A Resetter is any type that can be reset to some initial state.

type Runnable ΒΆ added in v0.10.0

type Runnable interface {
	Run()
}

type TestExecutor ΒΆ added in v0.10.0

type TestExecutor[T any] interface {
	Execute(string, T)
}

TestExecutor is an interface that defines a function to execute a test case

func For ΒΆ added in v0.10.0

func For[T any](exec func(string, T)) TestExecutor[T]

For creates a TestExecutor that uses the provided function to execute each test case. The function is called with the name of the test case and the test case data. This allows for variations in test execution based on the test case name, which can be useful for more complex test scenarios.

func ForEach ΒΆ added in v0.10.0

func ForEach[T any](exec func(T)) TestExecutor[T]

ForEach creates a TestExecutor that uses the provided function to execute each test case. The function is called with the test case data, and the test case name is not provided.

type TestOutcome ΒΆ added in v0.8.0

type TestOutcome int
const (
	TestPassed TestOutcome = iota
	TestFailed
	TestPanicked
)

func (TestOutcome) String ΒΆ added in v0.8.0

func (to TestOutcome) String() string

type TestingT ΒΆ added in v0.8.0

type TestingT interface {
	Cleanup(fn func())
	Name() string
	Run(name string, fn func(t *testing.T)) bool
	Error(args ...any)
	Errorf(s string, args ...any)
	Fail()
	FailNow()
	Failed() bool
	Fatal(args ...any)
	Fatalf(s string, args ...any)
	Helper()
	Parallel()
	Setenv(name string, value string)
	SkipNow()
}

func GetT ΒΆ added in v0.8.0

func GetT() TestingT

GetT retrieves the *testing.T for the calling test frame, by calling T().

GetT is provided for use where calling the T() function directly is not possible, e.g. due to a name collision with a generic type parameter.

func T ΒΆ added in v0.8.0

func T() TestingT

T retrieves the TestRunner for the calling test frame. When running in a test frame, this will return the *testing.T for the test.

The T is returned as a TestingT interface; this provides all of the functionality of the *testing.T type, but allows for more flexibility in the test package.

Directories ΒΆ

Path Synopsis
internal
matchers

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL