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(及更低版本)

testing 不直接支持此功能Go 1.6 及以下版本中的包。您必须自己实现它。

但这并不难。您可以使用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".

这对您的案件有何帮助?子测试的名称是字符串值,可以即时生成,例如:

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

关于unit-testing - 表驱动测试的子集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38911566/

相关文章:

arrays - Go Lang - 接口(interface)和数组

go - 尝试使用 interface{} slice 来选择随机元素

Python:如何测试是否使用正确的参数调用构造函数

unit-testing - Spark 单元测试不适用于 powermockito

unit-testing - 单元测试和 Web 应用程序 - 资源

java - 如何在 Espresso 中以编程方式单击 Android 手机 "home"按钮

javascript - 如何测试包裹在另一个连接组件中的连接组件?

ruby - 在 Ruby 中测试应用程序 API 库的最佳方法是什么?

php - 测试程序和过程

golang中的字符串拆分