如何在多个包中使用通用测试套件?

时间:2013-04-09 09:07:34

标签: go

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

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

是否有一种简单的方法可以在子包中运行相同的测试套件(在本例中,位于package / * _ test.go中)?

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

package/tests/

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

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

2)包/测试中的测试由自定义测试运行器运行,该测试运行器必须基本上复制&#39; go test&#39;的所有功能。扫描go测试并运行它们。

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

有更好的方法吗?

3 个答案:

答案 0 :(得分: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兼容(大加!)
  6. 当然,在某些情况下,您需要一个更复杂的TestInterface函数,但这是基本的想法。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

也许有些事情在这里混淆了一下: 如果package a只定义了一个接口,那么就没有代码 测试作为Go中的接口是免费实现的。

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

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

你的合同假设Tired如果超过则返回true 已经走了500步(否则为假) 并且您的测试代码会检查这些依赖项 (或假设,合同,不变量,不管你 把它命名。)

如果是这种情况,我会提供(在包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") }
}

哪些文件合同正确并且可以由包使用 b和c,其中类型实现了walker来测试它们的实现 在b_test.go和c_test.go中。恕我直言,这些都是完全可以的 像godW一样显示TestWalkerContract之类的函数。

P.S。比Walk和Tired更常见的可能是错误状态 保存并报告,直到清除/重置。