简介

GoConvey类似于C/C++语言的测试框架GTest,是一款针对Golang的测试框架,可以管理和运行测试用例,同时提供了丰富的断言函数,并支持很多 Web 界面特性。

Golang虽然自带了单元测试功能,并且在GoConvey框架诞生之前也出现了许多第三方测试框架,但没有一个测试框架像GoConvey一样能够让程序员如此简洁优雅的编写测试代码。

安装

在命令行输入命令:

sudo go get github.com/smartystreets/goconvey

相关文档 基本使用方法

我们通过一个案例来介绍GoConvey框架的基本使用方法,并对要点进行归纳。

产品代码

下面是一个能够实现整数基本四则运算(加、减、乘、除)的代码:

package goconvey

import (
    "errors"
)

func Add(a int, b int) int {
    c := a + b
    return c
}

func Subtract(a, b int) int {
    return a - b
}

func Multiply(a, b int) int {

    return a * b
}

func Division(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("被除数不能为 0")
    }
    return a / b, nil
}

测试代码

package goconvey

import (
    . "github.com/smartystreets/goconvey/convey"
    "testing"
)

func TestAdd(t *testing.T) {
    Convey("将两数相加", t, func() {
        So(Add(1, 2), ShouldEqual, 3)
    })
}

func TestSubtract(t *testing.T) {
    Convey("将两数相减", t, func() {
        So(Subtract(1, 2), ShouldEqual, -1)
    })
}

func TestMultiply(t *testing.T) {
    Convey("将两数相乘", t, func() {
        So(Multiply(3, 2), ShouldEqual, 6)
    })
}

func TestDivision(t *testing.T) {
    Convey("将两数相除", t, func() {

        Convey("除以非 0 数", func() {
            num, err := Division(10, 2)
            So(err, ShouldBeNil)
            So(num, ShouldEqual, 5)
        })

        Convey("除以 0", func() {
            _, err := Division(10, 0)
            So(err, ShouldNotBeNil)
        })
    })
}

上面的测试用例代码有如下几个要点:

  1. import goconvey包时,前面加点号”.”,以减少冗余的代码。凡是在测试代码中看到Convey和So两个方法,肯定是convey包的,不要在产品代码中定义相同的函数名
  2. 测试函数的名字必须以Test开头,而且参数类型必须为*testing.T
  3. 每个测试用例必须使用Convey函数包裹起来,它的第一个参数为string类型的测试描述,第二个参数为测试函数的入参(类型为*testing.T),第三个参数为不接收任何参数也不返回任何值的函数(习惯使用闭包)
  4. Convey函数的第三个参数闭包的实现中通过So函数完成断言判断,它的第一个参数为实际值,第二个参数为断言函数变量,第三个参数或者没有(当第二个参数为类ShouldBeTrue形式的函数变量)或者有(当第二个函数为类ShouldEqual形式的函数变量)

运行测试

现在,您可以打开命令行,然后输入 go test -v 来进行测试。由于 GoConvey 兼容 Go 原生的单元测试,因此我们可以直接使用 Go 的命令来执行测试。

以下便是命令行的输出(Mac):

=== RUN   TestAdd

  将两数相加 ✔


1 total assertion

--- PASS: TestAdd (0.00s)
=== RUN   TestSubtract

  将两数相减 ✔


2 total assertions

--- PASS: TestSubtract (0.00s)
=== RUN   TestMultiply

  将两数相乘 ✔


3 total assertions

--- PASS: TestMultiply (0.00s)
=== RUN   TestDivision

  将两数相除
    除以非 0 数 ✔✔
    除以 0 ✔


6 total assertions

--- PASS: TestDivision (0.00s)
PASS
ok      helloworld/goconvey 0.006s

我们可以看到,输出结果调理非常清晰,单元测试的代码写起来也非常优雅。那么,这就是全部吗?当然不是。GoConvey 不仅支持在命令行进行人工调用调试命令,还有非常舒适的 Web 界面提供给开发者来进行自动化的编译测试工作。

Web 界面

