Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save MikhailLipanin/abb8f81e6829f3435ebb3ae1ac7fd085 to your computer and use it in GitHub Desktop.

Select an option

Save MikhailLipanin/abb8f81e6829f3435ebb3ae1ac7fd085 to your computer and use it in GitHub Desktop.
Выбор инструментария для создания mock-ов
На данный момент для Go существуют следующие популярные инструменты для создания mock-ов, которые здесь будут рассмотрены:
1. [uber-go/mock: GoMock is a mocking framework for the Go programming language.](https://github.com/uber-go/mock)
2. [testify](https://github.com/stretchr/testify#mock-package) + [mockery](https://github.com/vektra/mockery)
3. [matryer/moq: Interface mocking tool for go generate](https://github.com/matryer/moq)
4. [gojuno/minimock: Powerful mock generation tool for Go programming language](https://github.com/gojuno/minimock)
5. [ovechkin-dm/mockio: Mockito for golang](https://github.com/ovechkin-dm/mockio)
За основу сравнения была взята таблица из [Comparison of golang mocking libraries](https://gist.github.com/maratori/8772fe158ff705ca543a0620863977c2#user-content-fn-33-32602835109cdc065a83b5486972bcc3)
| | [gomock][1] | [testify][2] + [mockery][3] | [mockio][4] | [minimock][5] | [moq][6] |
| :---------------------------------- | :----------------: | :-------------------------: | :----------------------: | :----------------: | :----------------: |
| <h3>Library</h3> | | | | | |
| GitHub звезды | [![s1]][1] | [![s2]][2] + [![s3]][3] | [![s4]][4] | [![s5]][5] | [![s6]][6] |
| Дата последнего релиза | [![d1]][r1] | [![d2]][r2] + [![d3]][r3] | [![d4]][r4] | [![d5]][r5] | [![d6]][r6] |
| Notes | [^1] | | [^4] | | |
| <h3>Mock creation</h3> | | | | | |
| Генерация кода | :white_check_mark: | :white_check_mark: | :heavy_multiplication_x: | :white_check_mark: | :white_check_mark: |
| Работает без генерации кода | :x: | :white_check_mark: | :white_check_mark: | :x: | :x: |
| Use `t.Cleanup()` [^12] | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| Support generics | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| <h3>DSL</h3> | | | | | |
| Execute custom func [^20] | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Calls order [^21] | :white_check_mark: | :white_check_mark: | :x: | :x: | :x: |
| Wait for `time.Duration` [^22] | :x: | :white_check_mark: | :x: | :x: | :x: |
| Wait for message from `chan` [^23] | :x: | :white_check_mark: | :x: | :x: | :x: |
| Panic [^24] | :x: | :white_check_mark: | :x: | :x: | :x: |
| Assert expectations with timeout | :x: | :x: | :x: | :white_check_mark: | :x: |
| Return zero values by default [^25] | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: |
| Capturer [^26] | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| <h3>Calls count</h3> | | | | | |
| Exact calls count [^30] | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| Min/max calls count [^31] | :white_check_mark: | :x: | :x: | :x: | :x: |
| Called at least once | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: |
| Don't check calls count | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| Expect once by default [^32] | :white_check_mark: | :x: | :x: | :x: | :x: |
| <h3>Matchers</h3> | | | | | |
| Any value | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| Equal to (`reflect`) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| Not equal to | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: |
| Exact (`==`) | :x: | :x: | :white_check_mark: | :x: | :x: |
| One of values | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: |
| Regex | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: |
| Substring | :x: | :x: | :white_check_mark: | :x: | :x: |
| Is `nil` | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: |
| Not `nil` | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: |
| Type is | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :x: |
| Length is | :white_check_mark: | :x: | :heavy_check_mark: [^33] | :x: | :x: |
| Slice elements in any order | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: |
| Slice contains | :x: | :x: | :white_check_mark: | :x: | :x: |
| Map contains keys | :x: | :x: | :white_check_mark: | :x: | :x: |
| Functional options | :x: | :white_check_mark: | :x: | :x: | :x: |
| Logical operations on matchers | :white_check_mark: | :x: | :x: | :x: | :x: |
| Custom matcher | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
[^1]: Google stopped maintaining the original [gomock](https://github.com/golang/mock) with 9K stars, Uber [forked](https://github.com/uber-go/mock) it and continue development
[^4]: `mockio` supports only amd64 and arm64 processors and it heavily depends on compiler internals and unsafe package
[^10]: CLI tool to auto generate mocks from interfaces
[^11]: Create mocks in runtime, without code generation
[^12]: Mock constructor uses `t.Cleanup()` to assert expectations after test by default
[^20]: Use arbitrary function to execute, allowing to implement any feature in the table
[^21]: Define expected order of calls
[^22]: Block execution of method using `time.Sleep()`
[^23]: Block execution of method using `<- channel`
[^24]: Panic instead of method execution
[^25]: Not defining return values leads to returning zero values
[^26]: Ability to capture actual call arguments to validate them later (not in mock)
[^30]: Define expected exact number of calls in test
[^31]: Define expected min/max number of calls in test
[^32]: Not defining number of calls leads to expectation that method to be called once
[^33]: There are only length matchers for slices and maps, not for strings
## Examples
### gomock
```go
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
```go
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
```go
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
```go
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
```go
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)
})
...
}
```
[1]: https://github.com/uber-go/mock
[2]: https://github.com/stretchr/testify#mock-package
[3]: https://github.com/vektra/mockery
[4]: https://github.com/ovechkin-dm/mockio
[5]: https://github.com/gojuno/minimock
[6]: https://github.com/matryer/moq
[s1]: https://img.shields.io/github/stars/uber-go/mock?label=&style=flat-square
[s2]: https://img.shields.io/github/stars/stretchr/testify?label=&style=flat-square
[s3]: https://img.shields.io/github/stars/vektra/mockery?label=&style=flat-square
[s4]: https://img.shields.io/github/stars/ovechkin-dm/mockio?label=&style=flat-square
[s5]: https://img.shields.io/github/stars/gojuno/minimock?label=&style=flat-square
[s6]: https://img.shields.io/github/stars/matryer/moq?label=&style=flat-square
[d1]: https://img.shields.io/github/release-date/uber-go/mock?label=&style=flat-square
[d2]: https://img.shields.io/github/release-date/stretchr/testify?label=&style=flat-square
[d3]: https://img.shields.io/github/release-date/vektra/mockery?label=&style=flat-square
[d4]: https://img.shields.io/github/release-date/ovechkin-dm/mockio?label=&style=flat-square
[d5]: https://img.shields.io/github/release-date/gojuno/minimock?label=&style=flat-square
[d6]: https://img.shields.io/github/release-date/matryer/moq?label=&style=flat-square
[r1]: https://github.com/uber-go/mock/releases
[r2]: https://github.com/stretchr/testify/releases
[r3]: https://github.com/vektra/mockery/releases
[r4]: https://github.com/ovechkin-dm/mockio/releases
[r5]: https://github.com/gojuno/minimock/releases
[r6]: https://github.com/matryer/moq/releases
Источники:
1. [Comparison of golang mocking libraries](https://gist.github.com/maratori/8772fe158ff705ca543a0620863977c2)
2. [Mocking in Golang. Mocking is an essential concept in… | by Deniz GÜRSOY | Medium](https://dgursoy.medium.com/mocking-in-golang-4bd0d93b98bd)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment