unit test code coverage of Golang

go由google在2009年11月发布,目前版本是1.11 。

GOROOT、GOPATH、GOBIN

配置这几个环境变量的时候要注意,如果是windows, 当前用户的配置会覆盖系统用户的配置,二者留一个即可。

GOPATH

go命令依赖一个重要的环境变量:$GOPATH
GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个目录的时候Windows是分号;
当有多个GOPATH时默认将go get获取的包存放在第一个目录下

$GOPATH目录约定有三个子目录

  • src存放源代码(比如:.go .c .h .s等) 按照golang默认约定,go run,go install等命令的当前工作路径(即在此路径下执行上述命令)。
  • pkg编译时生成的中间文件(比如:.a),此目录可自动生成
  • bin编译后生成的可执行文件(为了方便,可以把此目录加入到 PATH 变量中,如果有多个gopath,那么使用${GOPATH//://bin:}/bin添加所有的bin目录)。此目录可自动生成。

代码目录结构规划
GOPATH下的src目录就是接下来开发程序的主要目录,所有的源码都是放在这个目录下面,那么一般我们的做法就是一个目录一个项目,

例如: $GOPATH/src/mymath 表示mymath这个应用包或者可执行应用,这个根据package是main还是其他来决定,main的话就是可执行应用,其他的话就是应用包

查看GOROOT和GOPATH

$ go env
set GOARCH=amd64
set GOBIN=C:\bin
set GOCACHE=C:\Users\Administrator\AppData\Local\go-build
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=F:\gopath
set GOPROXY=
set GORACE=
set GOROOT=C:\Go
set GOTMPDIR=
set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\ADMINI~1\AppData\Local\Temp\go-build029955050=/tmp/go-build -gno-record-gcc-switches

Administrator@SC-201810221720 MINGW64 ~/Desktop

GOROOT

其实就是golang 的安装路径
当你安装好golang之后其实这个就已经有了

GOBIN

一般即为GOPATH目录下面的bin目录,也可以设置为其他目录。

go install编译存放路径。不允许设置多个路径。可以为空。为空时则遵循“约定优于配置”原则,可执行文件放在各自GOPATH目录的bin文件夹中(前提是:package main的main函数文件不能直接放到GOPATH的src下面。

go get

go get会做两件事:

  1. 从远程下载需要用到的包
  2. 执行go install

go get和git clone的区别是go get会下载依赖包的依赖包,git clone只能下载依赖包但依赖包的依赖包不会自动处理

#使用go get来下载依赖包,一般会保存到GOPATH的src目录里面
$ go run main.go
..\github.com\go-kit\kit\log\logfmt_logger.go:8:2: cannot find package "github.com/go-logfmt/logfmt" in any of:
        C:\Go\src\github.com\go-logfmt\logfmt (from $GOROOT)
        F:\gopath\src\github.com\go-logfmt\logfmt (from $GOPATH)

Administrator@SC-201810221720 MINGW64 /f/gopath/src/aph-go-service (master)
$ ls ../github.com/go-logfmt
ls: cannot access '../github.com/go-logfmt': No such file or directory

Administrator@SC-201810221720 MINGW64 /f/gopath/src/aph-go-service (master)
$ go get -v github.com/go-logfmt/logfmt
github.com/go-logfmt/logfmt (download)

Administrator@SC-201810221720 MINGW64 /f/gopath/src/aph-go-service (master)
$ ls ../github.com/go-logfmt/
logfmt/


go install

go install 会生成可执行文件直接放到bin目录下,当然这是有前提的
你编译的是可执行文件,如果是一个普通的包,会被编译生成到pkg目录下该文件是.a结尾

go的开发目录示例

go_project     // go_project为GOPATH目录
  -- bin
     -- myApp1  // 编译生成
     -- myApp2  // 编译生成
     -- myApp3  // 编译生成
  -- pkg
  -- src
     -- myApp1     // project1
        -- models
        -- controllers
        -- others
        -- main.go 
     -- myApp2     // project2
        -- models
        -- controllers
        -- others
        -- main.go 
     -- myApp3     // project3
        -- models
        -- controllers
        -- others
        -- main.go

开发环境配置

windows

  1. 下载go安装程序 下载地址:https://golang.org/dl/,默认路径安装
  2. 下载后直接双击msi文件安装,默认安装在c:\go
  3. 安装完成后默认会在环境变量 Path 后添加 Go 安装目录下的 bin 目录 C:\Go\bin\,并添加环境变量 GOROOT,值为 Go 安装根目录 C:\Go\
  4. 验证是否安装成功,在运行中输入 cmd 打开命令行工具,在提示符下输入 go
  5. 设置工作空间gopath目录, 也即开发的项目路径。Windows环境下,新建一个环境变量名称叫做GOPATH,值就是你的工作目录,例如GOPATH=e:\mygo
    %GOPATH% 目录约定有三个子目录:
    -- src 存放源代码(比如:.go .c .h .s等)
    -- pkg 编译后生成的文件(比如:.a)
    -- bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 windows的PATH 变量中,在环境变量path后追加%GOPATH%\bin)
  6. 用go env命令可以查看GO相关的环境变量设置
  7. IDE的选择和设置: Go的开发ide目前有很多选择,比如LiteIDE,Vim,Emcas,goEclipse等。这里使用LiteIDE
  8. 编写helloworld并运行
// helloworld
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World!")
}

Linux

Ubuntu16.04

bob@bob-Virtual-Machine:~$ go

Command 'go' not found, but can be installed with:

sudo snap install go         # version 1.11.5, or
sudo apt  install golang-go
sudo apt  install gccgo-go

See 'snap info go' for additional versions.

bob@bob-Virtual-Machine:~$ sudo apt install golang-go
Reading package lists... Done
Building dependency tree
Reading state information... Done
....

bob@bob-Virtual-Machine:~$ go version
go version go1.10.4 linux/amd64
bob@bob-Virtual-Machine:~$
bob@bob-Virtual-Machine:~$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/bob/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/bob/go"
GORACE=""
GOROOT="/usr/lib/go-1.10"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.10/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build549258037=/tmp/go-build -gno-record-gcc-switches"

可以看到GOPATH已经有默认值,可以保留默认值也可以修改为其他值。

单元测试

一般来讲,单元测试代码和对应的源代码文件是放在同一级目录并且是同名文件以_test.go结尾,也可以把所有的单元测试代码放在一个单独的test目录, 但需要处理依赖。

sum.go是源代码,sum_test.go是单元测试代码(按照惯例,go中的单元测试代码文件要以_test.go结尾)

执行单元测试,并查看单元测试覆盖率

Administrator@SC-201810221720 MINGW64 /f/gopath/src/main
$ ls
sum.go  sum_test.go

Administrator@SC-201810221720 MINGW64 /f/gopath/src/main
$ go build

Administrator@SC-201810221720 MINGW64 /f/gopath/src/main
$ ls
main.exe*  sum.go  sum_test.go

Administrator@SC-201810221720 MINGW64 /f/gopath/src/main
$ cat sum.go
package main

func Sum(x int, y int) int {
    return x + y
}

func main() {
    Sum(5, 5)
}
Administrator@SC-201810221720 MINGW64 /f/gopath/src/main
$ cat sum_test.go
package main

import "testing"

func TestSum(t *testing.T) {
    total := Sum(5, 5)
    if total != 10 {
       t.Errorf("Sum was incorrect, got: %d, want: %d.", total, 10)
    }
}
Administrator@SC-201810221720 MINGW64 /f/gopath/src/main
$ go test
PASS
ok      _/F_/gopath/src/main    0.027s

Administrator@SC-201810221720 MINGW64 /f/gopath/src/main
$ go test -cover
PASS
coverage: 50.0% of statements
ok      _/F_/gopath/src/main    0.026s

Administrator@SC-201810221720 MINGW64 /f/gopath/src/main

go run:go run 编译并直接运行程序,它会产生一个临时文件(但不会生成 .exe 文件),直接在命令行输出程序执行结果,方便用户调试。

go build:go build 用于测试编译包,主要检查是否会有编译错误,如果是一个可执行文件的源码(即是 main 包),就会直接生成一个可执行文件。

go install:go install 的作用有两步:第一步是编译导入的包文件,所有导入的包文件编译完才会编译主程序;第二步是将编译后生成的可执行文件放到 bin 目录下($GOPATH/bin),编译后的包文件放到 pkg 目录下($GOPATH/pkg)。

Golang unit test coverage threshold

可以参考https://github.com/jokeyrhyme/go-coverage-threshold

测试框架

gotests、mockery
#安装gotests
pilot@ubuntu:~$pwd
pilot@ubuntu:~$/home/pilot/gopath/src
pilot@ubuntu:~$go get -u github.com/cweill/gotests
pilot@ubuntu:~$ go build -o $GOPATH/bin/gotests github.com/cweill/gotests/gotests/main.go
pilot@ubuntu:~$ls $GOPATH/bin/gotests
pilot@ubuntu:~$/home/pilot/gopath/bin/gotests

pilot@ubuntu:~$pwd
/home/pilot/gopath/src/testme
pilot@ubuntu:~$ ls
sum.go
pilot@ubuntu:~$gotests -all -w sum.go  sum_test.go
pilot@ubuntu:~$ ls
sum.go    sum_test.go

pilot@ubuntu:~$  cat sum.go
package testme

func Sum(x int, y int) int {
    return x + y
}

func main() {
    Sum(5, 5)
}
Administrator@SC-201810221720 MINGW64 /f/gopath/src/testme
pilot@ubuntu:~$ cat sum_test.go
package testme

import "testing"

func TestSum(t *testing.T) {
        type args struct {
                x int
                y int
        }
        tests := []struct {
                name string
                args args
                want int
        }{
                // TODO: Add test cases.
        }
        for _, tt := range tests {
                if got := Sum(tt.args.x, tt.args.y); got != tt.want {
                        t.Errorf("%q. Sum() = %v, want %v", tt.name, got, tt.want)
                }
        }
}

func Test_main(t *testing.T) {
        tests := []struct {
                name string
        }{
                // TODO: Add test cases.
        }
        for range tests {
                main()
        }
}

pilot@ubuntu:~$ go test
PASS
ok      testme  0.025s

Administrator@SC-201810221720 MINGW64 /f/gopath/src/testme
pilot@ubuntu:~$ go test -cover
PASS
coverage: 0.0% of statements
ok      testme  0.026s

#更新自动生成的sum_test.go,添加单元测试代码,再次运行单元测试
pilot@ubuntu:~$  go test
PASS
ok      testme  0.027s


pilot@ubuntu:~$  go test -cover
PASS
coverage: 50.0% of statements
ok      testme  0.024s

pilot@ubuntu:~$  cat sum_test.go
package testme

import "testing"

func TestSum(t *testing.T) {
    type args struct {
        x int
        y int
    }
    tests := []struct {
        name string
        args args
        want int
    }{
        // TODO: Add test cases.
        {"lalala",
            args{3, 4},
            7,
        },
        {"lalala2",
            args{5, 4},
            8,
        },
    }
    for _, tt := range tests {
        if got := Sum(tt.args.x, tt.args.y); got != tt.want {
            t.Errorf("%q. Sum() = %v, want %v", tt.name, got, tt.want)
        }
    }
}

func Test_main(t *testing.T) {
    tests := []struct {
        name string
    }{
        // TODO: Add test cases.
    }
    for range tests {
        main()
    }
}
golang的测试框架stretchr/testify

Reference

https://goswagger.io/
https://github.com/golang/dep
https://www.cnblogs.com/pyyu/p/8032257.html
https://medium.com/@a.putraherawan/writing-your-first-micro-service-api-with-go-e781a549791
one 100% coverage sample project
https://medium.com/@benbjohnson/structuring-tests-in-go-46ddee7a25c
https://stackoverflow.com/questions/19200235/golang-tests-in-sub-directory
https://studygolang.com/articles/15329?fr=sidebar

你可能感兴趣的:(unit test code coverage of Golang)