I have two structs: FunctionalityClient
and TestClient
, both implementing Interface
. I have a global variable Client
of type Interface
. I assign to Client
either the actual client, or the mock client, depending on whether it's a test or a normal run.
Interface
has a method Request
that I want to mock in tests. That is, I want to:
- record what were the argument passed to the function
- return some arbitrarily defined return value from the function
So the struct looks like this:
type TestClient struct {
recordedArgs []interface{}
returnValues []interface{}
}
func (c *TestClient) Request(body io.Reader, method string, endpoint string, headers []Header) ([]byte, error) {
c.recordedArgs = append(c.recordedArgs, []interface{}{body, method, endpoint, headers}) // this can't be typed if I want the code to be reusable
if len(c.returnValues) != 0 {
last := c.returnValues[0]
c.returnValues = c.returnValues[1:]
return last.([]byte), nil
}
return nil, nil
}
And I use it like so:
testClient := TestClient{
returnValues: []interface{}{
[]byte("arbitrarily defined return value"),
[]byte("this will be returned after calling Request a second time"),
}
}
Client = &testClient
// run the test
// now let's check the results
r1 := testClient.recordedArgs[1].([]interface{}) // because I append untyped lists to recordedArgs
assert.Equal(t, "POST", r1[1].(string))
assert.Equal(t, "/file", r1[2].(string))
// and so on
Now the question.
I have a few structs that I want to mock like this. Currently I just copy and paste the code above for each struct. But that really sucks, I would like the mock logic to be abstracted away somehow. I would also accept something like Mockito's when
: when the mocked function is called with specific arguments, return a specific value and record the call.
How can I properly mock a struct with member functions in Golang?