12

当我在编写一个接口时,通常很方便地将我的测试定义在与接口相同的包中,然后定义多个实现接口集的包,例如。

package/
package/impl/x <-- Implementation X
package/impl/y <-- Implementation Y

有没有一种简单的方法可以在子包中运行相同的测试套件(在这种情况下,位于package/*_test.go)?

到目前为止我想出的最好的解决方案是添加一个测试包:

package/tests/

它实现了测试套件,并在每个实现中进行了测试以运行测试,但这有两个缺点:

1) 中的测试package/tests不在_test.go文件中,最终成为实际库的一部分,由godoc等记录。

2) 中的测试package/tests由自定义测试运行器运行,它必须基本上复制所有功能go test以扫描 go 测试并运行它们。

似乎是一个非常俗气的解决方案。

有没有更好的方法来做到这一点?

4

3 回答 3

7

我真的不喜欢使用单独的测试库的想法。如果您有一个接口并且您对每个接口都有通用测试,那么实现该接口的其他人可能也喜欢使用这些测试。

您可以创建一个"package/test"包含函数的包

// functions needed for each implementation to test it
type Tester struct {
    func New() package.Interface
    func (*package.Interface) Done()
    // whatever you need. Leave nil if function does not apply
}

func TestInterface(t *testing.T, tester Tester)

请注意, 的签名与预期的TestInterface不匹配。go test现在,为每个包package/impl/x添加一个文件generic_test.go

package x

import "testing"
import "package/test"

// run generic tests on this particular implementation
func TestInterface(t *testing.T) {
    test.TestInterface(t,test.Tester{New:New})
}

New()您的实现的构造函数在哪里。这种方案的优点是

  1. 你的测试对于任何实现你的接口的人来说都是可重用的,即使来自其他包
  2. 很明显,您运行了通用测试套件
  3. 测试用例是实现的地方,而不是另一个晦涩的地方
  4. 如果一个实现需要特殊的初始化或类似的东西,代码可以很容易地调整
  5. 它是go test兼容的(大加!)

当然,在某些情况下你需要一个更复杂的TestInterface函数,但这是基本思想。

于 2013-04-09T15:14:09.850 回答
1

如果您共享一段代码以供不同包重用,那么是的,根据定义,它是一个库。即使仅用于从 *_test.go 文件进行测试。这与在 _test.go 文件中导入“fmt”的“测试”没有什么不同。并且有 godoc 记录的 API 是一个加号,而不是减号恕我直言。

于 2013-04-09T09:12:10.920 回答
1

也许有些东西在这里混淆了:如果包 a 只定义了一个接口,那么就没有代码可以测试,因为 Go 中的接口是免费的。

所以我假设你的接口中的方法包 a 有约束。例如在

interface Walker {
    Walk(step int)
    Tired() bool
}

您的合同假设如果已步行超过 500 个步骤,则 Tired 返回 true(否则为 false),并且您的测试代码会检查这些依赖关系(或假设、合同、不变量,无论您如何命名)。

如果是这种情况,我将提供(在包 a 中)一个导出函数

func TestWalkerContract(w Walker) error {
    w.Walk(100)
    if w.Tired() { return errors.New("Tired after 100 steps") }
    w.Walk(450)
    if !w.Tired() { return errors.New("Not tired after 100+450 steps") }
}

它正确地记录了合同,并且可以由具有实现 walker 的类型的包 b 和 c 使用,以测试它们在 b_test.go 和 c_test.go 中的实现。恕我直言,这些函数(如 TestWalkerContract)由 godoc 显示是完全可以的。

PS 比 Walk and Tired 更常见的可能是错误状态,该错误状态会一直保留并报告,直到清除/重置。

于 2013-04-17T07:51:48.887 回答