This article introduces the basics of Gomock:
- Use the sample
- Making the document
- The source code comments
Use the sample
Gomock is a go simulation framework that integrates well with go’s built-in testing package and can be used in other contexts.
The installation
Go version < 1.16
GO111MODULE = on the go a get github.com/golang/mock/[email protected]Copy the code
Go version 1.16 +
Go install github.com/golang/mock/[email protected]Copy the code
The project structure
We prepare the following documents:
- Dal/Person.go, which corresponds to the layer in a typical project that interacts with the database or calls downstream RPC services.
- Service /group.go, corresponding to the service layer in general projects, is mainly used to process various business logic.
- Service /group_test.go, unit test file
Fill the content
Dal/Person. go: Suppose we have a table that stores the person information and use the Get(int) method to Get the corresponding person.
package dal
type Person interface {
Get(id int) string
}
Copy the code
Service /group.go: our business logic processing layer.
package service
import "gomockStudy/dal"
type Group struct {
person dal.Person
}
func (g *Group) GetPerson(id int) string {
return g.person.Get(id)
}
Copy the code
Generating mock files
Go back to the project root directory and execute the following command:
$mockgen -source=./dal/person.go -destination=./mock_gen/person_mock.go
Copy the code
After executing, we can see that there is an additional file: mock_gen/person_mock.go.
The mock file is generated with the following parameters:
- -source: specifies the interface file to mock.
- -destination: Sets the mock file name to be generated. If this parameter is not set, it is printed to standard output.
- -packge: specifies the registration of mock files. If this parameter is not set, it is
mock_
Prefix followed by file name.
We don’t have to worry about the details of the mock files generated, just how to use them.
The test case
service/group.go
package service import ( "github.com/golang/mock/gomock" mock_dal "gomockStudy/mock_gen" "testing" ) func TestGroup_GetPerson(t *testing.T) { ctl := gomock.NewController(t) mockPerson := mock_dal.NewMockPerson(ctl) mockPerson.EXPECT().Get(gomock.Any()).Return("person1").AnyTimes() group := Group{mockPerson} person := group.GetPerson(1) if person ! = "person1" { t.Errorf("group.GetPerson id = 1, result = %v", person) } person = group.GetPerson(2) if person ! = "person2" { t.Errorf("group.GetPerson id = 2, result = %v", person) } }Copy the code
gomock.NewController
: Controller represents the top-level control of the simulated ecosystem. It defines the mock objectScope, life cycleAnd theirexpect. The method of calling a Controller from multiple Goroutines is thread-safe. Each test should create a new Controller.mock_dal.NewMockPerson
Create a mock Person object.- Line 13:
EXPECT()
: returns an object that the caller can setExpected return value.Get()
: sets the input parameter and calls the methods in the mock object.gomock.Any()
Matches any input parameter.Return()
: Sets the return value.AnyTimes()
: an infinite number of calls, that is, the result will be returned no matter how many times the call is made.
Note: If the * testing.t object has been passed to the Controller when go 1.14+, Finish() is not called automatically.
test
Go back to the project root directory and execute the following command:
$go test. /service -- FAIL: TestGroup_GetPerson (0.00s) group_test.go:24: Group. GetPerson ID = 2, result = person1 FAIL gomockStudy/service 0.560s FAILCopy the code
As you can see, a line is expected to return person2, but person1 is returned. Modify line 13 in the test file:
mockPerson.EXPECT().Get(1).Return("person1")
mockPerson.EXPECT().Get(2).Return("person2")
Copy the code
The test success result can be obtained:
$go test./service ok gomockStudy/service 0.689sCopy the code
So far, we’ve learned how to use mock objects to help us unit test.
Let’s take a look at gomock’s Github page.
Git homepage
Run mockgen
Mockgen has two execution modes: Source and Reflect
Source mode
Source mode is used by using the -source parameter to generate the corresponding mock interface from the source code. In this mode, it is possible to use the -imports and -aux_files parameters.
mockgen -source=foo.go [other options]
Copy the code
Reflect mode
Reflection mode generates a mock interface by building a program that uses reflection to parse the interface. It is enabled by passing two non-flag arguments: the import path and a comma-separated list of symbols.
You can use “.” to indicate packages at the current path.
mockgen database/sql/driver Conn,Driver
# Convenient for `go:generate`.
mockgen . Conn,Driver
Copy the code
Flags
The mockgen command is used to generate source code for mock classes if the Go source file contains the interface to mock. It supports the following flags:
- -source: the file containing the interface to mock.
- -destination: writes the generated source code to the file. If this parameter is not set, the code is printed as standard output.
- -package: the package used to generate the source code of the mock class. If this parameter is not specified, the package name is
mock_
Connects to the package of the input file. - -imports: An explicit list of imports that should be used in the generated source code, specified as a comma-separated list of elements of the form foo=bar/baz, where bar/baz is the imported package and foo is the identifier used by the package in the generated source code.
- -aux_files: list of additional files to be queried, such as embedded interfaces defined in different files. It is specified as a comma-separated list of elements of the form foo=bar/baz, where bar/baz.go is the source file and foo is the package name of the file used by the -source file.
- -build_flags :(reflection mode only) flags are passed verbatim to the build.
- -mock_names: a list of custom names of generated mock objects. Looks like a comma-separated list of key-value pairs:
The Repository = MockSensorRepository, Endpoint = MockSensorEndpoint. Where, Repository is the interface name and MockSensorRepository is the name associated with the generated impersonation. If one of the interfaces does not specify a custom name, the default naming convention is used.
- – self_Package: indicates the complete package import path of the generated code. The purpose of this flag is to prevent import loops in generated code by trying to include its own package. This can happen if the mock’s package is set as one of its inputs (usually the main input) and the output is STdio, so mockGen cannot detect the final output package. Setting this flag tells Mockgen which import to exclude.
- -copyright_file: the copyright file used to add the copyright header to the generated source code.
- -debug_parser: prints only the results of the parser.
- -exec_only :(reflection mode) if set, the reflection program will be executed.
- -prog_only :(reflection mode) generates only reflection programs; Write it to stdout and exit.
- -write_package_comment: if true, write package documentation comments (godoc). (Default true)
Build away,
The example in the first video is a little bit more detailed than this one, so I’m not going to do it here.
Failed to modify message
When the matcher reports a failure, it prints the received value (Got) and the expected value (Want).
Got: [3]
Want: is equal to 2
Expected call at user_test.go:33 doesn't match the argument at index 1.
Got: [0 1 1 2 3]
Want: is equal to 1
Copy the code
Modify the Want
The Want value comes from the String() method of the matcher. If the default output of the matcher does not meet your needs, it can be modified as follows:
gomock.WantFormatter(
gomock.StringerFunc(func() string { return "is equal to fifteen" }),
gomock.Eq(15),
)
Copy the code
After the modification, the printed information of gomock.Eq(15) changes from is equal to 15 to is equal to fifteen.
Modify the Got
The Got value comes from the object’s String() method, if it is available. In some cases, the output of an object is difficult to read (for example, []byte), so it would be helpful to test printing it differently. The following command changes the format of the global offset table:
gomock.GotFormatterAdapter(
gomock.GotFormatterFunc(func(i interface{}) string {
return fmt.Sprintf("%02d", i)
}),
gomock.Eq(15),
)
Copy the code
If the received value is 3, it will be printed as 03.
Debugging Errors
Reflection vendoring error
cannot find package "."
... github.com/golang/mock/mockgen/model
Copy the code
If you encounter this error when using reflection mode and vendoring dependencies, there are three solutions:
- Use the source mode
- Import air import: import _ “making/golang/mock/mockgen/model”
- add
--build_flags=--mod=mod
Parameters.
This error is due to a change in the default behavior of the go command in recent versions.
The source code comments
Gomock’s source code is annotated with some information.
Execution order
By default, expected calls are not forced to run in any particular order.
Call order dependencies can be enforced by using InOrder and or call.after ().
Call.after () calls can create more diversified Call order dependencies, but InOrder is generally more convenient.
After
firstCall := mockObj.EXPECT().SomeMethod(1, "first")
secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)
mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)
Copy the code
InOrder
gomock.InOrder(
mockObj.EXPECT().SomeMethod(1, "first"),
mockObj.EXPECT().SomeMethod(2, "second"),
mockObj.EXPECT().SomeMethod(3, "third"),
)
Copy the code