代码改变世界

golang unit test测试

2022-08-10 01:23  youxin  阅读(621)  评论(0编辑  收藏  举报

Golang单元测试对文件名和方法名,参数都有很严格的要求。
  例如:
  1、文件名必须以xx_test.go命名
  2、方法必须是Test[^a-z]开头
  3、方法参数必须 t *testing.T‘

 

 

Go语言中的测试依赖go test命令。编写测试代码和编写普通的Go代码过程是类似的,并不需要学习新的语法、规则或工具。
go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。
_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。
image
go test命令会遍历所有的
_test.go文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。’‘

 

运行程序时报go: cannot find main module, but found .git/config in

 

编写单元测试,运行时报下面的错误

haima@haima-PC:/media/haima/34E401CC64DD0E28/site/go/src/haimait/learn/base/cheshi01$ go test
go: cannot find main module, but found .git/config in /media/haima/34E401CC64DD0E28/site/go/src/haimait/learn
        to create a module there, run:
        cd ../.. && go mod init

出错原因是开启了go mod,但是没有初使化生成go.mod文件

 

解决办法1:关闭gomod

 解决办法2:go mod init xx
https://www.cnblogs.com/haima/p/14020284.html
https://zhuanlan.zhihu.com/p/417246469

main函数里测试

有时候,要测试一个函数是否功能正常,有时候我们会直接写在main函数里,然后通过log框架或者fmt直接打印信息,验证,下面给出一个例子
package main

import "fmt"

func main() {
    res := addUpper(10)
    fmt.Printf("返回值res:%v", res)
}

func add(n1 int , n2 int) int {
    return n1 + n2
}

func addUpper(n int) int {
    res := 0
    for i := 0 ; i <= n ; i++ {
        res += i
    }
    return res
}

这种方法对于单个文件,代码量不多,其实也是可以的,但是对于一个庞大的项目,这种方法就显得很麻烦,特别是生产,我们难道为了测试去更改main函数,然后再重启,显然是不合理的。然后所有的测试业务都写在main函数,不利于管理,代码量一多就会变得复杂

3、单位测试入门
testing是golang里的一个轻量级的测试框架,可以用于单位测试,也可以用于性能测试,程序员可以基于这个框架写相应的单位测试用例,然后通过日志打印等方式进行debug调试,定位排查问题

使用很容易,基于原来的文件,sample.go,新建一个sample_test.do的测试文件,注意,必须命名为*_test.do

 

package main

import (
    "testing"
)


func TestAdd(t *testing.T)  {
    res := add(1,2)
    if res != 3 {
        t.Fatalf("error res %v" , res)
    }
    t.Logf("success res %v" , res)
}

func TestAddUpper(t *testing.T)  {
    res := addUpper(10)
    if res != 55 {
        t.Fatalf("error res %v" , res)
    }
    t.Logf("success res %v" , res)
}

注意:

测试用例文件必须以_test.go结尾
测试用例函数必须以Test开头,一般是Test+被测试的函数名
测试函数形式参数必须是*testing.T
出现错误,t.Fatalf打印错误日志并退出程序
程序正常,t.Logf打印格式化的日志
测试单个文件,要加上测试的原文件go test -v sample_test.go sample.go
测试单个函数,加上方法名,go test -v sample_test.go TestAdd
运行测试用例命令
cmd > go test :运行正确不打印日志,错误才会打印日志
cmd > go test -v :运行错误或成功,都打印日志

 

Golang单元测试的基础今天就暂时到这里面,下面几个testing包的常用断言方法.

// 输出测试日志
t.Logf()
t.Logf()
// 标记错误,但仍然执行后面的语句
t.Fail()
// 获取是否当前用例是执行错误的
t.Failed()
// 错误输出,等于 t.Logf 再执行 t.Fail()
t.Errorf("%s", "run ErrorF")
// 标记函数错误,并中断后面的执行
t.FailNow()
// 致命错误输出,等同于调用了 t.Logf 然后调用 t.FailNow()
t.Fatalf("%s", "run Fatelf")


————————————————

go test [-c] [-i] [build flags] [packages] [flags for test binary]

 

测试覆盖率

测试覆盖率是你的代码被测试套件覆盖的百分比。通常我们使用的都是语句的覆盖率,也就是在测试中至少被运行一次的代码占总代码的比例。
Go提供内置功能来检查你的代码覆盖率。我们可以使用go test -cover来查看测试覆盖率。例如:

split $ go test -cover
PASS
coverage: 100.0% of statements
ok      github.com/Q1mi/studygo/code_demo/test_demo/split       0.005s

从上面的结果可以看到我们的测试用例覆盖了100%的代码。
Go还提供了一个额外的-coverprofile参数,用来将覆盖率相关的记录信息输出到一个文件。例如:

split $ go test -cover -coverprofile=c.out
PASS
coverage: 100.0% of statements
ok      github.com/Q1mi/studygo/code_demo/test_demo/split       0.005s

上面的命令会将覆盖率相关的信息输出到当前文件夹下面的c.out文件中,然后我们执行go tool cover -html=c.out,使用cover工具来处理生成的记录信息,该命令会打开本地的浏览器窗口生成一个HTML报告。

 

 

 go test整个项目报错:

golang 运行 go test 时 遇到报错 call has possible formatting directive %v 怎么解决

 

问题

Println call has possible formatting directive %v

go test 中不能使用 fmt.PrintLn(“%v”, v)

 

方案

使用 fmt.Printf(“%+v”, v)

 

fmt.Println("Hello, playground %d",i)
那么会出现warning:Println call has possible formatting directive %d Go vet exited.

 

fmt.Println doesn't do formatting things like %d. Instead, it uses the default format of its arguments, and adds spaces between them.

fmt.Println("Hello, playground",i) // Hello, playground 5
If you want printf style formatting, use fmt.Printf.

fmt.Printf("Hello, playground %d\n",i)
And you don't need to be particular about the type. %v will generally figure it out.

fmt.Printf("Hello, playground %v\n",i)
————————————————
原文链接:https://blog.csdn.net/Hack_Bug/article/details/110944701

 

Test注意要点

  • Go的test不会保证多个TestXxx顺序执行,但是通常会按顺序执行
  • 使用t.Run来执行subtests可以做到控制test输出以及test的顺序
// 顺序执行
func TestPrintSub(t *testing.T){
    t.Run("a1",func(t *testing.T){fmt.Println("a1")})
    t.Run("a2",func(t *testing.T){fmt.Println("a2")})
    t.Run("a3",func(t *testing.T){fmt.Println("a3")})
}
  • 使用TestMain作为初始化test,并且使用m.Run()来调用其他tests可以完成一些需要初始化操作的testing,
  • 比如数据库连接,文件打开,Rest服务登录等
// TestMain
func TestMain(m *testing.M){
    fmt.Println("test main first")
    m.Run()
}
  • 如果没有在TestMain中调用m.Run()则除了TestMain以外的其他tests都不好被执行
package split

import (
    "reflect"
    "testing"
)

func TestSplit(t *testing.T) {
    type args struct {
        s   string
        sep string
    }
    tests := []struct {
        name       string
        args       args
        wantResult []string
    }{
        // TODO: Add test cases. 
        {name: "test1",args: args{s:"a:b:c",sep: ":"},wantResult: []string{"a", "b", "c"}},
        {name: "test2",args: args{s:"a:b:c",sep: ","},wantResult: []string{"a:b:c"}},
        {name: "test3",args: args{s:"abcd",sep: "bc"},wantResult: []string{"a", "d"}},
        {name: "test4",args: args{s:"沙河有沙又有河",sep: ""},wantResult: []string{"河有", "又有河"}},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if gotResult := Split(tt.args.s, tt.args.sep); !reflect.DeepEqual(gotResult, tt.wantResult) {
                t.Errorf("Split() = %v, want %v", gotResult, tt.wantResult)
            }
        })
    }
}

 



