doumin1897 2018-08-16 15:44
浏览 385
已采纳

测试接受在Golang中不返回值的回调函数的方法

I'm trying to test the following function:

// SendRequestAsync sends request asynchronously, accepts callback
//  func, which it invokes
//
// Parameters:
// - `context` : some context
// - `token` : some token
// - `apiURL` : the URL to hit
// - `callType` : the type of request to make. This should be one of
//  the HTTP verbs (`"GET"`, `"POST"`, `"PUT"`, `"DELETE"`, ...)
// - `callBack` : the func to invoke upon completion
// - `callBackCustomData`: the data to invoke `callBack` with
//
// Since this is an async request, it doesn't return anything.
func (a *APICoreSt) SendRequestAsync(context interface{}, token string, apiURL string, callType APIType, header map[string]string, jsonBody []byte,
    callBack OnCompletion, callBackCustomData interface{}) {
    go func(data interface{}) {
        callBack(a.SendRequest(context, token, apiURL, callType, header, jsonBody), data)
    }(callBackCustomData)
}

where OnCompletion is defined by:

type OnCompletion func(result CallResultSt, data interface{})

My mind instantly thinks to create a spy callback. To do so, I forked this framework, came up with the following:

// outside the test function
type MySpy struct {
    *spies.Spy
}

func (my *MySpy) Callback(res CallResultSt, data interface{}) {
    my.Called(res, data)
    fmt.Println("Hello world")
    return
}

//in the test function
spy := new(MySpy)

//...some table-driven test logic the generator came up with, containing my data

spy.MatchMethod("Callback", spies.AnyArgs)
assert.NotEmpty(t, spies.CallsTo("Callback"))

and it greeted me with

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
    panic: runtime error: invalid memory address or nil pointer dereference

How do I remedy this, and test this method?

  • 写回答

1条回答 默认 最新

  • duannuo7878 2018-08-16 21:23
    关注

    I would ditch the spy stuff. This task is simple enough that you shouldn't need an external dependency to handle it. You could instead make your own "spy" that has a channel it passes args into when the function is called. In your test, you then attempt to receive from the channel. That will force the test to wait for the callback function to be called. You may also consider adding a timeout period so that the test can fail instead of blocking forever if the function is never called.

    // outside the test function
    type MySpy struct {
        Args chan MySpyArgs
    }
    
    type MySpyArgs struct {
        Res  CallResultSt
        Data interface{}            
    }
    
    func (my *MySpy) Callback(res CallResultSt, data interface{}) {
        my.Args <- MySpyArgs{Res: res, Data: data}
    }
    
    //in the test function
    spyChan := make(chan MySpyArgs)
    spy := &MySpy{spyChan}
    
    //...some table-driven test logic the generator came up with, containing my data
    
    args := <-spyChan
    // can now assert arguments were as you expected, etc.
    

    A crude working example: https://play.golang.org/p/zUYpjXdkz-4.

    And if you want to use a timeout:

    ...
    select {
    case args := <-spyChan:
        // assertions on args
    case <-time.After(5 * time.Second):
        // prevent blocking for over 5 seconds and probably fail the test
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

    报告相同问题?

    悬赏问题

    • ¥15 Python词频统计,运行出的Excel没有内容
    • ¥15 求推荐一个好用的录屏软件
    • ¥15 kali显示no x11 display variable was set;
    • ¥15 如何实现这个Python开发问题
    • ¥15 Erasure Code纠删码表
    • ¥15 用vite创建的vue3项目,404重定向不起作用??
    • ¥15 关于#c语言#的问题:一个球从80米高度自由落下,每次落地后反弹的高度为原高度的一半计算6次小球反弹的高度.(反弹结果取整,使用走走for循环结构)
    • ¥15 SurfaceControl的screenshot问题
    • ¥15 基于51单片机的oled菜单代码,要C语言,模块化编程!
    • ¥15 JAVAswing,设计一个扑克牌什么的