问题描述
我必须为几个具有相似签名和返回值(一个对象和一个错误)的函数编写单元测试,它们必须通过类似的测试条件。
我想避免编写:
func TestFunc1(t * testing.T){
//测试返回值
$ b $ func TestFunc2(t * testing.T){
//测试相同的Func1
}
func TestFunc3(t * testing.T){
//测试结果与Func1相同
code $ $ $ $ $ $ $ $ $ $ $ $ $(参见这个go playground例子更完整的上下文)
(yes,go游乐场还不支持 go test
,只有 go run
和是否有申请该功能的要求)
你会如何使用反射(),以便只写一个测试:
- 依次调用每个函数?
- 测试它们的返回值吗?
我见过:
-
但是我错过了一个调用函数和在测试中使用返回值的完整示例。
解决方案一旦我了解到所有内容都必须使用或返回,此处我我想到了什么。
诀窍是使用:
- 以获取接收者的值
- 找到该接收者的一个函数
- 来测试
nil
返回的值。 li>
测试代码的主要提取:
var funcNames = [] string {Func1,Func2,Func3}
func TestFunc .t){
stype:= reflect.ValueOf(s)
for _,fname:= range funcNames {
fmt.Println(fname)
sfunc:= stype.MethodByName(fname)
//无参数=>空价值
ret:= sfunc.Call([] reflect.Value {})
val:= ret [0] .Int()
/ /那会恐慌为零返回err
// err:= ret [1] .Interface()。(error)
err:= ret [1]
如果val< 1 {
t.Error(fname +应该返回正值)
}
if err.IsNil()== false {
t.Error(fname +shouldn 't err)
}
}
}
请参阅。
请注意,如果您使用不存在的函数名称调用该测试函数,则会惊慌失措。
请参阅。
runtime.panic(0x126660,0x10533140)
/tmp/sandbox/go/src/pkg/runtime/panic.c:266 + 0xe0
testing.func ·005()
/tmp/sandbox/go/src/pkg/testing/testing.go:383 + 0x180
-----堆栈段边界-----
运行时.panic(0x126660,0x10533140)
/tmp/sandbox/go/src/pkg/runtime/panic.c:248 + 0x160
reflect.flag.must Be(0x0,0x13)
/tmp/sandbox/go/src/pkg/reflect/value.go:249 + 0xc0
reflect.Value.Call(0x0,0x0,0x0,0xfeef9f28,0x0, ...)
/tmp/sandbox/go/src/pkg/reflect/value.go:351 + 0x40
main.TestFunc(0x10546120,0xe)
/ tmpfs / gosandbox-3642d986_9569fcc1_f443bbfb_73e4528d_c874f1af /prog.go:34 + 0x240
让操场从恐慌中恢复,但您的测试程序可能不会。
这就是为什么我添加到上面的测试函数中:
for _,fname:= range funcNames {
推迟func(){
如果x:= recover(); x!= nil {
t.Error(TestFunc paniced for,fname,:,x)
}
}()
fmt.Println(fname)
Func1
Func2
Func3
Func4
--- FAIL:TestFunc(0.00秒)
prog.go:48:Func2应该返回正值
prog。去:51:Func3不应该错误
prog.go:32:TestFunc为Func4 paniced:反映:调用reflect.Value.Call零价值
失败
func TestFunc1(t *testing.T) {
// tests on return values
}
func TestFunc2(t *testing.T) {
// tests identical for Func1
}
func TestFunc3(t *testing.T) {
// tests identical for Func1
}
...
(See this go playground example for a more complete context)
(yes, go playground doesn't support yet go test
, only go run
, and issue 6511 is there to request that feature)
How would you use reflection (reflect
package) in order to write only one test which would:
- call each function in turn?
- test their return value?
I have seen:
- "How to properly use
.Call
in reflect package, Golang?", using Value.Call - "Selecting a function from a list of functions in Golang"
But I miss a complete example for calling functions and using the returned values in a test.
解决方案 Once I understood that everything must use or return the type Value, here is what I came up with.
The trick is to use:
ValueOf
in order to get a value of the receiverValue.MethodByName
to find a function of that receiver valueValue.IsNil
to test for nil
returned value.
Main extract of the test code:
var funcNames = []string{"Func1", "Func2", "Func3"}
func TestFunc(t *testing.T) {
stype := reflect.ValueOf(s)
for _, fname := range funcNames {
fmt.Println(fname)
sfunc := stype.MethodByName(fname)
// no parameter => empty slice of Value
ret := sfunc.Call([]reflect.Value{})
val := ret[0].Int()
// That would panic for a nil returned err
// err := ret[1].Interface().(error)
err := ret[1]
if val < 1 {
t.Error(fname + " should return positive value")
}
if err.IsNil() == false {
t.Error(fname + " shouldn't err")
}
}
}
See a runnable example in go playground.
Note that if you are calling that test function with a non-existent function name, that will panic.
See that example here.
runtime.panic(0x126660, 0x10533140)
/tmp/sandbox/go/src/pkg/runtime/panic.c:266 +0xe0
testing.func·005()
/tmp/sandbox/go/src/pkg/testing/testing.go:383 +0x180
----- stack segment boundary -----
runtime.panic(0x126660, 0x10533140)
/tmp/sandbox/go/src/pkg/runtime/panic.c:248 +0x160
reflect.flag.mustBe(0x0, 0x13)
/tmp/sandbox/go/src/pkg/reflect/value.go:249 +0xc0
reflect.Value.Call(0x0, 0x0, 0x0, 0xfeef9f28, 0x0, ...)
/tmp/sandbox/go/src/pkg/reflect/value.go:351 +0x40
main.TestFunc(0x10546120, 0xe)
/tmpfs/gosandbox-3642d986_9569fcc1_f443bbfb_73e4528d_c874f1af/prog.go:34 +0x240
Go playground recover from that panic, but your test program might not.
That is why I added to the test function above:
for _, fname := range funcNames {
defer func() {
if x := recover(); x != nil {
t.Error("TestFunc paniced for", fname, ": ", x)
}
}()
fmt.Println(fname)
That produces (see example) a much nicer output:
Func1
Func2
Func3
Func4
--- FAIL: TestFunc (0.00 seconds)
prog.go:48: Func2 should return positive value
prog.go:51: Func3 shouldn't err
prog.go:32: TestFunc paniced for Func4 : reflect: call of reflect.Value.Call on zero Value
FAIL
这篇关于如何在Go中通过反射来测试一系列函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!