链接:https://www.jianshu.com/p/3cf66a256d19

 

ubtests and Sub-benchmarks
T和B类型中的Run可以定义子测试和子benchmarks,不需要为sub们定义函数。This enables uses like table-driven benchmarks and creating hierarchical tests. It also provides a way to share common setup and tear-down code:(这个可以使用表驱动benchmark和层级式测试,也能提供公共setup和tear-down代码)

func TestFoo(t *testing.T) {
// <setup code>
t.Run("A=1", func(t *testing.T) { ... })
t.Run("A=2", func(t *testing.T) { ... })
t.Run("B=1", func(t *testing.T) { ... })
// <tear-down code>
}
运行测试时,可以-run和-bench flags设置运行那些测试:

go test -run '' # 运行所有测试
go test -run Foo # 运行一级测试,匹配 "Foo", 例如 "TestFooBar".
go test -run Foo/A= # 运行一级测试,匹配 "Foo", 运行二级测试,匹配 "A=".
go test -run /A=1 # 运行所有一级测试, 运行二级测试,匹配 "A=1".
子用例可以并行运行,父级仅在所有子用例运行完才完成。

func TestGroupedParallel(t *testing.T) {
for _, tc := range tests {
tc := tc // capture range variable
t.Run(tc.Name, func(t *testing.T) {
t.Parallel()
...
})
}
}
子测试间是并行的运行。

Main
有时需要在测试前或测试后有一些设置或tear-down的控制,或者控制那些代码需要在主线程运程,可以在一个文件内添加一个

func TestMain(m *testing.M)
————————————————
原文链接:https://blog.csdn.net/huafable007/article/details/109596821