mock
mock copied to clipboard
Add argument captors
Description
I think it would be nice to build an ArgumentCaptor
interface and argumentCaptor
struct that extends Matcher
through composition, but store the value of the arguments before the "match" step. Those argument values could then be retrieved for later assertions (very close to how Mockito's ArgumentCaptors work in Java) https://static.javadoc.io/org.mockito/mockito-core/2.6.9/org/mockito/ArgumentCaptor.html
Use Case
I recently ran into a situation where I wanted to write a test that asserted something about the contents of an argument (a slice of struct pointers) passed to a mocked method. Using matchers as they currently exist (i.e. eqMatcher
), I couldn't find a good way to do that without already knowing the memory addresses of the pointers in the slice.
Have you looked at the gomock.Do()
(docs)? It seems like this would get you pretty close.
Thanks for the reply @poy! Yes, I started writing my tests with gomock.Do()
and it was working ok. However, the methods I was mocking had long complicated signatures, so the anonymous funcs I needed to pass to Do()
were very long. The ArgumentCaptor
s get the same end result with a lot less code in those situations.
I also tried writing a decorator function for passing into Do()
that looked something like this.
func ArgumentCaptor(capturedArgs []interface{}) func(args ...interface{}) {
return func(args ...interface{}) {
if len(capturedArgs) != len(args) {
panic(fmt.Sprintf("The capturedArgs array length %d did not match the actual argument count %d",
len(capturedArgs), len(args)))
}
for i, arg := range args {
capturedArgs[i] = arg
}
}
}
It involves writing less code, but its downside was the need to grab the arguments based on their order in the signature with an index.
@tylersammann
Sorry for the slow response. @balshetzer and I are discussing how we would like to approach adding matchers. I'll let you know what decide!
@poy @balshetzer Any news here?
+1
+1
@poy @balshetzer
I'm struggling to test the following scenario with Do()
:
Say my function foo()
calls two mocked method .F1()
and .F2()
, I want to test the argument passed to .F1()
is the same as the one passed to .F2()
var arg string
mockStruct.EXPECT()
.F1(gomock.Any())
.Do(func(_arg string) { arg = _arg})
mockStruct.EXPECT()
.F2(gomock.Any(), gomock.Eq(arg))
foo()
This obviously won't work. Any good way to test this right now or the new matchers can help with that?
@hgl I think there might be a way to do something similar to this with .Do()
, but it would involve storing the arguments used in both F1
and F2
and comparing them afterwards. FWIW, here is how I think I'd do with with my argument captor implementation from this PR https://github.com/golang/mock/pull/263
f1Captor := gomock.AnyCaptor()
f2Captor := gomock.AnyCaptor()
mockStruct.EXPECT().F1(f1Captor)
mockStruct.EXPECT().F2(f2Captor)
foo()
assert.Equal(t, f1Captor.Value(), f2Captor.Value())