unit-testing - 表驱动测试的子集

原文 标签 unit-testing testing go tdd

对于测试功能,我可以选择由-run选项运行的功能。

go test -run regex

很常见,如果我们将数十个测试用例放入数组中,以免为每个函数编写函数:
cases := []struct {
  arg, expected string
} {
    {"%a", "[%a]"},
    {"%-a", "[%-a]"},
    // and many others
}
for _, c := range cases {
  res := myfn(c.arg) 
  if  res != c.expected {
    t.Errorf("myfn(%q) should return %q, but it returns %q", c.arg, c.expected, res)
  }
}

这项工作很好,但是维护方面存在问题。当我添加一个新的测试用例时,在调试时我只想启动一个新的测试用例,但是我不能这样说:
go test -run TestMyFn.onlyThirdCase

有什么优雅的方法,如何在阵列中具有多个测试用例以及选择运行哪个测试用例的能力?

最佳答案

使用Go 1.6(及更低版本)
Go 1.6及更低版本中的 testing 软件包不直接支持此功能。您必须自己实施。
但这并不难。您可以使用 flag 包轻松访问命令行参数。
让我们来看一个例子。我们定义一个"idx"命令行参数,如果存在该参数,则仅执行该索引处的用例,否则执行所有测试用例。
定义标志:

var idx = flag.Int("idx", -1, "specify case index to run only")
解析命令行标志(实际上,这不是必需的,因为go test已经调用了此标志,但只是为了确保/完成):
func init() {
    flag.Parse()
}
使用此参数:
for i, c := range cases {
    if *idx != -1 && *idx != i {
        println("Skipping idx", i)
        continue
    }
    if res := myfn(c.arg); res != c.expected {
        t.Errorf("myfn(%q) should return %q, but it returns %q", c.arg, c.expected, res)
    }
}
用3个测试用例进行测试:
cases := []struct {
    arg, expected string
}{
    {"%a", "[%a]"},
    {"%-a", "[%-a]"},
    {"%+a", "[%+a]"},
}
没有idx参数:
go test
输出:
PASS
ok      play    0.172s
指定索引:
go test -idx=1
输出:
Skipping idx 0
Skipping idx 2
PASS
ok      play    0.203s
当然,您可以实施更复杂的过滤逻辑,例如您可以使用minidxmaxidx标志来运行范围内的案例:
var (
    minidx = flag.Int("minidx", 0, "min case idx to run")
    maxidx = flag.Int("maxidx", -1, "max case idx to run")
)
和过滤:
if i < *minidx || *maxidx != -1 && i > *maxidx {
    println("Skipping idx", i)
    continue
}
使用它:
go test -maxidx=1
输出:
Skipping idx 2
PASS
ok      play    0.188s
从Go 1.7开始
Go 1.7(将于2016年8月18日发布)添加了subtests and sub-benchmarks的定义:

The testing package now supports the definition of tests with subtests and benchmarks with sub-benchmarks. This support makes it easy to write table-driven benchmarks and to create hierarchical tests. It also provides a way to share common setup and tear-down code. See the package documentation for details.


这样,您可以执行以下操作:
func TestFoo(t *testing.T) {
    // <setup code>
    t.Run("A=1", func(t *testing.T) { ... })
    t.Run("A=2", func(t *testing.T) { ... })
    t.Run("B=1", func(t *testing.T) { ... })
    // <tear-down code>
}
子测试的名称分别为"A=1""A=2""B=1"

The argument to the -run and -bench command-line flags is a slash-separated list of regular expressions that match each name element in turn. For example:

go test -run Foo     # Run top-level tests matching "Foo".
go test -run Foo/A=  # Run subtests of Foo matching "A=".
go test -run /A=1    # Run all subtests of a top-level test matching "A=1".

这对您的案件有何帮助?子测试的名称是string值,可以即时生成,例如:
for i, c := range cases {
    name := fmt.Sprintf("C=%d", i)
    t.Run(name, func(t *testing.T) {
        if res := myfn(c.arg); res != c.expected {
            t.Errorf("myfn(%q) should return %q, but it returns %q",
                c.arg, c.expected, res)
        }
    })
}
要在索引2处运行该案例,您可以像这样启动它
go test -run /C=2
要么
go test -run TestName/C=2

相关文章:

testing - 对于软件测试人员来说,这是一个很好的面试问题吗?

json - 如何在Go中自动向JSON添加类型字段?

go - 使用golang channel 的结果不一致

java - 如何在JUnit中打印错误的结果?

javascript - 下面的 Angular 方法如何编写测试用例

android - 基于gradle的项目缺少RobolectricContext

node.js - postman Newman如何在使用node.js时使用“选项”进行配置

unit-testing - 如何对Gin Gonic中的cookie进行单元测试提取

java - 如何使Mockito参数匹配方法签名

go - 如何使用paho.mqtt.golang库订阅多个MQTT主题?