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 条评论
登录 后参与评论

相关文章

来自专栏陈少文的专栏

apidoc 实践和自动化生成

在前后端分离框架中,API 文档频繁交接。如果涉及到第三方接口调用,多方合作场景下,API 和文档变更可能会更快。为了方便维护 API 和交接文档,这里给大家推...

1.3K0
来自专栏腾讯IVWEB团队的专栏

Docsify 深入源码

文档站点一般作为各行各业领域内的知识技术介绍及使用的资料站点,可提高资料的使用效率,保证资料的质量。今天我们详细介绍下 docsify 的使用文档及实现原理。

3790
来自专栏禅林阆苑

Vue2.5源码阅读笔记01—代码结构与初始化

Vue作为当下最流行的渐进式的js框架,其渐进式的思想、虚拟DOM的运用、组件化的开发模式、响应式数据侦听原理值得开发者进行探索学习,其中运用的代码组织的技巧,...

65759
来自专栏Golang语言社区

实效go编程--4

func handle(queue chan *Request) { for r := range queue { process(r)...

34215
来自专栏从零开始学自动化测试

Selenium+python自动化83-chrome手机wap模式

一、前言 这里是群里(QQ群:226296743)风神出60大洋悬赏的问题,学会这篇就是赚了60大洋了! 我的环境: - chrome 62 - chrome...

3154
来自专栏hotqin888的专栏

bootstrap treeview级联勾选的正确姿势

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

711
来自专栏大史住在大前端

webpack4.0各个击破(6)—— Loader篇

loader是webpack的核心概念之一,它的基本工作流是将一个文件以字符串的形式读入,对其进行语法分析及转换(或者直接在loader中引入现成的编译工具,例...

781
来自专栏Java学习123

HTTP 头部详细解释

3488
来自专栏前端儿

【转】http-equiv="X-UA-Compatible" 设置IE浏览器兼容模式详解

文件兼容性用于定义让IE如何编译你的网页。此文件解释文件兼容性,如何指定你网站的文件兼容性模式以及如何判断一个网页该使用的文件模式。

891
来自专栏carven

前端模块化开发

其实对前端模块化开发的接触时间并不多,很多见解都是别人的,或者是偏的, 还是乐意记录下来,谁让我一天一个念头 说到前端模块化开发,其实是说 javascript...

780

扫码关注云+社区