mmock

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2023 License: Apache-2.0 Imports: 12 Imported by: 0

README

MMOCK (Method Mock)

GoDoc Latest Version Go Report Card

mmock (method mock) is an overlay for the popular https://pkg.go.dev/github.com/stretchr/testify/mock that allows mocked methods to be specified by function rather than string name. The problem with specifying methods by string name is that refactoring tools will miss these.

As per this issue

For example, where you might have done...

  myMock.On("DoSomething", mock.Anything).Return(error.New(""))

mmock allows you to do...

  myMock.OnMethod(myMock.DoSomething).Return(error.New(""))

Installation

To install mmock, use go get:

go get github.com/go-andiamo/mmock

To update mmock to the latest version, run:

go get -u github.com/go-andiamo/mmock

Usage

To utilise mmock, simply replace the embedded mock instance in your structure. For example, where you had...

package main

import "github.com/stretchr/testify/mock"

type MyTestObject struct {
  mock.Mock
}

replace with...

package main

import "github.com/go-andiamo/mmock"

type MyTestObject struct {
  mmock.MockMethods
}

Your mock will now have all the same assertion, on call etc. methods so no other changes are needed - but you'll be able to utilise additional methods to specify mocking methods by function (rather than by name)...

package main

import (
  "errors"
  "github.com/go-andiamo/mmock"
  "github.com/stretchr/testify/assert"
  "testing"
)

type MyInterface interface {
  DoSomething(a string) (string, error)
  DoSomethingElse(a int) (int, error)
}

type MyTestObject struct {
  mmock.MockMethods
}

func (m *MyTestObject) DoSomething(a string) (string, error) {
  args := m.Called(a)
  return mmock.As[string](args, 0), mmock.As[error](args, 1)
}

func (m *MyTestObject) DoSomethingElse(a int) (int, error) {
  args := m.Called(a)
  return mmock.As[int](args, 0), mmock.As[error](args, 1)
}

func TestMyMock_DoSomething(t *testing.T) {
  mocked := mmock.NewMockOf[MyTestObject, MyInterface]()
  mocked.OnMethod(mocked.DoSomething).Return("", errors.New("foo"))

  _, err := mocked.DoSomething("a")
  assert.Error(t, err)
  mocked.AssertMethodCalled(t, mocked.DoSomething)
}

func TestMyMock_AllMethods(t *testing.T) {
  mocked := mmock.NewMockOf[MyTestObject, MyInterface]()
  mocked.OnAllMethods(true) // all methods return an error (where the method returns an error

  _, err := mocked.DoSomething("a")
  assert.Error(t, err)
  mocked.AssertMethodCalled(t, mocked.DoSomething)
  mocked.AssertMethodNotCalled(t, mocked.DoSomethingElse)

  _, err = mocked.DoSomethingElse(1)
  assert.Error(t, err)
  mocked.AssertMethodCalled(t, mocked.DoSomethingElse)
}

Things to notice:

  • when using .OnMethod() you only need to specify as many args that need matching - because mmock knows about the method, it can fill in remaining args with mock.Anything
  • the same is true of .AssertMethodCalled() and .AssertMethodNotCalled() - unspecified args are filled with mock.Anything
  • use .OnAllMethods() to mock all methods (optionally making all return an error)
  • use mmock.As() generic function in your mocked methods to return correct types

Spy Mocks

Mmock also provides for 'spy mocks' - where an actual underlying implementation is supplied to the mock. If methods on the mock are called but have not been mocked (using .On() or .OnMethod()) then the underlying method is called - but you can still assert that method was called.

See example

Mock generator

Mmock comes with a programmatic mock generator, e.g.

  f, _ := os.Create("internal/mock_thingy.go")
  _ = mmock.MockGenerateFile[internal.Thingy]("", f)

or...

  code, _ := mmock.MockGenerate[internal.Thingy]("")
  println(string(code))

Caveat emptor! The generated code is designed to save you work but is not 100% guaranteed to produce compilable code (it can get confused with convoluted or conflicting package names) and may sometimes require manual intervention.

Documentation

Overview

Package mmock - Go package extension for https://github.com/stretchr/testify

Provides additional methods on testify.mock that allow methods to be specified by func rather than string name

Which alleviates errors that can be introduced during refactoring

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func As

func As[T any](args mock.Arguments, index int) T

func As1

func As1[T1 any](args mock.Arguments) T1

func As2

func As2[T1 any, T2 any](args mock.Arguments) (T1, T2)

func As3

func As3[T1 any, T2 any, T3 any](args mock.Arguments) (T1, T2, T3)

func As4

func As4[T1 any, T2 any, T3 any, T4 any](args mock.Arguments) (T1, T2, T3, T4)

func MockGenerate

