Skip to content
Go back

Gomock v.s. Mockery

Updated:
Edit Page

Gomock: https://github.com/uber-go/mock
Mockery: https://vektra.github.io/mockery

My conclusion: Mockery is better in terms of type hint and API. Gomock may have clearer error messages. Mockery configs are managed in one yaml file. Gomock uses //go:generate directive in the files. I would choose Mockery for my new projects.

Code To Test

The interface in /internal/foo/client.go

type Client interface {
 Get(path string) (string, error)
}

Used by function Foo in main.go

type client struct{}

func (c *client) Get(path string) (string, error) {
 return "Hello", nil
}

func Foo(c Client) (string, error) {
 response, err := c.Get("/greet")
 return response, err
}

Mockery Example

Create config .mockery.yaml. Then run mockery to generate mocks. The example is not used for this demo.

all: false
dir: '{{.InterfaceDir}}'
filename: mocks_test.go
force-file-write: false
formatter: goimports
log-level: info
structname: '{{.Mock}}{{.InterfaceName}}'
pkgname: '{{.SrcPackageName}}'
recursive: false
template: testify
packages:
  github.com/vektra/mockery/v3/internal/fixtures:
    config:
      all: true

API with Testify

func TestFoo(t *testing.T) {
 m := mocks.NewMockClient(t)
 m.EXPECT().Get(mock.Anything).
  Return("Bar", nil).Once()

 got, err := Foo(m)
 assert.NoError(t, err)
 assert.Equal(t, "Bar", got)
}

A compile error would appear if the returned types are not correct. For example:

cannot use 123 (untyped int constant) as string value in argument to m.EXPECT().Get(mock.Anything).Returncompiler[IncompatibleAssign]

Error message

If the times being called do not match:

--- FAIL: TestFoo (0.00s)
    client.go:20: FAIL: Get(string)
                        at: [internal/mocks/client.go:72 main_test.go:13]
    client.go:20: FAIL: 0 out of 1 expectation(s) were met.
                The code you are testing needs to make 1 more call(s).
                at: [internal/mocks/client.go:20 
                /opt/homebrew/Cellar/go/1.25.7_1/libexec/src/testing/testing.go:1308 
                /opt/homebrew/Cellar/go/1.25.7_1/libexec/src/testing/testing.go:1572 
                /opt/homebrew/Cellar/go/1.25.7_1/libexec/src/testing/testing.go:1928]

Gomock Example

Run mockgen to generate mocks. mockgen -source=internal/foo/client.go -package=mocks -destination=internal/mocks/foo.go

main_test.go

func TestFoo(t *testing.T) {
 ctrl := gomock.NewController(t)
 m := mocks.NewMockClient(ctrl)
 m.EXPECT().Get("/greet").
  Return("Bar", nil).Times(1)

 got, err := Foo(m)
 assert.NoError(t, err)
 assert.Equal(t, "Bar", got)
}

There is no types defined by Return() in mockgen. The args are Return(rets ...any).

Error message if Return(123, nil):

--- FAIL: TestFoo (0.00s)
    main_test.go:15: wrong type of argument 0 to Return for *mocks.MockClient.Get: int is not assignable to string [main_test.go:14]
    controller.go:97: missing call(s) to *mocks.MockClient.Get(is anything) main_test.go:14
    controller.go:97: aborting test due to missing call(s)

Error message if the called times do not match:

--- FAIL: TestFoo (0.00s)
    controller.go:97: missing call(s) to *mocks.MockClient.Get(is anything) main_test.go:14
    controller.go:97: aborting test due to missing call(s)

Edit Page
Share this post on:

Next Post
Celebrating Solving 1000 Leetcode Questions