go 单元测试基本篇

作者:熊训德

go 语言本身内置了一套相对轻量级的测试框架,通过 testing 库和 go test 命令支持单元测试。本篇文档主要介绍使用 go 语言 testing 包进行单元测试方法,以及一些在编写单元测试过程遇到的坑。

在 go 中使用单元测试时,在同需测试的源文件目录下增加XXX_test.go(XXX是源文件名)文件即可。如用Intelij IDEA还会帮你把包设置为XXX_test(XXX是源包名),这样可以防止测试依赖的包时的相互依赖,go把这个称作扩展测试包(External test packages)。

下面举一个栗子就很容易理解:

需要测试的函数是一个查询函数,它的功能是通过http协议向异构的某个模块查询信息。因为对方这个模块的接口是可能变更的,所以使用在这个公共函数中使用单元测试不仅可以测试当前代码逻辑,也可帮助往后再开发时的效率。

被测源代码如下:

可以看到被测的公共函数就只有一个入参,两个出参,还算比较简单。

利用go自带框架,最简单的单测代码即可如下:

然后在同目录下输入命令

$go test -v XXX_test.go

其中,-v选项可用于打印每个测试函数的名字和运行时间。

正常情况下,是会出现单测成功或者失败的信息。但这个例子有点不一样,终端会出现这样的错误:

错误里提及component.url配置项并未设置,这是因为这个例子需要依赖外部配置文件,配置文件有设置component.url。

这里,可以使用-o选项。在某些依赖配置文件的情况下-o很有用

go test命令会先生成一个测试可执行文件,如果没-o会是一个临时目录(/tmp/go-build266773839/command-line-arguments),然后在这个临时目录下执行测试的可执行文件,如果使用-o选项:

可以看到可执行文件就会指定在生成可执行测试文件的地方执行(也即是-o指定的目录下执行)。

从这个示例中可以看到,testing包提供了Logf和Error等方法来帮助提高测试效率,如果是Error方法的分支被执行了,则这组测试示例会失败,Logf则是在标准输出上输出log信息。

示例到这里,单测代码也写好了,单测执行也成功了,是否意味着单测就完成。其实不是,个人认为单测的目的主要在于在最小的模块内覆盖所有可能逻辑测试,使得更复杂的模块集成后降低bug的风险。要覆盖被测模块中更多的代码,则需要更多的参数组(测试用例)。实现这个最简单的方法就是多写几个TestXXX,go语言提供了表格驱动的测试方式,把所有测试数据合并到了一个测试中的表格中再集中测试。

go语言中所谓的表格驱动,就是把输入和预期输出做成一个表格,很容易向表格添加新的测试数据,并且后面的测试逻辑也没有冗余,这样我们可以有更多的精力地完善错误信息,比如上例中单元测试是写成类似形式:

tests结构即是测试表格,这样即可以测试是否和预期的输入及输出一致。但是因为本示例中被测函数中的返回值复杂,为了简化单元测试(返回的error不易比对),最终的单元测试是使用t.Logf来查看:

func TestQueryClusterVip(t *testing.T){
    darwinService := component.NewDarwinService(9)
        //测试表格
    var tests = []struct {
        input int64
        rsp *QueryClusterGroupVipRsp
        err error
    }{
        {900086,&QueryClusterGroupVipRsp{Vip:"10.66.188.221"},nil},
        {0,nil,nil},
    }

    for _,test := range tests{
        rsp,err := darwinService.QueryClusterVip(test.input)
        t.Logf("rsp:%v",rsp)
        t.Logf("err:%v",err)
    }
}

go自带的测试框架还提供了测试覆盖率和基准测试两个工具帮助查看编写代码和单测的质量和效率,详见

如果使用惯了了xUnit(JUnit、CppUnitdeng)的单元测试,开始使用go自带的测试框架,会觉得怪怪的,心里满是疑问,为哈单测连都没有断言,表示不服,但是开源社区的gocheck提供了。像Assert简直新手拈来:

func Test(t *testing.T) { TestingT(t) }

type MyTest struct{}

var _ = Suite(&MyTest{})

func (s *Test) TestHelloWorld(c *C) {
    c.Assert(42, Equals, "42")
    c.Assert(io.ErrClosedPipe, ErrorMatches, "io: .*on closed pipe")
    c.Check(42, Equals, 42)
}

这样让单测可以看起来简洁不少,更详细功能和用法可查看其官网

一般来说使用go语言自带的测试框架可以使用单测覆盖60%以上的代码。如果代码中并没有自带http请求,那么测试的时候就需要自己编写代码去构造http请求,。还有一种很常见的场景就是需要测试的代码中依赖了外部资源,而这些外部资源不像本例单测环境里不能提供或者提供特别繁琐,比如网络资源、DB资源等,就需要使用其他测试框架中常用的mock,coverage等技术,而这些go自带测试框架并不提供。

下一篇将介绍以伪对象(Mock,Stub等)核心技术为主的第三方测试框架来补充,以拟补go语言自带的测试框架的不足。

相关推荐

go单元测试进阶篇

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏坚毅的PHP

my python FAQ

python编码规范 http://google-styleguide.googlecode.com/svn/trunk/pyguide.html 判断对象是否...

3287
来自专栏V站

Note丨记41条Web程序员日常使用的代码!

1908
来自专栏逆向技术

脱壳第三讲,UPX压缩壳,以及补充壳知识

           脱壳第三讲,UPX压缩壳,以及补充壳知识 一丶什么是压缩壳.以及壳的原理 在理解什么是压缩壳的时候,我们先了解一下什么是壳 1.什么是壳 ...

2198
来自专栏hotqin888的专栏

jQueryGantt—集变态与惊艳于一身

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

982
来自专栏IT技术精选文摘

教你如何监控网站URL是否正常

3095
来自专栏恰同学骚年

使用VS2013分析DMP文件

当一个发布的.NET应用程序出现app crash,无法通过日志分析异常原因时,就需要通过分析DMP文件了,传统方式是通过WinDbg来分析DMP文件,但是Wi...

842
来自专栏极客编程

如何用Python为以太坊和比特币生成vanity地址

今天,我们将编写一个非常简单的python脚本来生成虚荣地址,这些地址是以某个短语或字母序列开头的加密货币地址。该过程涉及生成私钥并检查目标短语的地址,直到找到...

663
来自专栏叁金大数据

自学Python八 爬虫大坑之网页乱码

  Bug有时候破坏的你的兴致,阻挠了保持到现在的渴望。可是,自己又非常明白,它是一种激励,是注定要被你踩在脚下的垫脚石!

701
来自专栏CaiRui

Shell-2-命令之乐

1.cat (1)基本用法 [root@cai tmp]# cat 1.txt 2.txt this is a test1 this is a test 2...

2055
来自专栏州的先生

使用Phantomjs模拟登录QQ空间

1132

扫码关注云+社区