mock

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Apr 18, 2025 License: MIT Imports: 15 Imported by: 0

README

Introduction

Mocking is a unit testing technique that isolates the code under test from external dependencies. By replacing dependencies with controlled objects that simulate real behavior, mocks ensure tests focus solely on the logic being tested, not on external systems. The mock package supports three types of replacement objects: fakes, stubs, and mocks.

Fakes: Fakes implement the same interface as the real dependency but operate independently, returning hardcoded results. While simple, they are inflexible—interface changes require updating all related fakes, which can be cumbersome.

Stubs: Stubs return predefined results for specific inputs and ignore unexpected calls. With the mock package, you can define stubs concisely, clearly specifying dependency behavior and expected system responses.

Mocks: Mocks are advanced stubs that support detailed expectations, such as call counts, call order, and argument values. The mock package enables creating mocks with minimal code, improving test readability and precision.

Basic Usage

Consider a basic example with a simple interface to mock:

// Adder is a simple interface to mock.
type Adder interface {
	Add(a, b float64) float64
}

// AdderMock implements the Adder interface. By convention, mock types are
// named after the interface with a "Mock" suffix.
type AdderMock struct {
	*mock.Mock // Embedded mock instance.

	// Add custom fields here if needed.
}

// NewAdderMock creates a new [AdderMock] instance. By convention, constructor
// functions are prefixed with "New". More complex mocks may accept additional
// parameters.
func NewAdderMock(t *testing.T) *AdderMock {
	return &AdderMock{mock.NewMock(t)}
}

// Add implements the Add method from the [Adder] interface.
func (mck *AdderMock) Add(a, b float64) float64 {
	// Record the method call with its arguments, returning [mock.Arguments]
	// containing the defined return values.
	rets := mck.Called(a, b)

	// Add custom logic here if needed.

	// Extract and return the first return value, cast to float64.
	return rets.Get(0).(float64)
}

// Test_Adder_Add demonstrates using AdderMock in a test.
func Test_Adder_Add(t *testing.T) {
	// --- Given ---
	mck := NewAdderMock(t) // Create the mock.
	mck.
		On("Add", 1.0, 2.0). // Specify expected method and arguments.
		Return(3.0)          // Define the return value.

	// --- When ---
	// In a real test, the mock would be passed to code requiring the Adder
	// interface, which would invoke Add as shown.
	have := mck.Add(1.0, 2.0)

	// --- Then ---
	// Prints: Result: 3.000000
	t.Logf("Result: %f", have)
}

This creates a fully functional mock for use in tests:

// Test_Adder_Add demonstrates using AdderMock in a test.
func Test_Adder_Add(t *testing.T) {
	// --- Given ---
	mck := NewAdderMock(t) // Create the mock.
	mck.
		On("Add", 1.0, 2.0). // Specify expected method and arguments.
		Return(3.0)          // Define the return value.

	// --- When ---
	// In a real test, the mock would be passed to code requiring the Adder
	// interface, which would invoke Add as shown.
	have := mck.Add(1.0, 2.0)

	// --- Then ---
	// Prints: Result: 3.000000
	t.Logf("Result: %f", have)
}

Defining Expectations

After creating a mock, you define how the Code Under Test (CUT) interacts with it using Mock.On, Mock.OnAny, or Mock.Proxy. These methods configure expectations for method calls, including arguments and return values. Each returns a mock.Call instance, which you use to refine the expectation.

Argument Matchers

In the example, we specified that Add expects exact argument values 1.0 and 2.0:

mck.On("Add", 1.0, 2.0)

Calls with different values cause the test to fail.

Matching Any Value

To ignore an argument’s value, use mock.Any:

mck.On("Add", 1.0, mock.Any)

This accepts any value of the second argument during validation.

Predefined Argument Matchers

