Reputation: 3866
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?
Upvotes: 0
Views: 2444
Reputation: 4515
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
}
Upvotes: 2