douzhenchun6782 2013-12-29 09:40
浏览 57
已采纳

如何在Go中通过反射测试功能集合?

I have to write unit tests for several functions with similar signature and return values (an object and an error), which must pass similar test conditions.
I would like to avoid writing:

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:

But I miss a complete example for calling functions and using the returned values in a test.

  • 写回答

1条回答 默认 最新

  • drag2458 2013-12-29 09:40
    关注

    Once I understood that everything must use or return the type Value, here is what I came up with.
    The trick is to use:

    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
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 笔记本上移动热点开关状态查询
  • ¥70 类鸟群Boids——仿真鸟群避障的相关问题
  • ¥15 CFEDEM自带算例错误,如何解决?
  • ¥15 有没有会使用flac3d软件的家人
  • ¥20 360摄像头无法解绑使用,请教解绑当前账号绑定问题,
  • ¥15 docker实践项目
  • ¥15 利用pthon计算薄膜结构的光导纳
  • ¥15 海康hlss视频流怎么播放
  • ¥15 Paddleocr:out of memory error on GPU
  • ¥30 51单片机C语言数码管驱动单片机为AT89C52