The mock package provides common matchers for convenience:

  • mock.AnyString – Matches any string.
  • mock.AnyInt – Matches any integer.
  • mock.AnyBool – Matches any boolean.
  • mock.MatchOfType – Matches an argument’s type as string (e.g., "int", "*http.Request").
  • mock.MatchType – Matches an argument’s type using reflect package.
  • mock.MatchError – Matches a non-nil error with a specific message or error (via errors.Is).
  • mock.MatchErrorContain – Matches a non-nil error containing a given substring.

Return Values

For methods with return values, use Call.Return:

mck.On("Add", 1.0, 2.0).Return(3.0)
mck.On("Add", 2.0, mock.Any).Return(4.0)

Delaying Returns

Using a Timeout

To delay a method’s return, use Call.After. This is useful for testing latencies:

mck.On("Method", arg1, arg2).After(time.Second).Return(1)

The method pauses for one second before returning.

Using a Channel

To block until an external signal, use Call.Until with a channel:

ch := make(chan struct{})
mck.On("Method").Until(ch).Return(1)

External code can close or send on ch to unblock the method.

Panicking

To make a mock panic, use Call.Panic:

mck.On("Method").Panic("test panic")

Expecting Number of Calls

By default, expected methods can be called any number of times. To limit calls, use Call.Times:

mck.On("Method").Return("abc").Times(5)

For single calls, use Call.Once (equivalent to Call.Times(1)):

mck.On("Method").Return(1).Once()

Modifying Arguments

To modify arguments before returning, use Call.Alter:

mck.On("Unmarshal", mock.MatchOfType("*map[string]any")).
	Return().
	Alter(func(args mock.Arguments) {
        arg := args.Get(0).(*map[string]any)
        (*arg)["foo"] = "bar"
    })

If Call.After or Call.Until is used, Call.Alter runs after the delay.

Optional Calls

To mark a method call as optional (no error if uncalled), use Call.Optional:

mck.On("Method").Return(1).Optional()

Advanced Topics

Proxying Calls

You can configure a mock to proxy method calls to an underlying implementation using Mock.Proxy:

obj := &types.TPtr{}

mck := NewItfMock(t)
mck.Proxy(obj.Method)

Here, calls to the mocked Method are forwarded to obj.Method. The mock automatically detects the method’s name. To override the method name, provide an optional name argument:

mck.Proxy(obj.Method, "OtherName")

Argument Matchers for Proxied Methods

To validate arguments for proxied calls, use Call.With:

obj := &types.TPtr{}

mck := NewItfMock(t)
mck.Proxy(obj.Method).With(1, "abc", 3)

This ensures the proxied method is called with the specified arguments.

Custom Matchers

For advanced matching, define custom matchers with mock.MatchBy. Some predefined matchers, like mock.MatchErrorContain, are built this way:

// MatchErrorContain creates a [Matcher] that verifies the argument is a
// non-nil error containing the specified substring in its message.
func MatchErrorContain(want string) *Matcher {
    return MatchBy(func(err error) bool {
        return strings.Contains(err.Error(), want)
    })
}

See the mock.MatchBy documentation for details.

Documentation

Overview

Package mock provides helpers for creating and testing with interface mocks.

Index

Constants

View Source
const (
	// Any is used in Diff and Assert when the argument being tested
	// shouldn't be taken into consideration.
	Any = "mock.Any"
)

Variables

View Source
var (
	// ErrNeverCalled signals mocked non-optional method was never called.
	ErrNeverCalled = errors.New("method never called")

	// ErrTooFewCalls signals mocked method was called too few times.
	ErrTooFewCalls = errors.New("method called to few times")

	// ErrTooManyCalls signals mocked method was called too many times.
	ErrTooManyCalls = errors.New("method called too many times")

	// ErrRequirements signals mocked method requirements were not met.
	ErrRequirements = errors.New("method requirements not met")

	// ErrNotFound signals mocked method has not been found.
	ErrNotFound = errors.New("method not found")
)

Mock requirements violations.

View Source
var AnyBool = MatchOfType("bool")

