Как разделить тестовые интерфейсы между пакетами Go?
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
поэтому мы определяем testing/quick.Generate
на Thing
:
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
возвращается Thing
s в диапазоне [0,3), как и ожидалось:
$ 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
также:
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
сломано. foo_test.go
недоступно для bar_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
(или любая другая библиотека тестирования с интерфейсами) на практике?
1 ответ
Любой код, совместно используемый пакетами, должен находиться в не тестовом файле. Это не значит, что он должен быть включен в какие-либо окончательные сборки; Вы можете использовать ограничения сборки, чтобы исключить файлы из обычных сборок, и создавать теги, чтобы включать их при запуске тестов. Например, вы можете поместить свой общий тестовый код в файл с префиксом:
//+build testtools
package mypackage
(но не называется _test.go). При сборке это не будет включено в сборку. Когда вы тестируете, вы будете использовать что-то вроде:
go test -tags "testtools" ./...
Это включит ограниченный файл в сборку и тем самым сделает общий код доступным для тестов.