A Quick Guide to gomock
TL;DR
gomock is the official Go mocking library for unit tests, useful when dependencies such as network requests, databases or file I/O are complex and hard to call directly. This tutorial covers matchers (Any, Nil, Not, Eq), return values (Do, Return, DoAndReturn), call counts (Times) and ordering (InOrder), and how to write mockable code.

1 gomock overview
The previous article, Go Test: Unit Testing in Go, covered common unit testing techniques in Go, including subtests, table-driven tests, helpers, network testing and benchmarks. This article introduces a new testing technique: mock/stub testing. It comes in handy when the function or object under test has complex dependencies, some of which cannot be created directly, such as database connections or file I/O. In short, mock objects are used to simulate the behavior of dependencies.
GoMock is a mocking framework for the Go programming language. It integrates well with Go’s built-in testing package, but can be used in other contexts too.
gomock is the official mocking framework, and it also ships with the mockgen tool, which helps generate the mock code.
Install it with the following commands:
go get -u github.com/golang/mock/gomock
go get -u github.com/golang/mock/mockgen
2 A simple demo
// db.go
type DB interface {
Get(key string) (int, error)
}
func GetFromDB(db DB, key string) int {
if value, err := db.Get(key); err == nil {
return value
}
return -1
}
Suppose DB is the part of the code that talks to the database (simulated with a map here), and you cannot create a real database connection in your tests. If we want to test the logic inside GetFromDB, we need to mock the DB interface.
Step 1: use mockgen to generate db_mock.go. You typically pass three arguments: the source file containing the interface to be mocked (source), the destination file for the generated code (destination), and the package name (package).
$ mockgen -source=db.go -destination=db_mock.go -package=main
Step 2: create db_test.go and write a test case.
func TestGetFromDB(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish() // asserts whether DB.Get() was called
m := NewMockDB(ctrl)
m.EXPECT().Get(gomock.Eq("Tom")).Return(100, errors.New("not exist"))
if v := GetFromDB(m, "Tom"); v != -1 {
t.Fatal("expected -1, but got", v)
}
}
- This test has two purposes. First,
ctrl.Finish()asserts whetherDB.Get()was called — if it wasn’t, the mocks that follow would be meaningless. - Second, it verifies that the logic of
GetFromDB()is correct (ifDB.Get()returns an error, thenGetFromDB()returns -1). NewMockDB()is defined indb_mock.go, generated automatically by mockgen.
The final project structure looks like this:
project/
|--db.go
|--db_mock.go // generated by mockgen
|--db_test.go
Run the tests:
$ go test . -cover -v
=== RUN TestGetFromDB
--- PASS: TestGetFromDB (0.00s)
PASS
coverage: 81.2% of statements
ok example 0.008s coverage: 81.2% of statements
3 Stubbing
In the example above, when Get() is called with the argument Tom, it returns an error. This is called stubbing. Specifying exact arguments and return values is the simplest way to stub. Beyond that, checking call counts, enforcing call order, and setting return values dynamically are also commonly used.
3.1 Arguments (Eq, Any, Not, Nil)
m.EXPECT().Get(gomock.Eq("Tom")).Return(0, errors.New("not exist"))
m.EXPECT().Get(gomock.Any()).Return(630, nil)
m.EXPECT().Get(gomock.Not("Sam")).Return(0, nil)
m.EXPECT().Get(gomock.Nil()).Return(0, errors.New("nil"))
Eq(value)matches values equal to value.Any()matches any argument.Not(value)matches anything other than value.Nil()matches nil values.
3.2 Return values (Return, DoAndReturn)
m.EXPECT().Get(gomock.Not("Sam")).Return(0, nil)
m.EXPECT().Get(gomock.Any()).Do(func(key string) {
t.Log(key)
})
m.EXPECT().Get(gomock.Any()).DoAndReturn(func(key string) (int, error) {
if key == "Sam" {
return 630, nil
}
return 0, errors.New("not exist")
})
Returnreturns fixed values.Doruns an action when the mocked method is called, ignoring the return values.DoAndReturnlets you control the return values dynamically.
3.3 Call counts (Times)
func TestGetFromDB(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
m := NewMockDB(ctrl)
m.EXPECT().Get(gomock.Not("Sam")).Return(0, nil).Times(2)
GetFromDB(m, "ABC")
GetFromDB(m, "DEF")
}
Times()asserts the number of times the mocked method is called.MaxTimes()sets the maximum number of calls.MinTimes()sets the minimum number of calls.AnyTimes()allows any number of calls (including zero).
3.4 Call order (InOrder)
func TestGetFromDB(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish() // asserts whether DB.Get() was called
m := NewMockDB(ctrl)
o1 := m.EXPECT().Get(gomock.Eq("Tom")).Return(0, errors.New("not exist"))
o2 := m.EXPECT().Get(gomock.Eq("Sam")).Return(630, nil)
gomock.InOrder(o1, o2)
GetFromDB(m, "Tom")
GetFromDB(m, "Sam")
}
4 How to write mockable code
Writing testable code is just as important as writing good test cases. So how do you write mockable code?
- Mocking works on interfaces, so abstract your dependencies behind interfaces instead of depending on concrete classes directly.
- Don’t create instances directly; use dependency injection to reduce coupling.
In software engineering, dependency injection means giving a caller the things it needs. A “dependency” is something that can be used by a method. With dependency injection, the caller no longer uses a “dependency” directly; instead, the dependency is “injected”. “Injection” refers to the process of passing a dependency to the caller. Only after injection does the caller use the dependency. Passing dependencies to the caller instead of letting the caller obtain them itself is the fundamental requirement of this design. — Dependency injection - Wikipedia
If GetFromDB() looked like this:
func GetFromDB(key string) int {
db := NewDB()
if value, err := db.Get(key); err == nil {
return value
}
return -1
}
a mock of the DB interface would have no effect on what happens inside GetFromDB(), so code written this way cannot be tested. But if the interface db DB is passed into GetFromDB() as a parameter, you can easily pass in a mock object.
The Chinese original of this article is available at geektutu.com/post/quick-gomock.html.
Found this helpful? Buy me a coffee ☕
Comments