AnyBool is a helper matching any argument value of boolean type.

View Source
var AnyCtx = MatchBy(func(ctx context.Context) bool {
	return ctx != nil
})

AnyCtx matches any non-nil context.

View Source
var AnyInt = MatchOfType("int")

AnyInt is a helper matching any argument value of integer type.

View Source
var AnyString = MatchOfType("string")

AnyString is a helper matching any argument value of string type.

Functions

func AnySlice

func AnySlice(cnt int) []any

AnySlice is a helper to create slice of length cnt of mock.Any values.

func WithNoStack

func WithNoStack(mck *Mock)

WithNoStack is option for NewMock turning off displaying stack traces in error log messages.

Types

type Arguments

type Arguments []any

Arguments holds an array of method arguments or return values.

func (Arguments) Bool

func (args Arguments) Bool(idx int) bool

Bool gets the argument at the specified index. Panics if there is no argument, or if the argument is of the wrong type.

func (Arguments) Diff

func (args Arguments) Diff(vs []any) ([]string, int)

Diff gets a string describing the differences between the expected arguments and the specified values. Returns a diff string and number of differences found.

nolint: cyclop

func (Arguments) Equal

func (args Arguments) Equal(haves ...any) bool

Equal gets whether the objects match the specified arguments. Panics on error.

func (Arguments) Error

func (args Arguments) Error(idx int) error

Error gets the argument at the specified index. Panics if there is no argument, or if the argument is of the wrong type.

func (Arguments) Get

func (args Arguments) Get(idx int) any

Get returns the argument at the specified index. Panics when index is out of bounds.

func (Arguments) Int

func (args Arguments) Int(idx int) int

Int gets the argument at the specified index cast to int. Panics for invalid index or when argument cannot be cast to an int.

func (Arguments) String

func (args Arguments) String(idx int) string

String gets the argument at the specified index cast to string. Panics for invalid index or when argument cannot be cast to a string. If index is set to -1 the method returns a complete string representation of the argument types.

type Call

type Call struct {
	// contains filtered or unexported fields
}

Call represents a mocked method call, used for defining expectations.

func (*Call) After

func (c *Call) After(d time.Duration) *Call

After sets how long to block until the call returns.

Mock.On("Method", arg1, arg2).After(time.Second)

func (*Call) Alter

func (c *Call) Alter(fn ...func(Arguments)) *Call

Alter sets functions to be called on arguments received by the mocked method before they are returned. It can be used when mocking a method (such as an unmarshaler) that takes a pointer to a struct and sets properties in such struct.

Example usage:

	Mock.On("Unmarshal", MatchOfType("map[string]any")).
 	Return().
 	Alter(func(args Arguments) {
	    arg := args.Get(0).(*map[string]any)
	    arg["foo"] = "bar"
	})

If Call.After or Call.Until methods are used, the provided function is run after the blocking is done.

func (*Call) CanCall

func (c *Call) CanCall() error

CanCall provided the argument match returns nil if the method can be called again, otherwise it returns error explaining why method cannot be called.

func (*Call) End

func (c *Call) End() *Mock

End ends a chain of calls on the Call instance and returns parent Mock.

func (*Call) Once

func (c *Call) Once() *Call

Once specifies that the mocked method should only be called once with this set of arguments and return values. It is a helper calling Call.Times.

Example usage:

Mock.On("Method", arg1, arg2).Return(returnArg1, returnArg2).Once()

func (*Call) Optional

func (c *Call) Optional() *Call

Optional allows the method call to be optional, meaning that the method may or may not be invoked without causing test failures. This can be useful for scenarios where some method calls are not strictly required. Invoking the method after Call.Times will cause a panic as the two are mutually exclusive.

Example usage:

Mock.On("MethodName").Optional()

func (*Call) Panic

func (c *Call) Panic(value any) *Call

Panic sets the panic value for the mock call and ensures that the method call will cause a panic during execution. This is useful for simulating failure scenarios where a panic is expected during a method call. It will panic if the Call instance represents a proxy call.

