Skip to content

Instantly share code, notes, and snippets.

@maratori
Last active November 27, 2024 20:30
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-09-01

gomock testify + mockery mockio minimock moq

Library

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

Mock creation

Code generation 3 ✖️
Works without code generation 4
Use t.Cleanup() 5
Support generics

DSL

Execute custom func 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
Capturer 12

Calls count

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

Matchers

Any value
Equal to (reflect)
Not equal to
Exact (==)
One of values
Regex
Substring
Is nil
Not nil
Type is
Length is ✔️ 16
Slice elements in any order
Slice contains
Map contains keys
Functional options
Logical operations on matchers
Custom matcher

Examples

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()
	...
}

mockio

func TestMockio(t *testing.T) {
	mock.SetUp(t, mockopts.StrictVerify())
	m := mock.Mock[MyInterface]()
	mock.WhenDouble(m.Method(mock.AnyContext(), mock.Equal("abc"))).ThenReturn(123, nil).Verify(mock.Once())
	mock.WhenDouble(m.AnotherMethod(mock.AnyContext(), mock.Regex("ab?c"))).Verify(mock.Once())
	captor := mock.Captor[string]()
	mock.WhenDouble(m.Method(mock.AnyContext(), captor.Capture()))
	...
	assert.Equal(t, "abc", captor.Last())
}

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)
	})
	...
}

Footnotes

  1. Google stopped maintaining the original gomock with 9K stars, Uber forked it and continue development

  2. mockio supports only amd64 and arm64 processors and it heavily depends on compiler internals and unsafe package

  3. CLI tool to auto generate mocks from interfaces

  4. Create mocks in runtime, without code generation

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

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

  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. Ability to capture actual call arguments to validate them later (not in mock)

  13. Define expected exact number of calls in test

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

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

  16. There are only length matchers for slices and maps, not for strings

@arshamalh
Copy link

Now gomock is not maintained anymore and we can use its forked version by Uber instead, which might have different features than the one mentioned in this table.

@maratori
Copy link
Author

@arshamalh thanks for pointing this out. I need some time to check it and update the table.

@maratori
Copy link
Author

@arshamalh I've added Uber's gomock to the table

@Zurvarian
Copy link

Hi, thanks for this table, it is quite helpful.

There is another, quite new, mocking library that I've been evaluating for our projects that is quite promising too, in case you want to consider it: https://github.com/ovechkin-dm/mockio

It is still not well known, but what I've tested so far works fine and the fact that it does not require mock generation is quite helpful.

@unmarshall
Copy link

testify column needs correction in the matchers section. Using assert you can check nil (assert.Nil and assert.NotNil), similarly assert.Len to check the length, assert.NotEqual. For regular expressions (https://pkg.go.dev/github.com/pgpst/pgpst/internal/github.com/stretchr/testify/assert#Regexp). The comparison is quite nice and provides one pager comparison but needs to be updated so that one can rely upon it.

@maratori
Copy link
Author

maratori commented Jan 8, 2024

@unmarshall correct me if I'm wrong, but you can't use functions from the assert package as matchers for the mock package.

@maratori
Copy link
Author

maratori commented Jan 8, 2024

@Zurvarian mockio looks really interesting, thanks! I need to think if it's mature enough to add it to the table.

@unmarshall
Copy link

@maratori Ah you are right.
There is another framework which has received attention: https://github.com/vektra/mockery

@maratori
Copy link
Author

maratori commented Jan 9, 2024

@unmarshall mockery is already on the table.

@LandonTClipp
Copy link

Maintainer of mockery here. The table lists mockery has not having Typed expected arguments (with the strict requirement here being that we don't use interface{}). There is a way to utilize strictly type-equivalent assertions through .RunAndReturn, see here: https://vektra.github.io/mockery/latest/features/#expecter-structs

Example:

requesterMock.EXPECT().
    Get(mock.Anything).
    RunAndReturn(func(path string) string { 
        fmt.Println(path, "was called")
        return "result for " + path
    })

I intend v3 to address the "various ways to assert the same expectation" issue, but just an FYI that mockery does in fact allow this particular pattern.

@maratori
Copy link
Author

maratori commented Jan 11, 2024

@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.

UPD: The last statement is incorrect as mockio has matchers, and it's strictly typed at the same time.

@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

@m1khal3v
Copy link

Hi, thanks for this table, it is quite helpful.

There is another, quite new, mocking library that I've been evaluating for our projects that is quite promising too, in case you want to consider it: https://github.com/ovechkin-dm/mockio

It is still not well known, but what I've tested so far works fine and the fact that it does not require mock generation is quite helpful.

It’s strange that it’s still not in the table. great library without code generation

@maratori
Copy link
Author

@m1khal3v @Zurvarian I've added mockio to the table

@mbalabin
Copy link

Hi, thanks for your great comparison.

Just a small correction: minimock has a "Capturer" capability. All call parameters are stored in a mock and may be accessed later via Calls() function.

@maratori
Copy link
Author

maratori commented Sep 1, 2024

@mbalabin good catch, thanks! Actually, all libs but gomock allow introspecting calls afterward. IMO, mockio provides the best API for that, but I've updated the table accordingly.

@ovechkin-dm
Copy link

ovechkin-dm commented Sep 1, 2024

@m1khal3v @Zurvarian I've added mockio to the table

Thank you for adding mockio to your table.
The feature set for it is very accurate.

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