Skip to content

Instantly share code, notes, and snippets.

@thiagozs
Last active March 27, 2026 01:29
Show Gist options
  • Select an option

  • Save thiagozs/4276432d12c2e5b152ea15b3f8b0012e to your computer and use it in GitHub Desktop.

Select an option

Save thiagozs/4276432d12c2e5b152ea15b3f8b0012e to your computer and use it in GitHub Desktop.
Tutorial gomock

08/16/17 by ย Sergey Grebenshchikov

No Comments

This is a quick tutorial on how to test code using theย GoMockย mocking library and the standard library testing packageย testing.

GoMockย is a mock framework for Go. It enjoys a somewhat official status as part of theย github.com/golangย organization, integrates well with the built-inย testingย package, and provides a flexible expectation API.

The code snippets referenced in this post are available on GitHub:ย github.com/sgreben/testing-with-gomock.

Contents

Installation

First, we need to install theย gomockย packageย github.com/golang/mock/gomockย as well as theย mockgenย code generation toolย github.com/golang/mock/mockgen. Technically, weย couldย do without the code generation tool, but then weโ€™d have to write our mocks by hand, which is tedious and error-prone.

Both packages can be installed usingย go get:

go get github.com/golang/mock/gomock go get github.com/golang/mock/mockgen

We can verify that theย mockgenย binary was installed successfully by running

$GOPATH/bin/mockgen

This should output usage information and a flag list. Now that weโ€™re all set up, weโ€™re ready to test some code!

Basic Usage

Usage ofย GoMockย follows four basic steps:

  1. Useย mockgenย to generate a mock for the interface you wish to mock.
  2. In your test, create an instance ofย gomock.Controllerย and pass it to your mock objectโ€™s constructor to obtain a mock object.
  3. Callย EXPECT()ย on your mocks to set up their expectations and return values
  4. Callย Finish()ย on the mock controller to assert the mockโ€™s expectations

Letโ€™s look at a small example to demonstrate the above workflow. To keep things simple, weโ€™ll be looking at just two files โ€” an interfaceย Doerย in the fileย doer/doer.goย that we wish to mock and a structย Userย inย user/user.goย that uses theย Doerย interface.

The interface that we wish to mock is just a couple of lines โ€” it has a single methodย DoSomethingย that does something with anย intย and aย stringย and returns anย error:

doer/doer.go

package doer

type Doer interface { DoSomething(int, string) error }

Hereโ€™s the code that we want to test while mocking out theย Doerย interface:

user/user.go

package user

import "github.com/sgreben/testing-with-gomock/doer"

type User struct { Doer doer.Doer }

func (u *User) Use() error { return u.Doer.DoSomething(123, "Hello GoMock") }

Our current project layout looks as follows:

'-- doer
    '-- doer.go
'-- user
    '-- user.go

Weโ€™ll put the mock forย Doerย in a packageย mocksย in the root directory and the test forย Userย in the fileย user/user_test.go:

'-- doer
    '-- doer.go
'-- mocks
    '-- mock_doer.go
'-- user
    '-- user.go
    '-- user_test.go

We start by creating a directoryย mocksย that will contain our mock implementations and then runningย mockgenย on theย doerย package:

mkdir -p mocks

mockgen -destination=mocks/mock_doer.go -package=mocks github.com/sgreben/testing-with-gomock/doer Doer

Here, we have to create the directoryย mocksย ourselves because GoMock wonโ€™t do it for us and will quit with an error instead. Hereโ€™s what the arguments given toย mockgenย mean:

  • -destination=mocks/mock_doer.go: put the generated mocks in the fileย mocks/mock_doer.go.
  • -package=mocks: put the generated mocks in the packageย mocks
  • github.com/sgreben/testing-with-gomock/doer: generate mocks for this package
  • Doer: generate mocks for this interface. This argument is required โ€” we need to specify the interfaces to generate mocks for explicitly. Weย can, however specify multiple interfaces here as a comma-separated list (e.g.ย Doer1,Doer2).

Ifย $GOPATH/binย was not in ourย $PATH, weโ€™d have to callย mockgenย viaย $GOPATH/bin/mockgen. In the following weโ€™ll assume that we haveย $GOPATH/binย in ourย $PATH.

As a result, our invocation ofย mockgenย places a fileย mocks/mock_doer.goย in our project. This is how such a generated mock implementation looks:

mocks/mock_doer.go

// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/sgreben/testing-with-gomock/doer (interfaces: Doer)

package mocks

import (
	gomock "github.com/golang/mock/gomock"
)

// MockDoer is a mock of Doer interface
type MockDoer struct {
	ctrl     *gomock.Controller
	recorder *MockDoerMockRecorder
}

