单元测试

129 阅读4分钟

我们每开发一个功能,都应该对其进行测试,以确保代码的正确性,而 go 语言为我们提供了一系列的工具来完成测试,其中就包括单元测试

什么是单元测试

顾名思义,单元测试强调的是对单元进行测试。在开发中,一个单元可以是一个函数、一个模块等。一般情况下,你要测试的单元应该是一个完整的最小单元,比如 Go 语言的函数。这样的话,当每个最小单元都被验证通过,那么整个模块、甚至整个程序就都可以被验证通过。

单元测试应该由开发人员编写。

怎么使用 go 语言的单元测试

接下来我以一个简单的例子来演示 go 语言单元测试的使用。

我们先编写一个简单的函数,这个函数的功能是求一个数的阶乘。

func Factorial(n int) int {
   // 负数没有阶乘,我们这里直接返回
   if n < 0 {
      return 0
   }

   // 0的阶乘为1
   if n == 0 {
      return 1
   }

   // 循环求阶乘
   fac := 1
   for i := 2; i <= n; i++ {
      fac *= i
   }
   return fac
}

这个函数编写好后我们编写一个测试函数,目的是测试阶乘函数是否正确。

测试函数与被测试函数放在同一目录下,文件名为 _test.go ,必须这么写,其中 * 是被测试文件的文件名。函数名一般为为 Test ,*为被测试函数的函数名。

func TestFactorial(t *testing.T) {
   // 这里先初始化一个 map 存一些已知的结果用于与函数的结果比较
   m := make(map[int]int, 10)
   m[0] = 1
   m[1] = 1
   m[2] = 2
   m[3] = 6
   m[4] = 24
   m[5] = 120
   m[6] = 720
   m[7] = 5040
   m[8] = 40320
   m[9] = 362880
   for i := 0; i < 10; i++ {
      fmt.Println(m[i] == Factorial(i))
   }
}

然后即可运行如下命令,进行单元测试:

go test -v . 

输出如下结果

=== RUN   TestFactorial
true
true
true
true
true
true
true
true
true
true
--- PASS: TestFactorial (0.00s)
PASS
ok      context_demo    0.001s

在打印的测试结果中,你可以看到 PASS 标记,说明单元测试通过,true 为我们在代码中打印的结果。

这就是一个完整的 Go 语言单元测试用例,它是在 Go 语言提供的测试框架下完成的。Go 语言测试框架可以让我们很容易地进行单元测试,但是需要遵循五点规则。

  1. 含有单元测试代码的 go 文件必须以 _test.go 结尾,Go 语言测试工具只认符合这个规则的文件。
  2. 单元测试文件名 _test.go 前面的部分最好是被测试的函数所在的 go 文件的文件名,比如以上示例中单元测试文件叫 main_test.go,因为测试的 Factorial 函数在 main.go 文件里。
  3. 单元测试的函数名必须以 Test 开头,是可导出的、公开的函数。
  4. 测试函数的签名必须接收一个指向 testing.T 类型的指针,并且不能返回任何值。
  5. 函数名最好是 Test + 要测试的函数名,比如例子中是 TestFactorial,表示测试的是 Factorial 这个函数。

遵循以上规则,你就可以很容易地编写单元测试了。单元测试的重点在于熟悉业务代码的逻辑、场景等,以便尽可能地全面测试,保障代码质量。

单元测试覆盖率

以上 Factorial 函数是否全部被执行了呢?这就引出了单元测试覆盖率的概念。

go 语言也为我们提供了非常方便的工具来查看,我们只需要在命令中指定--coverprofile即可

go test --coverprofile=test.cover .

输出

ok      context_demo    0.001s  coverage: 87.5% of statements

其中87.5%便是单元测试覆盖率,执行完此命令后同时还会在当前目录下生成一个名为test.cover的文件,就是我们指定的--coverprofile的参数。

上面的单元测试覆盖率不是百分之百,这代表函数有一部分是没有执行到的,想要获得更详细的信息,我们可以使用以下命令查看。

go tool cover -html=test.cover -o=test.html

其中-html=test.cover表示我们想要基于test.cover生成一个 HTML 文件,-o是指定生成文件的文件名,然后我们使用游览器打开 HTML 文件。

image.png

其中红色的便是没有覆盖到的代码区域。

总结

  • 单元测试是是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确
  • 单元测试是非常重要的,对于未测试的代码,如果强行合并到代码库,可能影响其他人的开发;如果强行上线,可能导致线上 Bug、影响用户使用。
  • go 语言为我们提供了go test命令来帮助我们快速进行单元测试,我们还可以配合go tool命令来获得更精确的报告,在终端输入go help test获取更多有关go test命令的信息。