0


Golang 测试工具 go test

阅读目录

测试工具 :go test

go test 本身可以携带很多的参数,熟悉这些参数,可以让我们的测试过程更加方便。

1 运行整个项目的测试文件

  1. $ go test
  2. PASS
  3. ok _/home/wangbm/golang/math 0.003s

2 只运行某个测试文件

math_test.go
math.go 是一对,缺一不可,前后顺序可对调。

  1. $ go test math_test.go math.go
  2. ok command-line-arguments 0.002s

3 加 -v 查看详细的结果

  1. $ go test math_test.go math.go=== RUN TestAdd
  2. TestAdd: main_test.go:22: the result is ok
  3. TestAdd: main_test.go:22: the result is ok
  4. TestAdd: main_test.go:22: the result is ok
  5. TestAdd: main_test.go:22: the result is ok
  6. TestAdd: main_test.go:22: the result is ok
  7. --- PASS: TestAdd (0.00s)
  8. PASS
  9. ok command-line-arguments 0.003s

4 只测试某个函数

  1. -run

支持正则,如下例子中

  1. TestAdd

,如果还有一个测试函数为

  1. TestAdd02

,那么它也会被运行。

  1. $ go test -v -run="TestAdd"=== RUN TestAdd
  2. TestAdd: math_test.go:22: the result is ok
  3. TestAdd: math_test.go:22: the result is ok
  4. TestAdd: math_test.go:22: the result is ok
  5. TestAdd: math_test.go:22: the result is ok
  6. TestAdd: math_test.go:22: the result is ok
  7. --- PASS: TestAdd (0.00s)
  8. PASS
  9. ok _/home/wangbm/golang/math 0.003s

5 生成 test 的二进制文件:加 -c 参数

  1. $ go test -v -run="TestAdd"-c
  2. $
  3. $ ls -l
  4. total 3208-rw-r--r--1 root root 95 May 2520:56 math.go-rwxr-xr-x 1 root root 3272760 May 2521:00 math.test
  5. -rw-r--r--1 root root 525 May 2520:56 math_test.go

6 执行这个 test 测试文件:加 -o 参数

  1. $ go test -v -o math.test
  2. === RUN TestAdd
  3. TestAdd: math_test.go:22: the result is ok
  4. TestAdd: math_test.go:22: the result is ok
  5. TestAdd: math_test.go:22: the result is ok
  6. TestAdd: math_test.go:22: the result is ok
  7. TestAdd: math_test.go:22: the result is ok
  8. --- PASS: TestAdd (0.00s)=== RUN TestAum
  9. TestAum: math_test.go:30:6--- PASS: TestAum (0.00s)
  10. PASS
  11. ok _/home/wangbm/golang/math 0.002s

7 测试安装/重新安装 依赖包,而不运行代码:加 -i 参数

  1. # 这里没有输出
  2. $ go test -i

单元测试

准备两个 Go 文件::

  1. E:\TEXT\test_go\test\math\math.go
  1. package math
  2. funcAdd(x, y int)int{return x + y
  3. }
  1. E:\TEXT\test_go\test\math\math_test.go
  1. package math
  2. import"testing"funcTestAdd(t *testing.T){
  3. t.Log(Add(1,2))}

然后使用 go test 工具去执行:

  1. PS E:\TEXT\test_go\test\math>go test .
  2. ok test/math 0.365s
  3. PS E:\TEXT\test_go\test\math>
  4. PS E:\TEXT\test_go\test\math>go test .-v
  5. === RUN TestAdd
  6. math_test.go:6:3--- PASS: TestAdd (0.00s)
  7. PASS
  8. ok test/math 0.359s
  9. PS E:\TEXT\test_go\test\math>

从上面这个例子中,可以总结中几点 Go 语言测试框架要遵循的规则:

  1. 单元测试代码的 go文件必须以 _test.go 结尾,而前面最好是被测试的文件名(不过并不是强制的),比如要测试 math.go 测试文件名就为 math_test.go。
  2. 单元测试的函数名必须以Test开头,后面直接跟要测试的函数名,比如要测试 Add函数,单元测试的函数名就得是 TestAdd。
  3. 单元测试的函数必须接收一个指向 testing.T 类型的指针,并且不能返回任何值。

表组测试

Add(1, 2) 是一次单元测试的场景,而 Add(2, 4) ,Add(3, 6) 又是另外两种单元测试的场景。