// MockDoerMockRecorder is the mock recorder for MockDoer
type MockDoerMockRecorder struct {
	mock *MockDoer
}

// NewMockDoer creates a new mock instance
func NewMockDoer(ctrl *gomock.Controller) *MockDoer {
	mock := &MockDoer{ctrl: ctrl}
	mock.recorder = &MockDoerMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use
func (_m *MockDoer) EXPECT() *MockDoerMockRecorder {
	return _m.recorder
}

// DoSomething mocks base method
func (_m *MockDoer) DoSomething(_param0 int, _param1 string) error {
	ret := _m.ctrl.Call(_m, "DoSomething", _param0, _param1)
	ret0, _ := ret\[0\].(error)
	return ret0
}

// DoSomething indicates an expected call of DoSomething
func (_mr *MockDoerMockRecorder) DoSomething(arg0, arg1 interface{}) *gomock.Call {
	return _mr.mock.ctrl.RecordCall(_mr.mock, "DoSomething", arg0, arg1)
}

Note that the generatedย EXPECT()ย method is defined on the same object as the mock methods (in this case,ย DoSomething) โ€” avoiding name clashes here is likely a reason for the non-standard all-uppercase name.

Next, we define aย mock controllerย inside our test. A mock controller is responsible for tracking and asserting the expectations of its associated mock objects.

We can obtain a mock controller by passing a valueย tย of typeย *testing.Tย to its constructor, and then use it to construct a mock of theย Doerย interface. We alsoย deferย itsย Finishย method โ€” more on this later.

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

mockDoer := mocks.NewMockDoer(mockCtrl)

Suppose we want to assert thatย mockerDoerโ€˜sย Doย method will be calledย once, withย 123ย andย "Hello GoMock"ย as arguments, and will returnย nil.

To do this, we can now callย EXPECT()ย on theย mockDoerย to set up its expectations in our test. The call toย EXPECT()returns an object (called a mockย recorder) providing methods of the same names as the real object.

Calling one of the methods on the mock recorder specifies an expected call with the given arguments. You can then chain other properties onto the call, such as:

  • the return value (viaย .Return(...))
  • the number of times this call is expected to occur (viaย .Times(number), or viaย .MaxTimes(number)ย andย .MinTimes(number))

In our case, the call looks like this:

mockDoer.EXPECT().DoSomething(123, "Hello GoMock").Return(nil).Times(1)

Thatโ€™s it โ€“ weโ€™ve now specified our first mock call! Hereโ€™s the complete example:

user/user_test.go

package user_test

import (
  "github.com/sgreben/testing-with-gomock/mocks"
  "github.com/sgreben/testing-with-gomock/user"
)

func TestUse(t *testing.T) {
    mockCtrl := gomock.NewController(t)
    defer mockCtrl.Finish()

    mockDoer := mocks.NewMockDoer(mockCtrl)
    testUser := &user.User{Doer:mockDoer}

    // Expect Do to be called once with 123 and "Hello GoMock" as parameters, and return nil from the mocked call.
    mockDoer.EXPECT().DoSomething(123, "Hello GoMock").Return(nil).Times(1)

    testUser.Use()
}

Probably, itโ€™s not obvious where in the code the mockโ€™s expectations are asserted. This happens in the deferredย Finish(). Itโ€™s idiomatic toย deferย this call toย Finishย at the point of declaration of the mock controller โ€” this way we donโ€™t forget to assert the mock expectations later.

Finally, weโ€™re ready to run our tests:

$ go test -v github.com/sgreben/testing-with-gomock/user
=== RUN   TestUse
--- PASS: TestUse (0.00s)
PASS
ok      github.com/sgreben/testing-with-gomock/user     0.007s

If you need to construct more than one mock, you can reuse the mock controller โ€” itsย Finishย method will then assert the expectations of all mocks associated with the controller.

We might also want to assert that the value returned by theย Useย method is indeed the one returned to it byย DoSomething. We can write another test, creating a dummy error and then specifying it as a return value forย mockDoer.DoSomething:

user/user_test.go

func TestUseReturnsErrorFromDo(t *testing.T) {
    mockCtrl := gomock.NewController(t)
    defer mockCtrl.Finish()

    dummyError := errors.New("dummy error")
    mockDoer := mocks.NewMockDoer(mockCtrl)
    testUser := &user.User{Doer:mockDoer}

    // Expect Do to be called once with 123 and "Hello GoMock" as parameters, and return dummyError from the mocked call.
    mockDoer.EXPECT().DoSomething(123, "Hello GoMock").Return(dummyError).Times(1)

    err := testUser.Use()

    if err != dummyError {
        t.Fail()
    }
}

Usingย GoMockย withย go:generate

Runningย mockgenย for each package and interface individually is cumbersome when there is a large number of interfaces/packages to mock. To alleviate this problem, theย mockgenย command may be placed in a specialย go:generateย comment.

In our example, we can add aย go:generateย comment just below theย packageย statement of ourย doer.go:

doer/doer.go

package doer

//go:generate mockgen -destination=../mocks/mock_doer.go -package=mocks github.com/sgreben/testing-with-gomock/doer Doer

type Doer interface {
    DoSomething(int, string) error
}

Note that at the point whereย mockgenย is called, the current working directory isย doerย โ€” hence we need to specifyย ../mocks/ย as the directory to write our mocks to, not justย mocks/.

We can now comfortably generate all mocks specified by such a comment by running

go generate ./...

from the projectโ€™s root directory. Note that there is no space betweenย //ย andย go:generateย in the comment. This is required forย go generateย to pick up the comment as an instruction to process.

A reasonable policy on where to put theย go:generateย comment and which interfaces to include is the following:

  • Oneย go:generateย comment per file containing interfaces to be mocked
  • Include all interfaces to generate mocks for in the call toย mockgen
  • Put the mocks in a packageย mocksย and write the mocks for a fileย X.goย intoย mocks/mock_X.go.

This way, theย mockgenย call is close to the actual interfaces, while avoiding the overhead of separate calls and destination files for each interface.

Using argument matchers

Sometimes, you donโ€™t care about the specific arguments a mock is called with. Withย GoMock, a parameter can be expected to have a fixed value (by specifying the value in the expected call) or it can be expected to match a predicate, called aย Matcher. Matchers are used to represent ranges of expected arguments to a mocked method. The following matchers are pre-defined inย GoMock:

  • gomock.Any(): matches any value (of any type)
  • gomock.Eq(x): uses reflection to match values that areย DeepEqualย toย x
  • gomock.Nil(): matchesย nil
  • gomock.Not(m): (whereย mย is a Matcher) matches values not matched by the matcherย m
  • gomock.Not(x): (whereย xย isย notย a Matcher) matches values notย DeepEqualย toย x

For example, if we donโ€™t care about the value of the first argument toย Do, we could write:

mockDoer.EXPECT().DoSomething(gomock.Any(), "Hello GoMock")

GoMockย automatically converts arguments that areย notย of typeย Matcherย toย Eqย matchers, so the above call is equivalent to:

mockDoer.EXPECT().DoSomething(gomock.Any(), gomock.Eq("Hello GoMock"))

You can define your own matchers by implementing theย gomock.Matcherย interface:

gomock/matchers.goย (excerpt)

type Matcher interface { Matches(x interface{}) bool String() string }

Theย Matchesย method is where the actual matching happens, whileย Stringย is used to generate human-readable output for failing tests. For example, a matcher checking an argumentโ€™s type could be implemented as follows:

match/oftype.go

package match

import (
    "reflect"
    "github.com/golang/mock/gomock"
)

type ofType struct{ t string }

func OfType(t string) gomock.Matcher {
    return &ofType{t}
}

func (o *ofType) Matches(x interface{}) bool {
    return reflect.TypeOf(x).String() == o.t
}

func (o *ofType) String() string {
    return "is of type " + o.t
}

We can then use our custom matcher like this:

// Expect Do to be called once with 123 and any string as parameters, and return nil from the mocked call.
mockDoer.EXPECT().
    DoSomething(123, match.OfType("string")).
    Return(nil).
    Times(1)

Weโ€™ve split the above call across multiple lines for readability. For more complex mock calls this is a handy way of making the mock specification more readable. Note that in Go we have to put the dot at theย endย of each line in a sequence of chained calls. Otherwise, the parser will consider the line ended and weโ€™ll get a syntax error.

Asserting call order

The order of calls to an object is often important.ย GoMockย provides a way to assert that one call must happen after another call, theย .Afterย method. For example,

callFirst := mockDoer.EXPECT().DoSomething(1, "first this")
callA := mockDoer.EXPECT().DoSomething(2, "then this").After(callFirst)
callB := mockDoer.EXPECT().DoSomething(2, "or this").After(callFirst)

specifies thatย callFirstย must occur before eitherย callAย orย callB.

GoMockย also provides a convenience functionย gomock.InOrderย to specify that the calls must be performed in the exact order given. This is less flexible than usingย .Afterย directly, but can make your tests more readable for longer sequences of calls:

gomock.InOrder(
    mockDoer.EXPECT().DoSomething(1, "first this"),
    mockDoer.EXPECT().DoSomething(2, "then this"),
    mockDoer.EXPECT().DoSomething(3, "then this"),
    mockDoer.EXPECT().DoSomething(4, "finally this"),
)

Underย the hood,ย InOrderย usesย .Afterย to chain the calls in sequence.

Specifying mock actions

Mock objects differ from real implementations in that they donโ€™t implement any of their behavior โ€” all they do is provide canned responses at the appropriate moment and record their calls. However, sometimes you need your mocks to do more than that. Here,ย GoMockโ€˜sย Doย actions come in handy. Any call may be decorated with an action by callingย .Doย on the call with a function to be executed whenever the call is matched:

mockDoer.EXPECT().
    DoSomething(gomock.Any(), gomock.Any()).
    Return(nil).
    Do(func(x int, y string) {
        fmt.Println("Called with x =",x,"and y =", y)
    })

Complex assertions about the call arguments can be written insideย Doย actions. For example, if the first (int) argument ofย DoSomethingย should be less than or equal to the length of the second (string) argument, we can write:

mockDoer.EXPECT().
    DoSomething(gomock.Any(), gomock.Any()).
    Return(nil).
    Do(func(x int, y string) {
        if x > len(y) {
            t.Fail()
        }
    })

The same functionality couldย notย be implemented using custom matchers, since we areย relatingย the concrete values, whereas matchers only have access to one argument at a time.

Summary

In this post, weโ€™ve seen how to generate mocks usingย mockgenย and how to batch mock generation usingย go:generateย comments and theย go generateย tool. Weโ€™ve covered the expectation API, including argument matchers, call frequency, call order andย Do-actions.

If you have any questions or if you feel that thereโ€™s something missing or unclear, please donโ€™t hesitate to let me know in the comments!

@rajholla
Copy link
Copy Markdown

Very well documented . Thank you ๐Ÿ™

@JamalYusuf
Copy link
Copy Markdown

Thanks for writing this, helped a ton.

@jahanarun
Copy link
Copy Markdown

This helped me, thanks!

@nade-harlow
Copy link
Copy Markdown

This helps a lot. Thanks!

@lkslts64
Copy link
Copy Markdown

Helped a lot!

@luwang-vmware
Copy link
Copy Markdown

It is really helpful to the new learners. Thanks very much indeed.

@eatwithforks
Copy link
Copy Markdown

eatwithforks commented May 16, 2022

Hi, where would you define the function for DoSomething? I tried adding it to doer.go and ran user_test.go with the stub commented out to see the test call the live function.

package doer

import "fmt"

//go:generate mockgen -destination=../mocks/mock_doer.go -package=mocks github.com/sgreben/testing-with-gomock/doer Doer

type Doer interface {
	DoSomething(int, string) error
}

func DoSomething(int, string) error {
	fmt.Println("doing stuff")
	return nil
}
func TestUse(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	mockDoer := mocks.NewMockDoer(mockCtrl)
	testUser := &user.User{Doer: mockDoer}

	// Expect Do to be called once with 123 and "Hello GoMock" as parameters, and return nil from the mocked call.
	//mockDoer.EXPECT().DoSomething(123, "Hello GoMock").Return(nil).Times(1)

	testUser.Use()
}

I get this error message:

    mock_doer.go:35: Unexpected call to *mocks.MockDoer.DoSomething([123 Hello GoMock]) at /Code/public/testing-with-gomock/user/user.go:10 because: there are no expected calls of the method "DoSomething" for that receiver

Reason I'm asking this is because I understand the DoSomething stub works but want to see it go through the real function without the stub.

cc @thiagozs

@DecodeWorms
Copy link
Copy Markdown

@eatwithforks ,it seems the DoSomething() has not been mocked, did you check the mocks package to confirm if Dosomething() is mocked or defined on a MockDoer?

@charleszheng44
Copy link
Copy Markdown

nice doc! Thanks!

@IamNator
Copy link
Copy Markdown

Thanks a trillion.

@LixvYang
Copy link
Copy Markdown

LixvYang commented Apr 7, 2023

nice!!!

@xyombo
Copy link
Copy Markdown

xyombo commented Jul 22, 2023

thanks, helpful!!

are there some ways to mock interfaces without source code ?

@Vractos
Copy link
Copy Markdown

Vractos commented Aug 4, 2023

Thanks, very helpful!

@ankitkumar5422
Copy link
Copy Markdown

Can you tell me How i can mock the AWS S3 service Collection in go for Unit test cases

@thiagozs
Copy link
Copy Markdown
Author

thiagozs commented Mar 6, 2024

@yshngg
Copy link
Copy Markdown

yshngg commented Jul 24, 2024

Very helpful! Thanks! ๐Ÿ˜Š

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