Version 1.5 by chrisby on 2023/06/18 14:17

Show last authors
1 === Introduction ===
2
3 (% style="text-align: justify;" %)
4 In contrast to run-time mock generation, as seen for example in Java's 'mockito' library, Go uses a unique compile-time strategy. This technique results in the creation of tangible mock files that are directly integrated into the test code. The 'mockgen' library automates this process, providing an efficient means of simulating and handling dependencies during testing. This quick start guide provides a basic introduction of the 'mockgen' library, outlining the necessary steps for initial mock generation and its integration into the test workflow.
5
6
7 === Setup ===
8
9 {{code language="bash"}}
10 mkdir -p mockgen_example
11 cd mockgen_example
12 go mod init example.com/mockgen_example
13 go get github.com/golang/mock/gomock
14 go install github.com/golang/mock/mockgen@v1.6.0
15 {{/code}}
16
17 (% style="text-align: justify;" %)
18 The last command installs the 'mockgen' in 'GOPATH', which does the actual mock file generation.
19
20 === ===
21
22 === Source Code ===
23
24 (% style="text-align: justify;" %)
25 First, we need the dependency we want to mock, for this example 'NameProvider.go':
26
27 {{code language="go"}}
28 package mockgen_example
29
30 //go:generate mockgen -destination=./mock_nameProvider.go -package=mockgen_example . NameProvider
31
32 type NameProvider interface {
33 ProvideName() string
34 }
35 {{/code}}
36
37 (% style="text-align: justify;" %)
38 Go uses what are called 'build constraints' such as '~/~/go:generate ...' which execute special commands when the application is built. In this case, when 'go generate' is executed, the just installed 'mockgen' command is also executed to create the mock file. The parameters are intuitive: 'destination' defines the path and name of the mock file, 'package' defines the package to address when importing and using the mock file, the dot means the current directory and 'NameProvider' is the type of mock object. Now the mock file can be created by running it:
39
40 {{code}}
41 go generate ./...
42 {{/code}}
43
44 (% style="text-align: justify;" %)
45 The 'mock_nameProvider.go' file should now be generated for the appropriate path and package.
46
47 (% style="text-align: justify;" %)
48 The unit to be tested, 'Greeter', is simple for the sake of an example. It has a dependency of type 'NameProvider' into which the mock object can be injected, and its method 'Greet()' adds a "Hello " before the name provided by the 'NameProvider'. The code for 'Greeter.go' is:
49
50 {{code language="go"}}
51 package mockgen_example
52
53 type Greeter struct {
54 nameProvider NameProvider
55 }
56
57 func (n *Greeter) Greet() string {
58 return "Hello " + n.nameProvider.ProvideName()
59 }
60 {{/code}}
61
62 (% style="text-align: justify;" %)
63 Finally, here is the test code from 'Greeter_test.go':
64
65 {{code}}
66 package mockgen_example
67
68 import (
69 "github.com/golang/mock/gomock"
70 "testing"
71 )
72
73 func TestGreet(t *testing.T) {
74 ctrl := gomock.NewController(t)
75 nameProviderMock := NewMockNameProvider(ctrl)
76 defer ctrl.Finish()
77 greeter := Greeter{nameProviderMock}
78
79 nameProviderMock.EXPECT().ProvideName().Return("John")
80
81 greeting := greeter.Greet()
82 if greeting != "Hello John" {
83 t.Fail()
84 }
85 }
86 {{/code}}
87
88 (% style="text-align: justify;" %)
89 The first four lines of the test are just to create the mock object and inject it into 'greeter'. Next, the mock can be instructed to behave in a specific way appropriate to the test case, i.e. to simply return "john" when "ProvideName()" is called. Note that the test calls the 'NewMockNameProvider()' method directly from the 'mock_nameProvider.go' file.
90
91 === ===
92
93 === Running the Test ===
94
95 {{code language="bash"}}
96 go test ./...
97 # output should look like this:
98 # ok example.com/mockgen_example 0.001s
99 {{/code}}
100
101 === ===
102
103 === Ignore Mock Files in Git ===
104
105 (% style="text-align: justify;" %)
106 Generated code should not be part of the git history, and should therefore be ignored. Using the mock naming convention from above, a corresponding entry in the '.gitignore' file could be:
107
108 {{code}}
109 **/mock_*.go
110 {{/code}}