Skip to content

Instantly share code, notes, and snippets.

@maratori
Last active June 7, 2024 18:26
Show Gist options
  • Save maratori/8772fe158ff705ca543a0620863977c2 to your computer and use it in GitHub Desktop.
Save maratori/8772fe158ff705ca543a0620863977c2 to your computer and use it in GitHub Desktop.
Comparison of golang mocking libraries

Comparison of golang mocking libraries

Updated 2024-05-29

Uber
gomock
testify + mockery minimock moq Google
gomock

Library

GitHub stars s6 s2 + s3 s4 s5 s1
Latest release date d6 d2 + d3 d4 d5 d1
Maintained

Mock creation

Code generation 1
Not only code generation 2
Use t.Cleanup() 3
Support generics

DSL

Execute custom func 4
Typed expected arguments 5 ✖️ ✖️ ✔️ ✔️ ✖️
Typed returned values 6 ✔️ ✔️ ✔️ ✔️ ✖️
Calls order 7
Wait for time.Duration 8
Wait for message from chan 9
Panic 10
Assert expectations with timeout
Return zero values by default 11

Calls count

Exact calls count 12
Min/max calls count 13
Called at least once
Don't check calls count
Expect once by default 14

Matchers

Any value
Equal to
Regex
Not equal to
Is nil
Type is
Length is
Slice elements in any order
Compose matchers with logical or
Custom matcher

Examples

Uber gomock

func TestUberGomock(t *testing.T) {
	ctrl := gomock.NewController(t)
	m := NewMockMyInterface(ctrl)
	gomock.InOrder(
		m.EXPECT().Method(gomock.Any(), "abc").Return(123, nil),
		m.EXPECT().AnotherMethod(gomock.Any(), gomock.Len(3)),
	)
	...
}

testify/mock + mockery

func TestTestifyMock(t *testing.T) {
	m := mocks.NewMyInterface(t)
	m.EXPECT().Method(mock.Anything, "abc").After(5*time.Second).Return(123, nil).Once()
	m.EXPECT().AnotherMethod(mock.Anything, "abc").Return(0, nil).Once()
	...
}

minimock

func TestMinimock(t *testing.T) {
	ctrl := minimock.NewController(t)
	m := NewMyInterfaceMock(ctrl)
	m.MethodMock.When(minimock.AnyContext, "abc").Then(123, nil)
	m.AnotherMethodMock.When(minimock.AnyContext, "abc").Then(0, nil)
	...
}

moq

func TestMoq(t *testing.T) {
	m := MyInterfaceMock{
		MethodFunc: func(ctx context.Context, s string) (int, error) {
			assert.Equal(t, "abc", s)
			return 123, nil
		},
		AnotherMethodFunc: func(ctx context.Context, s string) (int, error) {
			assert.Len(t, s, 1)
			return 0, nil
		},
	}
	t.Cleanup(func() {
		assert.Len(t, m.MethodCalls(), 1)
		assert.Len(t, m.AnotherMethodCalls(), 1)
	})
	...
}

Google gomock

func TestGoogleGomock(t *testing.T) {
	ctrl := gomock.NewController(t)
	m := NewMockMyInterface(ctrl)
	gomock.InOrder(
		m.EXPECT().Method(gomock.Any(), "abc").Return(123, nil),
		m.EXPECT().AnotherMethod(gomock.Any(), gomock.Len(3)),
	)
	...
}

Footnotes

  1. CLI tool to auto generate mocks from interfaces

  2. Allow writing mock manually, without code generation

  3. Mock constructor uses t.Cleanup() to assert expectations after test by default

  4. Use arbitrary function to execute, allowing to implement any feature in the table

  5. Defining expected arguments in test doesn't rely on any (aka empty interface{})

  6. Defining returned values (with methods like Return and DoAndReturn) in test doesn't rely on any (aka empty interface{})

  7. Define expected order of calls

  8. Block execution of method using time.Sleep()

  9. Block execution of method using <- channel

  10. Panic instead of method execution

  11. Not defining return values leads to returning zero values

  12. Define expected exact number of calls in test

  13. Define expected min/max number of calls in test

  14. Not defining number of calls leads to expectation that method to be called once

@maratori
Copy link
Author

@LandonTClipp, thanks for pointing me this. Most probably, the wording in the table needs to be better. Typed expected arguments means that a method to define expectation is typed. The RunAndReturn method doesn't define an expectation. In your example, the method Get does it, and it accepts any.

But I've removed the wrong checkmark in Uber gomock for that feature.

Actually, I realized that having Typed expected arguments means not having matches at all.

@LandonTClipp
Copy link

Theoretically, setting all elements to mock.Anything to the argument matcher, then using .RunAndReturn has the effect of a totally type-safe expectation. This is kind of the style of matryer/moq, just done in a different way.

It's for sure a hard thing to categorize correctly :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment