mockito checking MethodInvocation - java

I would like to check that method was invoked with particular parameter using mockito.
I my code, everytime when unit.permission().someCommand() is invoked, the intercetor's method invoke will be invoked along with that. invoke method looks like this:
void invoke(MethodInvocation methodInvocation) ..
This is a test, where I check that interceptor's method was invoked.
#Test
public void permission() throws Throwable {
unit.permission().someCommand();
verify(mockedMethodInterceptor, times(1)).invoke(any(MethodInvocation.class));
}
In this test, as you can see, I use any instance of MethodInvocation.
Question:
Is there any way to check to particular MethodInvocation instance to be sure that invoke() method was invoked with right parameter? This invoke method should hold information about someCommand() method..

Yes.
For this simple case, it's probably ok just to use the value itself in the test:
#Test
public void permission() throws Throwable {
MethodInvocation expectedInvocation = makeItHoweverYouDo();
unit.permission().someCommand();
verify(mockedMethodInterceptor, times(1)).invoke(expectedInvocation);
}
Getting that to be the one that's actually called by your code may require that you inject that value somewhere in your production code. You might need changes to even make that possible.
If you have any use of Matchers in your verify call, you have to use them everywhere. For that purpose, there's a matcher (eq) for matching a value:
verify(mockedMethodInterceptor, times(1)).invoke(eq(expectedInvocation));
You don't need it here, but if your method had another argument where you wanted to use an any matcher, you'd need it.

Related

Mockito mock calls actual implementation when not told to

