Preface
Good use of tools in go development can greatly facilitate standardized development, the first thing you need to do is to standardize and test.
- Generally speaking, usage habits are a key gofmt goimports must be used. Go vet golangci-link assists in checking semantics and correcting them.
- Complete the necessary basic use cases, form-based single go test, and use gomonkey if external
After the above two points are completed, we can say that the basic specification requirements have been completed.
Basics
# standard doc look up go doc cmd/gofmt # go version update go get -u brew upgrade go
Format and Code Specification
// Basic format updates // Gofmt, most of the formatting problems can be solved by gofmt, gofmt automatically formats the code to ensure that all go codes are consistent with the officially recommended format, so all formatting problems are based on the results of gofmt. go fmt . go help fmt // Including fmt will not optimize the reference package format, it must be applied // goimports, this tool adds automatic deletion and introduction of packages based on gofmt. goimports -w . // go vet, vet tool can help us to analyze all kinds of problems in our source code statically, such as redundant code, the logic of early return, whether struct tag meets the standard, etc. Perform code static analysis before compiling. go vet . // golint, a tool like jslint in javascript, is designed to detect irregularities in code. // https://github.com/golangci/golangci-lint golangci-lint run -c ~/.golangci.yaml . golangci-lint version golangci-lint --help golangci-lint run . golangci-lint run -c ~/.golangci.yaml $ProjectFileDir$/...
Single Test
base test
- Example (actually using an ide right-click goland test for function package)
go test -v
The code for calc.go is as follows:
package main func Add(a int, b int) int { return a + b } func Mul(a int, b int) int { return a * b }
Then calc_ Test. The test cases in go can be written as follows:
package main import "testing" func TestAdd(t *testing.T) { if ans := Add(1, 2); ans != 3 { t.Errorf("1 + 2 expected be 3, but %d got", ans) } assert.Equal(t, add(1, 2), 3, "Add Error!") if ans := Add(-10, -20); ans != -30 { t.Errorf("-10 + -20 expected be -30, but %d got", ans) } }
- The test case name is generally named Test plus the method name to be tested.
- There is one and only one parameter for the test, t *test here. T.
- The parameter of the benchmark is *test. B, the parameter of TestMain is *testing.M type.
The go test -v, -v parameter displays the test results for each use case, and the -cover parameter can view coverage.
If you only want to run one of these cases, such as TestAdd, you can specify it with the -run parameter, which supports wildcards*, and some regular expressions, such as ^, $.
$ go test -run TestAdd -v === RUN TestAdd --- PASS: TestAdd (0.00s) PASS ok example 0.007s
setup and teardown
If the logic before and after each test case is the same in the same test file, it is typically written in the setup and teardown functions. For example, the object to be tested needs to be instantiated before execution. If the object is complex, it is appropriate to extract this part of the logic. After execution, you may do some work on resource recycling classes, such as closing network connections, releasing files, and so on. The standard library testing provides such a mechanism:
func setup() { fmt.Println("Before all tests") } func teardown() { fmt.Println("After all tests") } func Test1(t *testing.T) { fmt.Println("I'm test1") } func Test2(t *testing.T) { fmt.Println("I'm test2") } func TestMain(m *testing.M) { setup() code := m.Run() teardown() os.Exit(code) }
- In this test file, there are two test cases, Test1 and Test2.
- If the test file contains the function TestMain, the generated test will call TestMain(m) instead of running the test directly.
- Calling m.Run() triggers the execution of all test cases and uses os.Exit() processes the returned status code, if it is not zero, indicating a case failure.
- So you can do some extra setup and teardown work before and after calling m.Run().
Executing go test will output
$ go test Before all tests I'm test1 I'm test2 PASS After all tests ok example 0.006s
come from https://geektutu.com/post/quick-go-test.html
go monkey
introduce
monkey Is a very common piling tool in Go unit testing. It rewrites the executable file in assembly language at runtime, jumps the implementation of the objective function or method to the piling implementation, and works like hot patch.
The monkey library is powerful, but here are some things to note when using it:
- Money does not support inline functions, so you need to turn off the Go Language inline optimization through the command line parameter -gcflags=-l when testing.
- Money is not thread safe, so don't use it in concurrent unit tests.
All the code in the example is here: https://github.com/fishingfly/gomonkey_examples
Execute unit tests:
Note: The -gcflags=-l parameter is added here to prevent inline optimization.
go test -run=TestMyFunc -v -gcflags=-l go test -gcflags=all=-l
convey can be used
convey.Convey("case Name", t, func() { Specific Tests case convey.So(...) //Assertion })
mock method and member method
// Method patch patchDeleteKey := gomonkey.ApplyMethod(reflect.TypeOf(&clients.RedisClientImpl{}), "DeleteKey", func(c *clients.RedisClientImpl, ctx context.Context, key string) (int, error) { return 1, nil }) defer patchDeleteKey.Reset() patchConfig := gomonkey.ApplyFunc(config.GetJSON, func(key string, val interface{}) error { log.Error("lalalalala") return nil }) defer patchConfig.Reset() // Interface method patch patchBatchGetAppInfo := gomonkey.ApplyMethod(reflect.TypeOf(&clients.GameClientImpl{}), "BatchGetAppInfo", func(c *clients.GameClientImpl, ctx context.Context, appids []string) (map[string]*pb.StAppInfoResult, error) { return map[string]*pb.StAppInfoResult{ "11059723": &pb.StAppInfoResult{ AppInfo: &pb.StAppInfo{ Appid: "11059307", Platform: &wrappers.Int32Value{Value: 0}, }, }, }, nil }) defer patchBatchGetAppInfo.Reset() gameManager := NewGameManager() ctx := context.TODO() req := &pb.GetGameHotListReq{ PlatformId: "1002", DevicePlatform: &wrappers.Int32Value{Value: 1}, } result, err := gameManager.GetGameHotList(ctx, req, 3) assert.Equal(t, 6, len(result)) assert.Nil(t, err)
mock global variable ApplyGlobalVar
// Address of @param[in] target global variable // Piles for @param[in] double global variable func ApplyGlobalVar(target, double interface{}) *Patches func (this *Patches) ApplyGlobalVar(target, double interface{}) *Patches
The global variable mock is simple, just look at the code:
var num = 10 func TestApplyGlobalVar(t *testing.T) { Convey("TestApplyGlobalVar", t, func() { Convey("change", func() { patches := ApplyGlobalVar(&num, 150) defer patches.Reset() So(num, ShouldEqual, 150) }) Convey("recover", func() { So(num, ShouldEqual, 10) }) }) }
mock function variable ApplyFuncVar
// Address of @param[in] target function variable // Definition of @param[in] double stake function func ApplyFuncVar(target, double interface{}) *Patches func (this *Patches) ApplyFuncVar(target, double interface{}) *Patches
var funcVar = func(a,b int) (int,error) { if a < 0 && b < 0 { errmsg := "a<0 && b<0" return 0, fmt.Errorf("%v",errmsg) } return a+b, nil } func TestMockFuncVar(t *testing.T) { Convey("TestMockFuncVar", t, func() { gomonkey.ApplyFuncVar(&funcVar, func(a,b int)(int,error) { return a-b, nil }) v, err := funcVar(20, 5) So(v, ShouldEqual, 15) So(err, ShouldBeNil) }) }
Author: 123 archu
Links: https://www.jianshu.com/p/adb8493f44e1
Enclosure
golangci-lint installation
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.43.0 https://golangci-lint.run/usage/install/ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.40.0 go get -u github.com/golangci/golangci-lint/cmd/golangci-lint@v1.44.0 # Note version issues # You can install with brew
https://github.com/golangci/golangci-lint
goland Configuration golinter
-
Install go linter
-
Configure go linter
-
Configuring golanglint-ci in File Watcher
run -c ~/.golangci.yaml $ProjectFileDir$/...
Usage
Open a project, insert an empty line, save it, and in the output bar below, show the project's code checking problems, and automatically fix the scanned code problems
configuration file
https://git.woa.com/standards/go/blob/master/.golangci.yml
run -c ~/.golangci.yaml P r o j e c t F i l e D i r ProjectFileDir ProjectFileDir/...
.golangci.yml
# Full Version https://golangci-lint.run/usage/configuration/ linters-settings: funlen: lines: 80 statements: 80 goconst: min-len: 2 min-occurrences: 2 gocyclo: min-complexity: 20 goimports: # revive: confidence: 0 govet: check-shadowing: true lll: line-length: 120 errcheck: check-type-assertions: true gocritic: enabled-checks: - nestingReduce - commentFormatting settings: nestingReduce: bodyWidth: 5 linters: disable-all: true enable: - deadcode - funlen - goconst - gocyclo - gofmt - ineffassign - staticcheck - structcheck - typecheck - goimports - revive - gosimple - govet - lll - rowserrcheck - errcheck - unused - varcheck - sqlclosecheck - gocritic run: timeout: 20m issues: exclude-use-default: true include: - EXC0004 # govet (possible misuse of unsafe.Pointer|should have signature) - EXC0005 # staticcheck ineffective break statement. Did you mean to break out of the outer loop - EXC0012 # revive exported (method|function|type|const) (.+) should have comment or be unexported - EXC0013 # revive package comment should be of the form "(.+)... - EXC0014 # revive comment on exported (.+) should be of the form "(.+)..." - EXC0015 # revive should have a package comment, unless it's in another file for this package exclude-rules: - path: _test\.go linters: - funlen - linters: - staticcheck text: "SA6002: argument should be pointer-like to avoid allocations" # sync.pool.Put(buf), slice `var buf []byte` will tiger this - linters: - structcheck text: "Store` is unused" - linters: - lll source: "^//go:generate " # Exclude lll issues for long lines with go:generate max-same-issues: 0 new: false max-issues-per-linter: 0 output: sort-results: true service: golangci-lint-version: 1.28.x