Example usage:

Mock.On("MethodName").Panic("test panic")
Mock.On("MethodName").Panic(errors.New("test panic"))

func (*Call) Requires

func (c *Call) Requires(calls ...*Call) *Call

Requires specifies that the current call depends on other calls being satisfied. It's used to define prerequisite calls that need to be executed before this call. The referenced calls may be from the same mock instance and/or other mock instances.

Example usage:

other := Mock.On("Init").Return(nil)
Mock.On("Do").Return(nil).Requires(other)

func (*Call) Return

func (c *Call) Return(args ...any) *Call

Return specifies the expected return arguments when the mocked method is invoked. If the Call instance represents a proxy call, this method will panic, as proxy calls cannot have predefined return values.

Example usage:

Mock.On("Method").Return(errors.New("operation failed"))

func (*Call) Satisfied

func (c *Call) Satisfied() bool

Satisfied returns true if the method call requirements are met.

A method call is considered satisfied if:

  • It has been called the exact number of requested times.
  • It has no maximum call limit and has been called at least once.
  • It has not been called, but is marked as optional.

func (*Call) Times

func (c *Call) Times(i int) *Call

Times specify that the mocked method should only be called i times with this set of arguments and return values.

Mock.On("Method", arg1, arg2).Return(returnArg1, returnArg2).Times(5)

func (*Call) Until

func (c *Call) Until(ch <-chan time.Time) *Call

Until sets a channel that will block the return of the method call until the channel is either closed or a message is received. This is useful for scenarios where delaying the method's return until certain conditions are met is required.

Example usage:

Mock.On("Method", arg1, arg2).Until(time.After(time.Second))

func (*Call) With

func (c *Call) With(args ...any) *Call

With sets argument requirements for the proxied method call.

Example usage:

Mock.Proxy(obj.Method).With(1, 2, 3)

type Matcher

type Matcher struct {
	// contains filtered or unexported fields
}

Matcher represents argument matcher.

func MatchBy

func MatchBy(fn any) *Matcher

MatchBy constructs an Matcher instance which validates arguments using a given function. The function must be accepting a single argument (of the expected type) and return a true if argument matches expectations or false when it doesn't. If function doesn't match the required signature, MatchBy panics.

Examples:

fn := func(have int) bool { ... }
fn := func(have float64) bool { ... }
fn := func(have ExampleItf) bool { ... }
fn := func(have ExampleType) bool { ... }
fn := func(have *ExampleType) bool { ... }

MatchBy can be used to match complex mocked method argument like function, structure, channel, map, ...

Example:

MatchBy(func(req *http.Request) bool { return req.Host == "localhost" })

func MatchError

func MatchError(want any) *Matcher

MatchError creates an Matcher that verifies the argument is a non-nil error. The "want" parameter specifies the expected error behavior:

  • If want is a string, the error's message (via Error()) is matched.
  • If want is an error, errors.Is is used to check.

It will panic if "want" is neither.

func MatchErrorContain

func MatchErrorContain(want string) *Matcher

MatchErrorContain constructs an argument matcher (Matcher) instance which ensures argument is a non nil error with given message.

func MatchOfType

func MatchOfType(typ string) *Matcher

MatchOfType constructs an argument matcher (Matcher) instance which ensures argument is of given type.

Examples:

MatchOfType("int")
MatchOfType("string")
MatchOfType("mock.ExampleType")
MatchOfType("*mock.ExampleType")
MatchOfType("map[string]interface {}")

The MatchOfType will not match if the string is an interface name.

func MatchSame

func MatchSame(want any) *Matcher

MatchSame matches two generic pointers point to the same object using [is.SamePointers].

func MatchType

func MatchType(typ any) *Matcher

MatchType constructs an argument matcher (Matcher) instance which ensures argument is of the same type as the MatchType argument.

Examples:

