I have a function in Go that takes an input argument, modifies it, and finally returns it. To be concrete, this function persists some data in DB using the Bun ORM and returns the persisted entity. That said, I have a unit test where I mock this function, and would need the mocked function to return the variable it receives as an argument. Let's better explain it with a fairly simple example:
package doer
type Something struct {
Message string
}
type Doer interface {
DoSomething(s *Something) *Something
}
func TestDoer(t *testing.T) {
var response *doer.Something
request := &doer.Something{
Message: "foo",
}
sut := mocks.Doer{}
sut.On("DoSomething", mock.Anything).Run(func(args mock.Arguments) {
response = args.Get(0).(*doer.Something)
}).Return(response)
r := sut.DoSomething(request)
assert.Equal(t, request.Message, r.Message)
}
The issue I'm facing here is that the assertion triggers a panic because r
is nil
. After thinking about it, I think this happens because Return
is executed before the function passed to Run
and at this point response
is nil
, so it always returns nil
. That said, I would like to know if there is any way of achieving what I want.
Please take into account that I need to honor the interface, I already know that I could just rely on the pointer passed to the function as an input argument and get rid of the return value, but I need to stick to this contract.
I'm using testify
as the mocking framework
The solution has already been posted as a comment by @mkopriva. Adding that as an answer along with explanation.
The issue is that when you declare:
sut.On("DoSomething", mock.Anything).Run(func(args mock.Arguments) {
response = args.Get(0).(*doer.Something)
}).Return(response)
response is nil.
It is actually being initialized inside the Run() block when the function DoSomething
is being invoked. But at that point, it doesn't matter. The value of response
that was nil at the time of declaration, is copied.
Changing it to *response = *(args.Get(0).(*doer.Something))
ensures that you're actually modifying the memory location that is being pointed to by the response
pointer.
To explain in simpler words: say response is storing an address : 0x7FFF5FBFFD98
. Now , when you do suite.On(...)
you're instructing it to return response
i.e. 0x7FFF5FBFFD98
. And inside, Run() you're modifying the memory being pointed to by 0x7FFF5FBFFD98
.