GoConvey不仅支持在命令行进行自动化编译测试,而且还支持在 Web 界面进行自动化编译测试。想要使用GoConvey的 Web 界面特性,需要在测试文件所在目录下执行goconvey:

$GOPATH/bin/goconvey

这时候会弹出界面如下:
这里写图片描述

在 Web 界面中:

可以设置界面主题
查看完整的测试结果
使用浏览器提醒等实用功能
自动检测代码变动并编译测试
半自动化书写测试用例
查看测试覆盖率
临时屏蔽某个包的编译测试

Skip

针对想忽略但又不想删掉或注释掉某些断言操作,GoConvey提供了Convey/So的Skip方法:

  • SkipConvey函数表明相应的闭包函数将不被执行
  • SkipSo函数表明相应的断言将不被执行
    当存在SkipConvey或SkipSo时,测试日志中会显式打上”skipped”形式的标记:

  • 当测试代码中存在SkipConvey时,相应闭包函数中不管是否为SkipSo,都将被忽略,测试日志中对应的符号仅为一个”⚠”

  • 当测试代码Convey语句中存在SkipSo时,测试日志中每个So对应一个”✔”或”✘”,每个SkipSo对应一个”⚠”,按实际顺序排列
  • 不管存在SkipConvey还是SkipSo时,测试日志中都有字符串”{n} total assertions (one or more sections skipped)”,其中{n}表示测试中实际已运行的断言语句数
定制断言函数

我们先看一下So的函数原型:

func So(actual interface{}, assert assertion, expected ...interface{})

第二个参数为assertion,它的原型为:

type assertion func(actual interface{}, expected ...interface{}) string

当assertion的返回值为”“时表示断言成功,否则表示失败,GoConvey框架中的相关代码为:

const (
    success                = ""
    needExactValues        = "This assertion requires exactly %d comparison values (you provided %d)."
    needNonEmptyCollection = "This assertion requires at least 1 comparison value (you provided 0)."
)

我们简单实现一个assertion函数:

func ShouldSummerBeComming(actual interface{}, expected ...interface{}) string {
    if actual == "summer" && expected[0] == "comming" {
        return ""
    } else {
        return "summer is not comming!"
    }
}

我们仍然在slice_test文件中写一个简单测试:

func TestSummer(t *testing.T) {
    Convey("TestSummer", t, func() {
        So("summer", ShouldSummerBeComming, "comming")
        So("winter", ShouldSummerBeComming, "comming")
    })
}

根据ShouldSummerBeComming的实现,Convey语句中第一个So将断言成功,第二个So将断言失败。
我们运行测试,查看执行结果,符合期望:

=== RUN   TestSummer

  TestSummer ✔✘


Failures:

  * /Users/zhangxiaolong/Desktop/D/go-workspace/src/infra/alg/slice_test.go 
  Line 52:
  summer is not comming!


2 total assertions

--- FAIL: TestSummer (0.00s)
FAIL
exit status 1
FAIL    infra/alg       0.006s
小结

Golang虽然自带了单元测试功能,但笔者建议大家使用已经成熟的第三方测试框架。本文主要介绍了GoConvey框架,通过文字结合代码示例讲解基本的使用方法,要点归纳如下:

  1. import goconvey包时,前面加点号”.”,以减少冗余的代码
  2. 测试函数的名字必须以Test开头,而且参数类型必须为*testing.T
  3. 每个测试用例必须使用Convey函数包裹起来,推荐使用Convey语句的嵌套,即一个函数有一个测试函数,测试函数中嵌套两级Convey语句,第一级Convey语句对应测试函数,第二级Convey语句对应测试用例
  4. Convey语句的第三个参数习惯以闭包的形式实现,在闭包中通过So语句完成断言
  5. 使用GoConvey框架的 Web 界面特性,作为命令行的补充
  6. 在适当的场景下使用SkipConvey函数或SkipSo函数
  7. 当测试中有需要时,可以定制断言函数
    至此,希望读者已经掌握了GoConvey框架的基本用法,从而可以写出简单优雅的测试代码。

参考文档如下:
https://studygolang.com/articles/9801
https://studygolang.com/articles/1513