Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money

At work, many companies demand efficiency and automated testing

When you’re actually in the ground, it turns out that you’re going to do unit testing, automated testing, maybe the current service is going to rely on other services’ data, interfaces, etc

During the single test or automation process, the test may fail or block due to other services or environmental factors

This is a problem that has to be solved, and we can use golang’s built-in mock tool to do this. We can pile data where necessary, mock data

What’s a gomock?

Mock data is an official provided framework for mock data

The Mockgen tool was also provided to help us generate test code

The project address on Github is github.com/golang/mock

Here’s how gomock is officially introduced:

Gomock is a mocking framework for the Go programming language. It integrates well with Go’s built-in test suite, but can also be used in other environments.

How do I use Gomock?

Gomock is also very easy to use, starting with go get the corresponding tools gomock and Mockgen

go get -u github.com/golang/mock/gomock
go get -u github.com/golang/mock/mockgen
Copy the code

You can write a demo to do this

The directory structure looks like this

├── Go, do, do, do, do, do, do, do, do, do, do, do, do, do, do, do, do, do, do, do myuser_test.goCopy the code

  • Mock_myfunc.go is generated using the Mockgen tool
  • Myfunc.go is mainly used to simulate the underlying implementation of the call
  • Myuser. go basically calls the interface inside myfunc. Go
  • Myuser_test. go is the corresponding single test file

myfunc.go

  • Write an interface that has aGetInfo() stringMethod, simulation to obtain information
package myfunc

type MyFunc interface {
	GetInfo() string
}
Copy the code

myuser.go

  • Call the method in myfunc.go and call the interface to get the information
package myfunc

func getUser(m MyFunc) string {
	user := m.GetInfo()
	return user
}
Copy the code

Mock file generation

mock_myfunc.go

We did not write this file ourselves, but generated it using the Mockgen tool as follows:

Execute the following statement in the parent directory of myfunc.go to generate the mock file by filling in the source source and target files

mockgen -source=myfunc.go -destination=mock_myfunc.go
Copy the code

Take a look at the Mockgen help, and there are other parameters you can use

# mockgenmockgen has two modes of operation: source and reflect. Source mode generates mock interfaces from a source file. It is enabled by using the -source flag. Other flags that may be useful in this mode are -imports and -aux_files. Example: mockgen -source=foo.go [other options] Reflect mode generates mock interfaces by building a program that uses reflection  to understand interfaces. It is enabled by passing two non-flag arguments: an import path, and a comma-separated list of symbols. Example: mockgen database/sql/driver Conn,Driver -aux_files string (source mode) Comma-separated pkg=path pairs of auxiliary Go source files. -build_flags string (reflect mode) Additional flags for go build. -copyright_file string Copyright file used to add copyright header -debug_parser Print out parser results only. -destination string Output file; defaults to stdout. -exec_only string (reflect mode) If set, execute this reflection program. -imports string (source mode) Comma-separated name=path pairs of explicit imports to use. -mock_names string Comma-separated interfaceName=mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix. -package string Package of the generated code; defaults to the package of the input with a 'mock_' prefix. -prog_only (reflect mode) Only generate the reflection program; write it to stdout and exit. -self_package string The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the  mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude. -source string (source mode) Input Go source file; enables source mode. -version Print version. -write_package_comment Writes package documentation comment (godoc) if true. (default true) 2021/10/30 16:43:25 Expected exactly two argumentsCopy the code

In general, it is more commonly used

  • -sourceThe source file
  • -destinationThe target file
  • -importsDependent packages that need to be imported
  • -build_flagsParameters passed to the build tool
  • -aux_filesThe interface file is appended if there is more than one file
  • -packageSet the package name of the mock file. If this is not set, the package name of the mock file defaults toMock_ Enter the package name of the file

The mock file generated from the above instruction looks like this:

  • NewMockMyFunc

Create a new mock instance

  • EXPECT

An object that allows the caller to indicate the intended use

  • GetInfo

The basic method of the mock, which is the method we need to mock

How to use it

myuser_test.go

  • The single test file corresponding to myUser. go uses the mock method
package myfunc

import (
	"fmt"
	"testing"
	gomock "github.com/golang/mock/gomock"
)

func Test_getUser(t *testing.T) {
	mockCtl := gomock.NewController(t)
	mockMyFunc := NewMockMyFunc(mockCtl)
	mockMyFunc.EXPECT().GetInfo().Return("xiaomotong")
	v := getUser(mockMyFunc)
	if v == "xiaomotong" {
		fmt.Println("get user right!")}else {
		t.Error("get error user")}}Copy the code

If you don’t know the difference, let’s look at how to write a single test without a mock

package myfunc

import (
	"fmt"
	"testing"
	gomock "github.com/golang/mock/gomock"
)

func Test_getUser(t *testing.T) {
    m := myfunc.CreateMyFunc() // Create your own object
	v := getUser(m)
	if v == "xiaomotong" {
		fmt.Println("get user right!")}else {
		t.Error("get error user")}}Copy the code

M := myfunc.CreateMyFunc() = myfunc.CreateMyFunc() = myfunc

However, creating MyFunc objects at this point is not yet coded due to external dependencies, but it does not block our unit tests

You can mock a MyFunc object and set the return value to mock it. For example:

mockCtl := gomock.NewController(t)
mockMyFunc := NewMockMyFunc(mockCtl)
mockMyFunc.EXPECT().GetInfo().Return("xiaomotong")
Copy the code

The result of executing the above code is as follows:

> go testget user right! PASS ok mygomock/myfunc is 0.427 sCopy the code

Interested friends can use it, use more will be more familiar with

What are the benefits of gomock?

  • Gomock implements a fairly complete interface-based Mock feature that integrates well with Golang’s built-in Testing package and can be used in other testing environments
  • It’s cheap and easy to learn

Tools need to be used, to play its value, need to use it

Welcome to like, follow and favorites

Friends, your support and encouragement, I insist on sharing, improve the quality of the power

All right, that’s it for this time

Common technology is open, our mentality, should be more open. Embrace change, live in the sun, and strive to move forward.

I am Nezha, welcome to like, see you next time ~