原文:https://gocn.io/article/366
GOLANG测试时,可以用匿名对象填充测试序列,但是如果没有带堆栈的errors,那么会造成出现错误时无法排查。
先看测试序列的填充,参考tricks-2015这个文章,怕你翻不了墙,我把内容粘贴过来就是:
Anonymous structs: test cases (1/2)
These properties enable a nice way to express test cases:
func TestIndex(t *testing.T) {
var tests = []struct {
s string
sep string
out int
}{
{"","",0},{"","a",-1},{"fo","foo",{"foo",{"oofofoofooo","f",2},// etc
}
for _,test := range tests {
actual := strings.Index(test.s,test.sep)
if actual != test.out {
t.Errorf("Index(%q,%q) = %v; want %v",test.s,test.sep,actual,test.out)
}
}
}
是的,看起来很方便,出错时也知道哪里的问题,但是实际上如果序列中有函数,那就悲剧了。让我们来试试,考虑一个包头的定义,它就是两个字段,然后序列化成[]byte
:
type MyHeader struct {
Version uint8
Size uint16
}
func (v MyHeader) MarshalBinary() ([]byte,error) {
return []byte{byte(v.Version),0,0},nil // Failed.
}
为了测试设置不同的值,得到不同的字节,我们用两个函数来填充测试序列:
func TestMyHeader_MarshalBinary(t *testing.T) {
mhs := []struct {
set func(h *MyHeader)
compare func(p []byte) error
}{
{func(h *MyHeader) { h.Size = 1 },func(p []byte) error {
if p[1] != 0x01 {
return fmt.Errorf("p[1] is %v",p[1]) // line 194
}
return nil
}},{func(h *MyHeader) { h.Size = 2 },func(p []byte) error {
if p[1] != 0x02 {
return fmt.Errorf("p[1] is %v",p[1]) // line 200
}
return nil
}},}
for _,mh := range mhs {
h := &MyHeader{}
mh.set(h)
if b,err := h.MarshalBinary(); err != nil {
t.Errorf("error is %+v",err)
} else if err = mh.compare(b); err != nil {
t.Errorf("invalid data,err is %+v",err) // line 211
}
}
}
结果我们就懵逼了,出现的错误行数都是在error那个地方211
行是不够的,还需要知道是194还是200出问题了:
--- FAIL: TestMyHeader_MarshalBinary (0.00s)
iprouter_test.go:211: invalid data,err is p[1] is 0
iprouter_test.go:211: invalid data,err is p[1] is 0
怎么办呢?把堆栈信息带上,参考错误最佳实践,改成这样:
import oe "github.com/ossrs/go-oryx-lib/errors"
创建error时用这个package:
if p[1] != 0x01 {
return oe.Errorf("p[1] is %v",p[1]) // line 194
}
if p[1] != 0x02 {
return oe.Errorf("p[1] is %v",p[1]) // line 200
}
结果可以看到详细的堆栈:
iprouter_test.go:211: invalid data,err is p[1] is 0
_/Users/winlin/git/test/src/core.TestMyHeader_MarshalBinary.func4
/Users/winlin/git/test/src/core_test.go:200
_/Users/winlin/git/test/src/core.TestMyHeader_MarshalBinary
/Users/winlin/git/test/src/core_test.go:210
testing.tRunner
/usr/local/Cellar/go/1.8.1/libexec/src/testing/testing.go:657
runtime.goexit
/usr/local/Cellar/go/1.8.1/libexec/src/runtime/asm_amd64.s:2197
这样可以嵌套非常多的函数做测试了。