I have a class which contains the following three methods:
void add(Service... objs)
void add(Collection<Service> objs)
void add(Stream<Service> objs)
As you might expect, these all support the adding of zero or more objects, which can be specified individually or as part of an array, a collection or a stream. The first two variants create a stream from their arguments, and pass them to the third variant which actually does the adding.
In testing an object that uses this class, I have created a Mockito mock object to represent an instance of this class, using Spring's #MockBean annotation. I can see in the debugger that the object under test contains the mock object, and that the call I am expecting (with a single argument of type Service) is being addressed to the mock. Because the method that should be called is the first variant (the varargs one), and I know that varargs parameters are a little tricky, I coded the test to check that the mock is called with the correct parameter as follows:
ArgumentCaptor<Service> captor = ArgumentCaptor.forClass(Service.class);
verify(theMock).add(captor.capture());
assertThat(captor.getAllValues()).containsExactly(expectedService);
However, when I run this code the assertion fails because the List returned by captor.getAllValues() contains not a Service, but a Stream: the failure message says:
java.lang.AssertionError:
Expecting:
<[java.util.stream.ReferencePipeline$Head#2cfe272f]>
to contain exactly (and in same order):
<[com.xxx.data.Service#37c5]>
but some elements were not found:
<[com.xxx.data.Service#37c5]>
and others were not expected:
<[java.util.stream.ReferencePipeline$Head#2cfe272f]>
When I run the code in the debugger, I can see that the call from the object under test to add(Service...) invokes the real implementation; this invokes add(Stream<Service>) and it is that call which is intercepted by the mock. That explains why I am seeing the failure, but I don't understand why the mock is failing to intercept the original call, or what I can do to make it do so.
Update your ArgumentCaptor to accept Service[]
ArgumentCaptor<Service[]> serviceCaptor = ArgumentCaptor.forClass(Service[].class);
And assert
Service[] actualServices = serviceCaptor.getAllValues();
assertEquals(actualServices.length, 1);
assertEquals(actualServices[0], service);
And it's best practice to use ErrorCollector in Junit to assert more than one
assert and SoftAssect in Testng and call after your assertion softAssert.assertAll()
I have come up with a workaround to the problem, but the actual problem still exists and I think it is probably a Mockito bug (raised as https://github.com/mockito/mockito/issues/1929).
The workaround is to add this method to my test class. I've added a generic method because it is not just a call to the add() method that has the problem, but also a call to a similar overloaded remove() method that takes String arguments.
private <T, V> void verifyCall(T mock, BiConsumer<T, V> call,
V expectedArg, Class<V> type)
{
ArgumentCaptor<V> captor = ArgumentCaptor.forClass(type);
call.accept(verify(mock), captor.capture());
List<?> values = captor.getAllValues();
try {
assertThat(values.get(0)).isEqualTo(expectedArg);
} catch (AssertionFailedError ex) {
assertThat((Stream<V>) values.get(0)).containsExactly(expectedArg);
}
}
This should work whether the call intercepted by the mock was to the varargs variant of the method (as it should be) - in which case the assertion in the body of the try block will not throw an exception - or to the Stream variant (as it currently is) - in which case the assertion in the body will throw an exception and the assertion in the catch block will be executed.
Then, when I want to verify that my mock's add() method was called with the expected Service object, I do so with:
verifyCall(theMock, Datastore::add, expectedService, Service.class);
And similarly, for the remove() method:
verifyCall(theMock, Datastore::remove, expectedDeletedKey, String.class);
Very pleasingly, when I finally got this working the test failed because I had made a mistake in the method under test. Which made it all worthwhile.
D'oh. I failed to spot that the two varargs methods were declared as final. Removed this and it all works as expected.

How do i verify the call to mocked instance when the instance takes "this" as parameter

I have a method which has call to event publisher, i want to verify the call to this publisher.
public void submitRegistrationForm(String registrationKey, RegistrationFormSubmitDTO registrationFormSubmitDTO) {
applicationEventPublisher.publishEvent(new RegistrationCompletedEvent(this, targetCustomerRegistration));
}
So, i mocked the applicationEventPublisher class and i had verified the call to it like this:
verify(applicationEventPublisher, times(1)).publishEvent(any(RegistrationCompletedEvent.class));
but i was told not to use any(), instead i need to build the concrete instance.
how can i verify the method which takes "this" as parameter.
You can use an argument captor to capture the RegistrationCompletedEvent and assert on it...
ArgumentCaptor<RegistrationCompletedEvent > argument = ArgumentCaptor.forClass(RegistrationCompletedEvent .class);
verify(applicationEventPublisher).publishEvent(argument.capture());
assertEquals("whatever", argument.getValue().something());

Mocking the arguments passed to callbacks (lambdas)

How would I mock methods that accept a lambda using Mockito so that I am able to control which arguments are passed into the callback? I am specifically trying to mock the JDBI method useExtension which is used like this:
jdbi.useExtension(OrgUnitDao.class, dao -> {
// Skip if already loaded
// Skip if already loaded
if (dao.orgUnitsAreLoaded()) {
I would like to substitute the dao object passed back into the callback so that I could control the branching using the return value of dao.orgUnitsAreLoaded().
The signature looks like this
public <E,X extends Exception> void useExtension(Class<E> extensionType,
ExtensionConsumer<E,X> callback)
throws NoSuchExtensionException,
X extends Exception
This is the full answer to my question. It's simplified down to the very basics of how to do the stubbing and so doesn't reflect the production code I am to test, but it shows exactly the mechanics needed to do it.
final Jdbi jdbi = mock(Jdbi.class);
doAnswer(invocation -> {
System.out.println("this is the doAnswer lambda - just setting up the answer and the mocks");
final Class<OrgUnitDao> daoClass = invocation.getArgument(0);
final ExtensionConsumer callback = invocation.getArgument(1);
final OrgUnitDao mock1 = mock(daoClass);
when(mock1.orgUnitsAreLoaded()).thenReturn(false);
// call the actual callback method
callback.useExtension(mock1);
return null;
}).when(jdbi).useExtension(eq(OrgUnitDao.class), any());
// This is the method call I am to test
// Regard this as hidden away in some outer method in
// the System-Under-Test, but that I have been able
// to inject all its dependencies
jdbi.useExtension(OrgUnitDao.class, new Foo());
/// Further down, outside of the method
// Only replaced the lambda with this to get toString() for debugging ...
class Foo implements ExtensionConsumer<OrgUnitDao, RuntimeException> {
#Override
public void useExtension(OrgUnitDao orgUnitDao) throws RuntimeException {
System.out.println("A real method call, now using the passed in mocked dao:" + orgUnitDao.orgUnitsAreLoaded());
}
#Override
public String toString() {
return "OrgUnitDao class";
}
}
To parallel the conversation on the question "Calling callbacks with Mockito", your lambda might be called synchronously during the execution of your method-under-test, or it might be called later based on some external factor or interaction. Like Dawood's answer there, your answer here using a Mockito Answer will work, and is the only way to go if you are looking for the synchronous style (where mockJdbi calls your lambda immediately before methodUnderTest returns). If your lambdas are going to be called asynchronously, or if your system tolerates you calling the lambda asynchronously, you might want to test the state after your method-under-test returns but before you interact with the lambda.
// MockitoJUnitRunner, MockitoRule, or MockitoAnnotations.initMocks populate these.
// Especially useful for the ArgumentCaptor's generic arguments.
#Mock Jdbi mockJdbi;
#Mock OrgUnitDao mockOrgUnitDao;
#Captor ArgumentCaptor<ExtensionConsumer<OrgUnitDao, RuntimeException>>
extensionConsumerCaptor;
#Test public void yourTest() throws Exception {
// No stubbing needed! Just create the system under test.
YourSystemUnderTest systemUnderTest = new YourSystemUnderTest(mockJdbi);
// Call the method under test, which presumably calls useExtension(...).
systemUnderTest.methodUnderTest();
// Assert anything that should be true before the lambda is called.
assertFalse(systemUnderTest.getSomeState());
// Confirm that useExtension was called, and simultaneously acquire the lambda.
// ArgumentCaptor.capture() is a matcher, so every argument requires a matcher like eq.
verify(mockJdbi).useExtension(eq(OrgUnitDao.class), extensionConsumerCaptor.capture());
// Prepare the mock DAO and call the lambda.
when(mockDao.getFoo()).thenReturn("bar");
extensionConsumerCaptor.getValue().useExtension(mockDao);
// Assert anything that should be true after the lambda is called.
assertTrue(systemUnderTest.getSomeState());
}
Though lambdas reduce the boilerplate previously associated with anonymous inner classes, you may also prefer using the Captor style because it saves you from creating lengthy Answer implementations and hiding your test assertions or Mockito verifications in them. This is especially tempting if your project prefers BDD-style mocks with clear "given-when-then" structure (though my example more-closely resembles "given-when-then-when-then").

mock methods in same class

I am using Mockito to mock a method in the same class for which I am writing test. I have seen other answers on SO (Mocking method in the same class), but probably I am misunderstanding them, since I running into issues.
class Temp() {
public boolean methodA(String param) {
try {
if(methodB(param))
return true;
return false;
} catch (Exception e) {
e.printStackTrace();
}
}
}
My Test method:
#Test
public void testMethodA() {
Temp temp = new Temp();
Temp spyTemp = Mockito.spy(temp);
Mockito.doReturn(true).when(spyTemp).methodB(Mockito.any());
boolean status = temp.methodA("XYZ");
Assert.assertEquals(true, status);
}
I however get the expection printed out because definition of methodB gets executed.
My understanding is definition of methodB would get mocked by using spyTemp. However that does not appear to be the case.
Can someone please explain where I am going wrong?
The first issue is that you have to use spyTemp object to expect something from Mockito. Here it is not the same as test. spyTemp is wrapped by the Mockito object temp.
Another issue is that you stub only methodB(), but you are trying to run methodA(). Yes in your implementation of methodA() you call methodB(), but you call this.methodB(), not spyTemp.methodB().
Here you have to understand that mocking would work only when you call it on the instance of temp. It's wrapped by a Mockito proxy which catches your call, and if you have overriden some method, it will call your new implementation instead of the original one. But since the original method is called, inside it you know nothing about Mockito proxy. So your "overriden" method would be called only when you run spyTemp.methodB()
This should work:
Mockito.doReturn(true).when(spyTemp).methodB(Mockito.any());
boolean status = spyTemp.methodA("XYZ");
You created a spy and mocked methodB(). That is correct! But you called methodA() on the original object. To get the correct result call it on the spy
boolean status = spyTemp.methodA("XYZ");
Note the following from Mockito documentation:
Mockito does not delegate calls to the passed real instance, instead
it actually creates a copy of it. So if you keep the real instance and
interact with it, don't expect the spied to be aware of those
interaction and their effect on real instance state. The corollary is
that when an unstubbed method is called on the spy but not on the
real instance, you won't see any effects on the real instance.
This is referring specifically to your situation. You keep a reference to temp and then call its methodA. Mockito is not spying on that instance at all; it's spying on spyTemp. So the normal methodB is called.
Note that you should avoid partial mocks altogether for new code.

Use the method parameters of expect in the andReturn

Collection<T_SI_IDABAREME> tSiIdabaremes;
DAO_F_IDA_DESC mockDaoFIdaDesc
prepareExpects(){
expect(mockDaoTSiIdabareme.findByDate(isA(Date.class)))
.andReturn(searchByParameter(tSiIdabaremes, date));
}
Is it possible to use the date that will be passed to the findByDate in the andReturn?
PS: This is a service test class and I'm doing it in a way to bypass the database.
whenever you use expect method like this
Easymock.expect(someMethod(Date.Class)).andReturn(something);
you are instructing compiler to mock all calls to that method whenever ANY object of Date class is passed as parameter,and you will not be able to use that object in return expression.
on the other hand if you have something like this,
Easymock.expect(someMethod(someSpecificDateObject)).andReturn(someSpecificDateObject);
you are instructing the compiler to mock this method call ONLY when a specific object of Date class is passed as parameter(someSpecificDateObject in this case) and you will be able to use this parameter while returning, because you know that method gets mocked only when this object is passed.
You can use second option if it is favourable to you,but with first option what you ask is not possible.
Hope this helps!
Good luck!
Instead of:
expect(mockDaoTSiIdabareme.findByDate(isA(Date.class)))
.andReturn(searchByParameter(tSiIdabaremes, date));
I should have put
expect(mockDaoTSiIdabareme.findByDate(isA(Date.class)))
.andAnswer(new IAnswer<Collection<T_SI_IDABAREME>>() {
public Collection<T_SI_IDABAREME> answer() throws Throwable {
return searchByParameter((Date)getCurrentArguments()[0]);
}
}
);
Which will only look for the return value when the method is being exectued and then we can use getCurrentArguments() to retrieve the arguments passed to the method.
You can find more about this in the EasyMock Documentation.

Categories