Reputation: 934
My question, which was similar to this:
But different enough that I still had take a while to figure it out was:
How do I verify that a method called by a mock that was used inside a lambda that was passed to a method of another mock object?
This may seem convoluted, but it happens a lot with Java 8 libraries like JDBI, for example, you have a JDBI object:
JDBI MyDBConnection
That you should mock. And then that is used with the withHandle method to pass a lambda implementing the HandleCallback<R,X> type:
//code I'm testing. I implement the lambda, and want to verify it
//calls the correct method in dao provided by JDBI.
MyDBConnection.withHandle(
(handle) -> { ... handle.attach(SomeDao.class).findSomethingInDB(args) .. }
Which is the recommended way to do this.
So I want to verify that findSomethingInDB(eq(args)) is called.
Like I said this was similar, but different enough, that, I at least, will find this answer valuable at some future point, when I forget how to do this. So the original 3rd party library method that invokes my lambda is processed similar to the answer given in the question referenced above, but with some tweaks:
when(JDBIMock.withHandle(any())).then(
//Answer<Void> lambda
invocationOnMock -> {
Object[] args = invocationOnMock.getArguments();
assertEquals(1, args.length);
//the interface def for the callback passed to JDBI
HandleCallback lambda = (HandleCallback) args[0];
when(mockHandle.attach(SomeDao.class)).thenReturn(mockDao);
//this actually invokes my lambda, which implements the JDBI interface, with a mock argument
lambda.withHandle(mockHandle);
//bingo!
verify(mockDao).findSomethingInDB(eq(args));
}
)
Upvotes: 1
Views: 1827
Reputation: 1
This was a very helpful starting point for me trying to mock a useTransaction
call on a JDBI instance, which is more like useHandle
than withHandle
in that it has void
return type and is incompatible with the OP's example.
For completeness in case others come looking:
Jdbi mockDbi = mock(Jdbi.class);
Handle mockHandle = mock(Handle.class);
MyDAO mockDao = mock(MyDAO.class);
when(mockHandle.attach(MyDAO.class)).thenReturn(mockDao));
// void return requires doAnswer().when() pattern instead of when().then()
doAnswer(invocation -> {
// HandleConsumer replaces HandleCallback
HandleConsumer<?> callback = invocation.getArgument(0);
callback.useHandle(mockHandle);
// any further logic and assertions for your use case
return null; // Answer must return something even if the lambda doesn't
}).when(mockDbi).useTransaction(any());
Upvotes: 0
Reputation: 2564
I am trying to do something very similar with verifying the arguments passed to another mock from withHandle
on a mock JDBI call in a test.
The answer you give in the question pointed me in the right direction but gives me the error message:
The method then(Answer<?>) in the type OngoingStubbing<Object> is not applicable for the arguments ((<no type> invocationOnMock) -> {})
Instead I had to use a new org.mockito.stubbing.Answer
passed to the then
, similar to in the other question you linked to.
In your example this would be something like:
when(JDBIMock.withHandle(any())).then(
//Answer<Void> lambda
new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
assertEquals(1, args.length);
//the interface def for the callback passed to JDBI
HandleCallback lambda = (HandleCallback) args[0];
when(mockHandle.attach(SomeDao.class)).thenReturn(mockDao);
//this actually invokes my lambda, which implements the JDBI interface, with a mock argument
lambda.withHandle(mockHandle);
//bingo!
verify(mockDao).findSomethingInDB(eq(args));
return null; // to match the Void type
}
}
)
In my case I was expecting a result list from withHandle
so I had to change the Answer
type, and return type of answer
to match and return a dummy list instead of Void
. (The actual results returned didn't matter in this test, only that the expected arguments were passed to my subsequent mock object).
I also moved the verify
call outside of the Answer
into the main body of my test so it was clearer this was the expectation of the test, not part of the mocking setup.
Upvotes: 0