对于多种输入场景的测试,我们可以同时放在 TestAdd 里进行测试,这种测试方法就是表组测试。

修改

  1. E:\TEXT\test_go\test\math\math_test.go

如下:

  1. package math
  2. import"testing"funcTestAdd(t *testing.T){
  3. sum :=Add(1,2)if sum ==3{
  4. t.Log("the result is ok")}else{
  5. t.Fatal("the result is wrong")}
  6. sum =Add(2,4)if sum ==6{
  7. t.Log("the result is ok")}else{
  8. t.Fatal("the result is wrong")}}
  1. PS E:\TEXT\test_go\test\math>go test .-v
  2. === RUN TestAdd
  3. math_test.go:8: the result is ok
  4. math_test.go:15: the result is ok
  5. --- PASS: TestAdd (0.00s)
  6. PASS
  7. ok test/math 0.356s
  8. PS E:\TEXT\test_go\test\math>

如果输入的场景实在太多(比如下面用的五组输入),用上面的方法,可能需要写很多重复的代码,这时候可以利用表格测试法

  1. E:\TEXT\test_go\test\math\math_test.go
  1. package math
  2. import("fmt""testing")type TestTable struct{
  3. xarg int
  4. yarg int}funcTestAdd(t *testing.T){
  5. tables :=[]TestTable{{1,2},{2,4},{4,8},{5,10},{6,12},}for_, table :=range tables {
  6. result :=Add(table.xarg, table.yarg)if result ==(table.xarg + table.yarg){
  7. t.Log(""+ fmt.Sprintf("the result is ok %d", result))}else{
  8. t.Fatal("the result is wrong")}}}
  1. PS E:\TEXT\test_go\test\math>go test .-v
  2. === RUN TestAdd
  3. math_test.go:25: the result is ok 3
  4. math_test.go:25: the result is ok 6
  5. math_test.go:25: the result is ok 12
  6. math_test.go:25: the result is ok 15
  7. math_test.go:25: the result is ok 18--- PASS: TestAdd (0.00s)
  8. PASS
  9. ok test/math 0.351s
  10. PS E:\TEXT\test_go\test\math>

理清 Go 中晦涩难懂的寻址问题

什么叫可寻址?

可直接使用 & 操作符取地址的对象,就是可寻址的(Addressable)。

  1. E:\TEXT\test_go\test\math\math.go
  1. package math
  2. import"fmt"funcUnit(){
  3. name :="heihie"
  4. fmt.Println(&name)}
  1. E:\TEXT\test_go\test\math\math_test.go
  1. PS E:\TEXT\test_go\test\math>go test .-v
  2. === RUN TestUnit
  3. 0xc0000505a0--- PASS: TestUnit (0.00s)
  4. PASS
  5. ok test/math 0.313s
  6. PS E:\TEXT\test_go\test\math>

程序运行不会报错,说明

  1. name

这个变量是可寻址的。

但不能说 “iswbm” 这个字符串是可寻址的。

“iswbm” 是字符串,字符串都是不可变的,是不可寻址的,后面会介绍到。

在开始逐个介绍之前,先说一下结论:

  • 指针可以寻址:&Profile{}
  • 变量可以寻址:name := Profile{}
  • 字面量通通不能寻址:Profile{}

哪些是可以寻址的?

