Created
March 4, 2025 23:09
-
-
Save MikhailLipanin/abb8f81e6829f3435ebb3ae1ac7fd085 to your computer and use it in GitHub Desktop.
Выбор инструментария для создания mock-ов
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| На данный момент для 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