func MockGenerate[T any](pkg string) ([]byte, error)

MockGenerate generates Go code with a mock for interface type T

The pkg arg is the package name (or package path) for the code - if this is an empty string then the package of the interface type T is used

Caveat emptor! The generated code is designed to save you work but is not 100% guaranteed to produce compilable code (it can get confused with convoluted or conflicting package names) and may sometimes require manual intervention.

func MockGenerateFile

func MockGenerateFile[T any](pkg string, f *os.File) error

MockGenerateFile generates a Go file with a mock for interface type T

The pkg arg is the package name (or package path) for the code - if this is an empty string then the package of the interface type T is used

Caveat emptor! The generated code is designed to save you work but is not 100% guaranteed to produce compilable code (it can get confused with convoluted or conflicting package names) and may sometimes require manual intervention.

func NewMock

func NewMock[T any]() *T

NewMock creates a new mock of a specified type

Note: It ensures that the type implements mocks.MockMethods (and panics if it does not!)

Example usage:

type MockedSomething struct {
  mocks.MockMethods
}
myMock := NewMock[MockedSomething]()

func NewMockOf

func NewMockOf[T any, I any]() *T

NewMockOf creates a new mock of a specified type

same as NewMock except that it also checks that the specified type implements the interface specified by the second generic arg (and panics if it does not!)

Example usage:

type my interface {
  SomeMethod()
}
type MockedSomething struct {
  mocks.MockMethods
}
func (m *MockedSomething) SomeMethod() {
}
myMock := NewMockOf[MockedSomething, my]()

func NewSpyMockOf added in v1.1.0

func NewSpyMockOf[T any, I any](wrapped I) *T

NewSpyMockOf creates a new spy mock of a specified type and provides the underling wrapped implementation

The wrapped arg is implementation to be spied on - any methods that are called on the mock but have not been expected (by using On or OnMethod) will call this underlying - but you can still assert that the method has been called

Types

type MockMethods

type MockMethods struct {
	mock.Mock
	// contains filtered or unexported fields
}

MockMethods is the replacement for mock.Mock

func (*MockMethods) AssertMethodCalled

func (mm *MockMethods) AssertMethodCalled(t *testing.T, method any, arguments ...any) bool

AssertMethodCalled is the same as Mock.AssertCalled() (https://pkg.go.dev/github.com/stretchr/testify/mock#Mock.AssertCalled)

Except the method can be specified by func pointer or name

Also, if the number of arguments specified is less than the expected args of the method then the arguments is padded with mock.Anything

func (*MockMethods) AssertMethodNotCalled

func (mm *MockMethods) AssertMethodNotCalled(t *testing.T, method any, arguments ...any) bool

AssertMethodNotCalled is the same as Mock.AssertNotCalled() (https://pkg.go.dev/github.com/stretchr/testify/mock#Mock.AssertNotCalled)

Except the method can be specified by func pointer or name

Also, if the number of arguments specified is less than the expected args of the method then the arguments is padded with mock.Anything

func (*MockMethods) AssertNumberOfMethodCalls

func (mm *MockMethods) AssertNumberOfMethodCalls(t *testing.T, method any, expectedCalls int) bool

AssertNumberOfMethodCalls is the same as Mock.AssertNumberOfCalls() (https://pkg.go.dev/github.com/stretchr/testify/mock#Mock.AssertNumberOfCalls)

Except the method can be specified by func pointer or name

func (*MockMethods) Called added in v1.1.0

func (mm *MockMethods) Called(arguments ...interface{}) mock.Arguments

func (*MockMethods) MethodCalled added in v1.1.0

func (mm *MockMethods) MethodCalled(methodName string, arguments ...interface{}) (result mock.Arguments)

func (*MockMethods) OnAllMethods

func (mm *MockMethods) OnAllMethods(errs bool)

OnAllMethods setups expected calls on every method of the mock

Use the errs arg to specify that methods that return an error should return an error when called

func (*MockMethods) OnMethod

func (mm *MockMethods) OnMethod(method any, arguments ...any) *mock.Call

OnMethod is the same as Mock.On() (https://pkg.go.dev/github.com/stretchr/testify/mock#Call.On)

Except the method can be specified by func pointer or name

func (*MockMethods) SetSpyOf added in v1.1.0

func (mm *MockMethods) SetSpyOf(wrapped any)

type Spying added in v1.1.0

type Spying interface {
	// SetSpyOf sets the mock to be a spy mock
	//
	// The wrapped arg is implementation to be spied on - any methods that are called on the mock
	// but have not been expected (by using On or OnMethod) will call this underlying - but you can still assert
	// that the method has been called
	SetSpyOf(wrapped any)
}

Directories

Path Synopsis
examples
spy

Jump to

Keyboard shortcuts

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