SoFunction
Updated on 2025-03-05

Detailed explanation of GoMock installation and use in go language

Install

GoMockIt is a Go framework. It integrates well with the built-in test package and provides flexibility when unit testing. As we know, unit testing code with external resources (databases, networks, and files) or dependencies is always cumbersome.

In order to use GoMock, we need to installgomockBag/golang/mock/gomockandmockgenCode Generation Tool/golang/mock/mockgen. Use this command line:

go get /golang/mock/gomock
go get /golang/mock/mockgen

The use of GoMock follows four basic steps:

  • usemockgenGenerate a mock object for the interface you want to simulate.
  • In the test section, create aand pass it to yoursmockThe constructor of the object to obtain amockObject.
  • Called on mockEXPECT()to set the expected value and return value.
  • Called on the analog controllerFinish()To assert the expectation of the mock object.

start

Let's create a folder like this (this code is executed under go1.16.15 version)

gomock_test
├── doer
│ └── 
├── mocks
│ └── mock_doer.go
└── user
  ├── 
  └── user_test.go

doer/

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

So here is the code we want to test when mocking the Doer interface.

user/

package user
import "gomock_test/doer"
const (
	filtered   = "OK"
	unfiltered = "spam"
	Nice       = "nice"
	Bad        = "bad"
)
type User struct {
	// struct while mocking the doer interface
	Doer 
}
// method Use using it
func (u *User) Use() error {
	return (123, "Hello GoMock")
}
func (u *User) SaySomething(num int) string {
	if num == 3 {
		return (unfiltered)
	}
	return (filtered)
}
type Student struct{}
func (s *Student) DoSomething(_ int, _ string) error {
	panic("not implemented") // TODO: Implement
}
func (s *Student) SaySomething(kata string) string {
	if kata == filtered {
		return Nice
	}
	return Bad
}

We willDoerSimulation in one packagemocksmiddle. We first create a directory containing our mock implementationmocks, and thendoerRun on the packagemockgen

mockgen -destination=../mocks/mock_doer.go -package=mocks gomock_test/doer Doer

NOTE: When executing this step, an error will be reported: Failed to format generated source code: mocks/mock_doer.go:5:15: expected ';', found '.’ At this time, we only need to copy the printed code to our corresponding file.

Running mockgen for each package and interface is a utopia when there are a large number of interfaces/packages that need to be simulated. To alleviate this problem,mockgenCommand andgo:generatePut it together.

go:generate mockgen -destination=../mocks/mock_doer.go -package=mocks gomock_test/doer Doer

We have to create the directory mock ourselves, because GoMock won't do this for us, but will exit with an error.

  • destination=.../mocks/mock_doer.go: Put the generated mocks in this path.
  • -package=mocks: Put the generated mocks in this package
  • gomock_test/doer: Generate mocks for this package.
  • Doer: Generate mocks for this interface (required) because we need to specify which interface to generate mocks. (If necessary, multiple interfaces can be specified with a comma-separated list. For example, Doer1, Doer2)

Because we'remockgenThe call placed a file in our projectmocks/mock_doer.go. This is how it is generatedmockWhat the implementation looks like:

// Code generated by MockGen. DO NOT EDIT.
// Source: /timliudream/go-test/gomock_test/doer (interfaces: Doer)
// Package /timliudream/go-test/gomock_test/mocks is a generated GoMock package.
package mocks
import (
	gomock "/golang/mock/gomock"
	reflect "reflect"
)
// MockDoer is a mock of Doer interface.
type MockDoer struct {
	ctrl     *
	recorder *MockDoerMockRecorder
}
// MockDoerMockRecorder is the mock recorder for MockDoer.
type MockDoerMockRecorder struct {
	mock *MockDoer
}
// NewMockDoer creates a new mock instance.
func NewMockDoer(ctrl *) *MockDoer {
	mock := &MockDoer{ctrl: ctrl}
	 = &MockDoerMockRecorder{mock}
	return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockDoer) EXPECT() *MockDoerMockRecorder {
	return 
}
// DoSomething mocks base method.
func (m *MockDoer) DoSomething(arg0 int, arg1 string) error {
	()
	ret := (m, "DoSomething", arg0, arg1)
	ret0, _ := ret[0].(error)
	return ret0
}
// DoSomething indicates an expected call of DoSomething.
func (mr *MockDoerMockRecorder) DoSomething(arg0, arg1 interface{}) * {
	()
	return (, "DoSomething", ((*MockDoer)(nil).DoSomething), arg0, arg1)
}
// SaySomething mocks base method.
func (m *MockDoer) SaySomething(arg0 string) string {
	()
	ret := (m, "SaySomething", arg0)
	ret0, _ := ret[0].(string)
	return ret0
}
// SaySomething indicates an expected call of SaySomething.
func (mr *MockDoerMockRecorder) SaySomething(arg0 interface{}) * {
	()
	return (, "SaySomething", ((*MockDoer)(nil).SaySomething), arg0)
}

Next, we define an analog controller in the test. A simulation controller is responsible for tracking and asserting the expectations of its associated simulation objects. We can pass a*The value of type gives it the constructor to obtain a simulation controller and use it to construct aDoerSimulated object of the interface.

//core of gomock
mockCtrl := (t)
//used to trigger final assertions. if its ignored, mocking assertions will never fail
defer ()
// create a new mock object, passing the controller instance as parameter
// for a newly created mock object it will accept any input and outpuite
// need to define its behavior with the method expect
mockDoer := (mockCtrl)

Use parameter matcher

In GoMock, a parameter can be expected to have a fixed value or can be expected to match a predicate (called a matcher). A matcher is used to represent the expected parameter range of the method being simulated. The following matchers are predefined in Gomock:

  • () : Match any value (any type).
  • (x) : Use reflection to match values ​​equal to the x depth.
  • () : Match nil

user/user_test.go

package user
import (
	"fmt"
	"/golang/mock/gomock"
	"gomock_test/mocks"
	"testing"
)
func TestUse(t *) {
	//core of gomock
	mockCtrl := (t)
	//used to trigger final assertions. if its ignored, mocking assertions will never fail
	defer ()
	// create a new mock object, passing the controller instance as parameter
	// for a newly created mock object it will accept any input and outpuite
	// need to define its behavior with the method expect
	mockDoer := (mockCtrl)
	testUser := &User{Doer: mockDoer}
	//
	// Expect Do to be called once with 123 and "Hello GoMock" as parameters, and return nil from the mocked call.
	().DoSomething(123, "Hello GoMock").Return(nil).Times(1)
	(())
	()
}
func TestUser_SaySomething(t *) {
	mockCtrl := (t)
	defer ()
	mockDoer := (mockCtrl)
	user := User{
		Doer: mockDoer,
	}
	type args struct {
		num int
	}
	tests := []struct {
		name    string
		args    args
		want    string
		expect  func()
		wantErr bool
	}{
		{
			name: "Positive test case 1",
			expect: func() {
				().SaySomething("spam").Return("bad")
			},
			args:    args{num: 3},
			wantErr: false,
			want:    "bad",
		},
	}
	for _, tt := range tests {
		(, func(t *) {
			()
			if got := (); (got != ) !=  {
				("gott :", got)
				("() = %v, want %v", got, )
			}
		})
	}
}

And the result of the unit test will be like this:

=== RUN   TestUser_SaySomething
=== RUN   TestUser_SaySomething/Positive_test_case_1
--- PASS: TestUser_SaySomething (0.00s)
    --- PASS: TestUser_SaySomething/Positive_test_case_1 (0.00s)
PASS
ok      /tokopedia/go_learning/udemy/pzn/gomock_test/user     1.100s

Talk about experience

Unit testing can be performed without calling external dependencies. usemockingFramework can help us in many ways, building clean and lightweight unit tests. Combined with interface and dependency injection.

The above is the detailed explanation of the installation and use of GoMock in go language. For more information about Go language GoMock installation, please follow my other related articles!