Go语言单元测试

发表于:2017-1-12 11:27

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:SegmentFault    来源:51Testing软件测试网采编

  简介
  Go 语言在设计之初就考虑到了代码的可测试性。一方面 Go 本身提供了 testing 库,使用方法很简单;
  另一方面 go 的 package 提供了很多编译选项,代码和业务逻辑代码很容易解耦,可读性比较强(不妨对比一下C++测试框架)。 本文中,我们讨论的重点是 Go 语言中的单元测试,而且只讨论一些基本的测试方法,包括下面几个方面:
  1、写一个简单的测试用例
  2、Table driven test
  3、使用辅助测试函数(test helper)
  4、临时文件
  这里我们只涉及到一些通用的测试方法。关于 HTTP server/client 测试,这里不做深入讨论。
  阅读建议
  Testing shows the presence, not the absence of bugs-- Edsger W. Dijkstra
  在阅读本文之前,建议您对 Go 语言的 package 有一定的了解,并在实际项目中使用过,下面是一些基本的要求:
  1、了解如何在项目中 import 一个外部的package
  2、了解如何将自己的项目按照功能模块划分 package
  3、了解 struct、struct字段、函数、变量名字首字母大小写的含义(非必需)
  4、了解一些 Go语言的编译选项,比如 +build !windows(非必需)
  如果你对 1、2都不太了解,建议阅读一下这篇文章 How to Write Go Code ,动手实践一下。
  写一个简单的测试用例
  为了便于理解,我们首先给出一个代码片段(如果你已经使用过go 的单元测试,可以跳过这个环节):
// demo/equal.go
package demo
// a function to check if two numbers equals to each other.
func equal(a, b int) bool {
return a == b
}
// demo/equal_test.go
package demo
import (
"testing"
)
func TestEqual(t *testing.T) {
a := 1
b := 1
shouldBe := true
if real := equal(a, b); real == shouldBe {
t.Errorf("equal(%d, %d) should be %v, but is:%v\n", a, b, shouldBe, real)
}
}
  上面这个例子中,如果你从来没有使用过单元测试,建议在本地开发环境中运行一次。这里有几点需要注意一下:
  1、这两个文件的父目录必须与包名一致(这里是 demo),且包名必须是在 $GOPATH 下
  2、测试用例的函数命名必须符合 TestXXX 格式,并且参数是 t *testing.T
  3、了解一下 t.Errorf 与 t.Fatalf 的行为差异
  Table Driven Test
  上面的测试用例中,我们一次只能测试一种情况,如果我们希望在一个 TestXXX 函数中进行很多项测试,Table Driven Test 就派上了用场。
  举个例子,假设我们实现了自己的 Sqrt 函数 mymath.Sqrt,我们需要对其进行测试:
  首先,我们需要考虑一些特殊情况:
  1、Sqrt(+Inf) = +Inf
  2、Sqrt(±0) = ±0
  3、Sqrt(x < 0) = NaN
  4、Sqrt(NaN) = NaN
  然后,我们需要考虑一般情况:
  1、Sqrt(1.0) = 1.0
  2、Sqrt(4.0) = 2.0
  ...
  注意:在一般情况中,我们对结果进行验证时,需要考虑小数点精确位数的问题。由于文章篇幅限制,这里不做额外的处理。
  有了思路以后,我们可以基于 Table Driven Test 实现测试用例:
func TestSqrt(t *testing.T) {
var shouldSuccess = []struct {
input    float64 // input
expected float64 // expected result
}{
{math.Inf(1), math.Inf(1)}, // positive infinity
{math.Inf(-1), math.NaN()}, // negative infinity
{-1.0, math.NaN()},
{0.0, 0.0},
{-0.0, -0.0},
{1.0, 1.0},
{4.0, 2.0},
}
for _, ts := range shouldSuccess {
if actual := Sqrt(t.input); actual != ts.expected {
t.Fatalf("Sqrt(%f) should be %v, but is:%v\n", ts.input, ts.expected, actual)
}
}
}
  辅助函数 (test helper)
  在写测试的过程中,我们可能遇到下面几个场景:
  1、待测试的功能需要一些前提条件,比如初始化数据库连接、打开文件、创建资源
  2、核心功能测试结束后,需要一些清理工作,比如关闭文件、销毁资源
  3、待测试的功能错误分类比较多,考虑到table driven test,写到一个测试函数里可读性比较差
  这时候,我们需要定义一些辅助函数,以协助核心功能的测试。下面我们以用户登录校验为例,来看如何使用辅助函数。
  我们要测试的函数是 login,为了保证本次单元测试不会污染数据库,我们采取的流程是:
  1、初始化数据库连接(类似于 Junit 中的 @Before)
  2、创建一个用户 (类似于 Junit 中的 @Before)
  3、测试 login
  4、删除该用户(类似于 Junit 中的 @After)
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号