Skip to content

Instantly share code, notes, and snippets.

@jbreiding
Forked from thiagozs/gomock.md
Created October 14, 2021 22:01
Show Gist options
  • Save jbreiding/00fbd00bb3370f75f2b6882ad24d7ed6 to your computer and use it in GitHub Desktop.
Save jbreiding/00fbd00bb3370f75f2b6882ad24d7ed6 to your computer and use it in GitHub Desktop.

Revisions

  1. @thiagozs thiagozs revised this gist Oct 16, 2018. 1 changed file with 24 additions and 24 deletions.
    48 changes: 24 additions & 24 deletions gomock.md
    Original file line number Diff line number Diff line change
    @@ -20,7 +20,7 @@ Contents
    * [Using argument matchers](#using-argument-matchers)
    * [Asserting call order](#asserting-call-order)
    * [Specifying mock actions](#specifying-mock-actions)
    * [Summary](#summary)
    * [Summary](#summary)

    Installation
    ------------
    @@ -43,10 +43,10 @@ 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
    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.

    @@ -72,7 +72,7 @@ type User struct {
    Doer doer.Doer
    }

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

    @@ -101,7 +101,7 @@ We start by creating a directory `mocks` that will contain our mock implementa

    mkdir -p mocks

    mockgen -destination=mocks/mock\_doer.go -package=mocks github.com/sgreben/testing-with-gomock/doer Doer
    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:

    @@ -128,37 +128,37 @@ import (

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

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

    // NewMockDoer creates a new mock instance
    func NewMockDoer(ctrl \*gomock.Controller) \*MockDoer {
    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
    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)
    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)
    func (_mr *MockDoerMockRecorder) DoSomething(arg0, arg1 interface{}) *gomock.Call {
    return _mr.mock.ctrl.RecordCall(_mr.mock, "DoSomething", arg0, arg1)
    }
    ```

    @@ -193,14 +193,14 @@ That’s it – we’ve now specified our first mock call! Here’s the complete
    `user/user_test.go`

    ```go
    package user\_test
    package user_test

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

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

    @@ -232,7 +232,7 @@ We might also want to assert that the value returned by the `Use` method is in

    `user/user_test.go`
    ```go
    func TestUseReturnsErrorFromDo(t \*testing.T) {
    func TestUseReturnsErrorFromDo(t *testing.T) {
    mockCtrl := gomock.NewController(t)
    defer mockCtrl.Finish()

    @@ -263,7 +263,7 @@ In our example, we can add a `go:generate` comment just below the `package` 
    ```go
    package doer

    //go:generate mockgen -destination=../mocks/mock\_doer.go -package=mocks github.com/sgreben/testing-with-gomock/doer 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
    @@ -334,11 +334,11 @@ func OfType(t string) gomock.Matcher {
    return &ofType{t}
    }

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

    func (o \*ofType) String() string {
    func (o *ofType) String() string {
    return "is of type " + o.t
    }
    ```
  2. @thiagozs thiagozs created this gist Oct 16, 2018.
    418 changes: 418 additions & 0 deletions gomock.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,418 @@
    [Testing with GoMock: A Tutorial](https://blog.codecentric.de/en/2017/08/gomock-tutorial/ "Testing with GoMock: A Tutorial")
    ============================================================================================================================

    08/16/17 by  Sergey Grebenshchikov

    [No Comments](https://blog.codecentric.de/en/2017/08/gomock-tutorial/#comments "Testing with GoMock: A Tutorial")

    This is a quick tutorial on how to test code using the [GoMock](https://github.com/golang/mock) mocking library and the standard library testing package [`testing`](http://godoc.org/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](https://github.com/sgreben/testing-with-gomock).

    Contents
    --------

    * [Installation](#installation)
    * [Basic Usage](#basic-usage)
    * [Using _GoMock_ with `go:generate`](#using-gomock-with-gogenerate)
    * [Using argument matchers](#using-argument-matchers)
    * [Asserting call order](#asserting-call-order)
    * [Specifying mock actions](#specifying-mock-actions)
    * [Summary](#summary)

    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:

    ```txt
    '-- 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`:

    ```txt
    '-- 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`

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

    ```go
    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`

    ```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:

    ```sh
    $ 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`
    ```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](https://blog.carlmjohnson.net/post/2016-11-27-how-to-use-go-generate/).

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

    `doer/doer.go`

    ```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

    ```sh
    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`

    ```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:

    ```go
    // 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,

    ```go
    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:

    ```go
    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](https://github.com/golang/mock/blob/master/gomock/call.go#L256-) 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:

    ```go
    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:

    ```go
    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!