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

测试接受在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?

我正在尝试测试以下功能: p>

   // SendRequestAsync异步发送请求,接受回调
 // func,由它调用
 // 
 //参数:
 //-`context`:某些上下文
 //-`token`:某些令牌 
 //-`apiURL`:命中的URL 
 //-`callType`:发出请求的类型。 这应该是
 // HTTP动词之一(“ GET”,“ POST”,“ PUT”,“ DELETE”,...)
 //-`callBack`: 完成时要调用的函数
 //-`callBackCustomData`:用
 // 
 //调用`callBack`的数据由于这是异步请求,因此不会返回任何内容。
func(a *  APICoreSt)SendRequestAsync(上下文接口{},令牌字符串,apiURL字符串,callType APIType,标头映射[string]字符串,jsonBody [] byte,
 callBack OnCompletion,callBackCustomData接口{}){
 go func(数据接口{}  ){
 callBack(a.SendRequest(上下文,令牌,apiURL,callType,标头,jsonBody),数据)
}(callBackCustomData)
} 
  code>  pre> 
 
 

其中 OnCompletion code>的定义如下: p>

  type OnCompletion func(结果CallResultSt,数据接口{})
  code>  pre>  
 
 

我的大脑立即想到创建一个间谍回调。 为此,我分叉了此框架,提出了以下建议: p >

  //在测试函数之外
type MySpy struct {
 * spies.Spy 
} 
 
func(我的* MySpy)回调(res CallResultSt,数据接口{})  {
 my.Called(res,data)
 fmt.Println(“ H​​ello world”)
 return 
} 
 
 //在测试函数中
spy:= new(MySpy)
 
  // ...生成器提出了一些表驱动测试逻辑,其中包含我的数据
 
spy.MatchMethod(“ Callback”,spies.AnyArgs)
assert.NotEmpty(t,spies.CallsTo(“ Callback”)  )
  code>  pre> 
 
 

,它以 p>

  panic向我打招呼:运行时错误:无效的内存地址或nil指针取消引用[ 已恢复] 
恐慌:运行时错误:无效的内存地址或nil指针取消引用
  code>  pre> 
 
 

我该如何解决这个问题并测试此方法? p> DIV>

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
}