MatchType(42)
MatchType(true)
MatchType("string")
MatchType(mock.ExampleType{})
MatchType(*mock.ExampleType{})

func NewMatcher

func NewMatcher(fn any, desc string) *Matcher

NewMatcher returns new instance of an Matcher. Takes a function as in MatchBy documentation and argument matcher description. Function panics on error.

func (*Matcher) Desc

func (am *Matcher) Desc() string

Desc returns argument matcher description.

func (*Matcher) Match

func (am *Matcher) Match(have any) bool

Match runs matcher function with "have" argument and returns true if it matches expected value, otherwise returns false. It will panic if the "have" doesn't match expected type for the matcher.

type Mock

type Mock struct {
	// contains filtered or unexported fields
}

Mock tracks activity on a mocked interface.

func NewMock

func NewMock(t tester.T, opts ...Option) *Mock

NewMock returns new instance of Mock.

func (*Mock) AssertCallCount

func (mck *Mock) AssertCallCount(method string, want int) bool

AssertCallCount asserts the method was called "want" number of times.

func (*Mock) AssertExpectations

func (mck *Mock) AssertExpectations() bool

AssertExpectations asserts that everything specified with Mock.On and Call.Return was in fact called as expected. Calls may have occurred in any order.

func (*Mock) Call

func (mck *Mock) Call(method string, args ...any) Arguments

Call calls method on the mock with arguments and returns the mocked method Arguments. Panics if the call is unexpected (i.e. not preceded by appropriate Mock.On calls). Blocks before returning if Call.Until or Call.After were used.

func (*Mock) Callable

func (mck *Mock) Callable(method string, args ...any) error

Callable finds a callable method with given name and matching arguments. When found it returns it, otherwise it returns an error describing the reason. Note that there may be more methods in the expected slice matching the criteria in which case the first one is returned.

A callable method is one that returns no error from Call.CanCall method, and has matching arguments.

func (*Mock) Called

func (mck *Mock) Called(args ...any) Arguments

Called records that a method was invoked with the given arguments and returns the configured return values as a Arguments slice. It panics if the call is unexpected, meaning no matching Mock.On or Mock.OnAny expectation was set.

If the expectation uses Call.Until or Call.After, this method blocks until the specified condition is met.

func (*Mock) Failed

func (mck *Mock) Failed() bool

Failed returns true if Mock instance is in filed state, false otherwise.

func (*Mock) GetData

func (mck *Mock) GetData() map[string]any

GetData returns data that might be useful for testing (see SetData method).

func (*Mock) On

func (mck *Mock) On(method string, args ...any) *Call

On adds method call expectation for the interface being mocked.

Example usage:

Mock.On("Method", 1).Return(nil)
Mock.On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))

func (*Mock) OnAny

func (mck *Mock) OnAny(method string) *Call

OnAny adds a method call expectation for the mocked interface. Unlike Mock.On, where specific arguments are expected, this allows the method to be called with any combination of arguments, regardless of their number or type.

Example usage:

Mock.OnAny("Method").Return(nil)

func (*Mock) Proxy

func (mck *Mock) Proxy(met any, name ...string) *Call

Proxy uses passed method as a proxy for calls to its "name". If "name" argument is not empty the first value from the slice will be used as the proxied method name. It panics if "met" is not a method or function.

Example:

obj := &types.TPtr{}
mck.Proxy(obj.Method)

func (*Mock) SetData

func (mck *Mock) SetData(data map[string]any)

SetData sets data that might be useful for testing. The Mock ignores it. To get it back call GetData method.

func (*Mock) Unset

func (mck *Mock) Unset(remove *Call) *Mock

Unset removes Call instance from expected Mock calls. If the instance doesn't exist, it will trigger a test failure.

Example usage:

call := Mock.On("Method", mock.Any)
Mock.Unset(call).Unset()

type Option

type Option func(*Mock)

Option represents a NewMock option.

Jump to

Keyboard shortcuts

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