变量:&x

  1. funcmain(){
  2. name :="iswbm"
  3. fmt.Println(&name)// output: 0xc000010200}

指针:&*x

  1. type Profile struct{
  2. Name string}funcmain(){
  3. fmt.Println(unsafe.Pointer(&Profile{Name:"iswbm"}))// output: 0xc000108040}

数组元素索引: &a[0]

  1. funcmain(){
  2. s :=[...]int{1,2,3}
  3. fmt.Println(&s[0])// output: xc0000b4010}

切片

  1. funcmain(){
  2. fmt.Println([]int{1,2,3}[1:])}

切片元素索引:&s[1]

  1. funcmain(){
  2. s :=make([]int,2,2)
  3. fmt.Println(&s[0])// output: xc0000b4010}

组合字面量不可寻址字段属性可寻址

所有的组合字面量都是不可寻址的,就像下面这样子

  1. type Profile struct{
  2. Name string}funcnew() Profile {return Profile{Name:"iswbm"}}funcmain(){
  3. fmt.Println(&new())// cannot take the address of new()}

注意上面写法与这个写法的区别,下面这个写法代表不同意思,其中的

  1. &

并不是取地址的操作,而代表实例化一个结构体的指针。

  1. type Profile struct{
  2. Name string}funcmain(){
  3. fmt.Println(&Profile{Name:"iswbm"})// ok}

虽然组合字面量是不可寻址的,但却可以对组合字面量的字段属性进行寻址(直接访问)

  1. type Profile struct{
  2. Name string}funcnew() Profile {return Profile{Name:"iswbm"}}funcmain(){
  3. fmt.Println(new().Name)}

哪些是不可以寻址的?

常量

  1. import"fmt"const VERSION ="1.0"funcmain(){
  2. fmt.Println(&VERSION)}

字符串

  1. funcgetStr()string{return"iswbm"}funcmain(){
  2. fmt.Println(&getStr())// cannot take the address of getStr()}

函数或方法

  1. funcgetStr()string{return"iswbm"}funcmain(){
  2. fmt.Println(&getStr)// cannot take the address of getStr}

基本类型字面量

字面量分:基本类型字面量复合型字面量

基本类型字面量,是一个值的文本表示,都是不应该也是不可以被寻址的。

  1. funcgetInt()int{return1024}funcmain(){
  2. fmt.Println(&getInt())// cannot take the address of getInt()}

map 中的元素

字典比较特殊,可以从两个角度来反向推导,假设字典的元素是可寻址的,会出现 什么问题?

  1. 如果字典的元素不存在,则返回零值,而零值是不可变对象,如果能寻址问题就大了。
  2. 而如果字典的元素存在,考虑到 Go 中 map 实现中元素的地址是变化的,这意味着寻址的结果也是无意义的。

基于这两点,Map 中的元素不可寻址,符合常理。

  1. funcmain(){
  2. p :=map[string]string{"name":"iswbm",}
  3. fmt.Println(&p["name"])// cannot take the address of p["name"]}

搞懂了这点,你应该能够理解下面这段代码为什么会报错啦~

  1. E:\TEXT\test_go\test\math\math.go
  1. package math
  2. import"fmt"type Person struct{
  3. Name string
  4. Email string}funcUnit(){
  5. m :=map[int]Person{1: Person{"Andy","1137291867@qq.com"},2: Person{"Tiny","qishuai231@gmail.com"},3: Person{"Jack","qs_edu2009@163.com"},}//编译错误:cannot assign to struct field m[1].Name in map// m[1].Name = "Scrapup"
  6. fmt.Println(m[1].Name)}
  1. E:\TEXT\test_go\test\math\math_test.go
  1. package math
  2. import("fmt""testing")funcTestUnit(t *testing.T){Unit()}funcTestCc(t *testing.T){
  3. fmt.Println("hello")}
  1. PS E:\TEXT\test_go\test\math> go test -v -run="TestUnit"
  2. === RUN TestUnit
  3. Andy
  4. --- PASS: TestUnit (0.00s)
  5. PASS
  6. ok test/math 0.339s
  7. PS E:\TEXT\test_go\test\math>

解决map修改值的方法

  1. package math
  2. import"fmt"type Person struct{
  3. Name string
  4. Email string}funcUnit(){
  5. m :=map[int]Person{1: Person{"Andy","1137291867@qq.com"},2: Person{"Tiny","qishuai231@gmail.com"},3: Person{"Jack","qs_edu2009@163.com"},}// 创建一个新的Person
  6. newPerson := Person{Name:"Scrapup", Email:"1137291867@qq.com"}// 创建一个新的map
  7. newMap :=make(map[int]Person)// 遍历原始mapfor k, v :=range m {// 如果键值不等于1,则复制元素if k !=1{
  8. newMap[k]= v
  9. }else{// 否则,添加新的Person
  10. newMap[k]= newPerson
  11. }}// 将新的map赋值给原始map
  12. m = newMap
  13. //打印结果for k, v :=range m {
  14. fmt.Printf("key is %d, value is %v)\n", k, v)}}

数组字面量进行切片操作

数组字面量是不可寻址的,当你对数组字面量进行切片操作,其实就是寻找内部元素的地址,下面这段代码是会报错的。

  1. funcmain(){
  2. fmt.Println([3]int{1,2,3}[1:])// invalid operation [3]int literal[1:] (slice of unaddressable value)}

本文转载自: https://blog.csdn.net/weiguang102/article/details/129279648
版权归原作者 知其黑、受其白 所有, 如有侵权,请联系我们删除。

“Golang 测试工具 go test”的评论:

还没有评论