如何在Go包之间共享测试接口?

huangapple go评论151阅读模式
英文:

How to share test interfaces between Go packages?

问题

Go在不同包的测试文件之间不共享代码,因此测试接口的定义不会自动重用。在实践中,我们如何解决这个问题呢?

使用testing/quick的示例:

foo/foo.go:

package foo

type Thing int

const (
  X Thing = iota
  Y
  Z
)

bar/bar.go:

package bar

import (
  "foo"
)

type Box struct {
  Thing foo.Thing
}

我们想要对foo进行属性测试,因此我们在Thing上定义了testing/quick.Generate

foo_test.go:

package foo

import (
  "math/rand"
  "reflect"
  "testing"
  "testing/quick"
  "time"
)

func (_ Thing) Generate(r *rand.Rand, sz int) reflect.Value {
  return reflect.ValueOf(Thing(r.Intn(3)))
}

func TestGenThing(t *testing.T) {
  r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
  for i := 0; i < 5; i++ {
    val, _ := quick.Value(reflect.TypeOf(Thing(0)), r)
    tng, _ := val.Interface().(Thing)
    t.Logf("%#v\n", tng)
  }
}

quick.Value按预期返回范围在[0,3)的Thing

$ go test -v foo
=== RUN   TestGenThing
--- PASS: TestGenThing (0.00s)
        foo_test.go:20: 0
        foo_test.go:20: 1
        foo_test.go:20: 2
        foo_test.go:20: 1
        foo_test.go:20: 2
PASS
ok      foo     0.026s

让我们也对bar进行属性测试:

bar/bar_test.go:

package bar

import (
  "math/rand"
  "reflect"
  "testing"
  "testing/quick"
  "time"

  "foo"
)

func (_ Box) Generate(r *rand.Rand, sz int) reflect.Value {
  val, _ := quick.Value(reflect.TypeOf(foo.Thing(0)), r)
  tng, _ := val.Interface().(foo.Thing)
  return reflect.ValueOf(Box{tng})
}

func TestGenBox(t *testing.T) {
  r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
  for i := 0; i < 5; i++ {
    val, _ := quick.Value(reflect.TypeOf(Box{}), r)
    box, _ := val.Interface().(Box)
    t.Logf("%#v\n", box)
  }
}

但是Box.Generate是有问题的。bar_test.gofoo_test.go不可见,因此quick.Value()不会使用Thing.Generate()

$ GOPATH=$PWD go test -v bar
=== RUN   TestGenBox
--- PASS: TestGenBox (0.00s)
        bar_test.go:24: bar.Box{Thing:3919143124849004253}
        bar_test.go:24: bar.Box{Thing:-3486832378211479055}
        bar_test.go:24: bar.Box{Thing:-3056230723958856466}
        bar_test.go:24: bar.Box{Thing:-847200811847403542}
        bar_test.go:24: bar.Box{Thing:-2593052978030148925}
PASS
ok      bar     0.095s

有没有解决这个问题的方法?人们在实践中如何使用testing/quick(或任何其他带有接口的测试库)?

英文:

Go doesn't share code between test files of different packages, so definitions of test interfaces aren't automatically reused. How can we work around this in practice?

Example using testing/quick:

foo/foo.go:

package foo

type Thing int

const (
  X Thing = iota
  Y
  Z
)

bar/bar.go:

package bar

import (
  &quot;foo&quot;
)

type Box struct {
  Thing foo.Thing
}

We want to property test foo, so we define testing/quick.Generate on Thing:

foo_test.go:

package foo

import (
  &quot;math/rand&quot;
  &quot;reflect&quot;
  &quot;testing&quot;
  &quot;testing/quick&quot;
  &quot;time&quot;
)

func (_ Thing) Generate(r *rand.Rand, sz int) reflect.Value {
  return reflect.ValueOf(Thing(r.Intn(3)))
}

func TestGenThing(t *testing.T) {
  r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
  for i := 0; i &lt; 5; i++ {
    val, _ := quick.Value(reflect.TypeOf(Thing(0)), r)
    tng, _ := val.Interface().(Thing)
    t.Logf(&quot;%#v\n&quot;, tng)
  }
}

quick.Value returns Things in the range [0,3) as expected:

$ go test -v foo
=== RUN   TestGenThing
--- PASS: TestGenThing (0.00s)
        foo_test.go:20: 0
        foo_test.go:20: 1
        foo_test.go:20: 2
        foo_test.go:20: 1
        foo_test.go:20: 2
PASS
ok      foo     0.026s

Let's property test bar as well:

package bar

import (
  &quot;math/rand&quot;
  &quot;reflect&quot;
  &quot;testing&quot;
  &quot;testing/quick&quot;
  &quot;time&quot;

  &quot;foo&quot;
)

func (_ Box) Generate(r *rand.Rand, sz int) reflect.Value {
  val, _ := quick.Value(reflect.TypeOf(foo.Thing(0)), r)
  tng, _ := val.Interface().(foo.Thing)
  return reflect.ValueOf(Box{tng})
}

func TestGenBox(t *testing.T) {
  r := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
  for i := 0; i &lt; 5; i++ {
    val, _ := quick.Value(reflect.TypeOf(Box{}), r)
    box, _ := val.Interface().(Box)
    t.Logf(&quot;%#v\n&quot;, box)
  }
}

But Box.Generate is broken. foo_test.go isn't available to bar_test.go, so quick.Value() doesn't use Thing.Generate():

$ GOPATH=$PWD go test -v bar
=== RUN   TestGenBox
--- PASS: TestGenBox (0.00s)
        bar_test.go:24: bar.Box{Thing:3919143124849004253}
        bar_test.go:24: bar.Box{Thing:-3486832378211479055}
        bar_test.go:24: bar.Box{Thing:-3056230723958856466}
        bar_test.go:24: bar.Box{Thing:-847200811847403542}
        bar_test.go:24: bar.Box{Thing:-2593052978030148925}
PASS
ok      bar     0.095s

Is there a workaround for this? How do folks use testing/quick (or any other testing library with interfaces) in practice?

答案1

得分: 5

不同包之间共享的代码必须位于非测试文件中。这并不意味着它必须包含在任何最终构建中;你可以使用构建约束来排除这些文件在正常构建中的使用,并使用构建标签在运行测试时包含它们。例如,你可以将共享的测试代码放在一个以以下方式为前缀的文件中:

//+build testtools

package mypackage

(但不要命名为_test.go)。当你构建时,这个文件将不会被包含在构建中。当你进行测试时,你可以使用类似以下的命令:

go test -tags "testtools" ./...

这将在构建中包含受约束的文件,并使共享的代码对测试可用。

英文:

Any code shared between packages must be in a non-test file. That doesn't mean it has to be included in any final builds though; you can use build constraints to exclude the files from normal builds, and build tags to include them when running tests. For example, you can put your shared test code in a file prefixed with:

//+build testtools

package mypackage

(but not named _test.go). When you build, this will not be included in the build. When you test, you'd use something like:

go test -tags &quot;testtools&quot; ./...

This would include the constrained file in the build, and thereby make the shared code available to the tests.

huangapple
  • 本文由 发表于 2017年5月26日 10:31:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/44192688.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定