I'm trying to mock a collaborator, named Worker
and capture arguments of its method execute
which runs in different thread.
However, the method itself has method references as arguments: childService::listClients
and childService::refreshObjects
.
When I assert method references with captured arguments, I get different lambda objects.
Is there a way to reach and assert them in proper way?
The class being tested:
public class ParentService {
private ChildService childService;
private Worker worker;
...
public void doAction() {
worker.execute(
childService::listClients,
childService::refreshObjects
);
}
}
The test:
@Test
public void shouldUseChildService() {
ArgumentCaptor<Callable> callableCaptor = ArgumentCaptor.forClass(Callable.class);
ArgumentCaptor<Consumer> consumerCaptor = ArgumentCaptor.forClass(Consumer.class);
parentService.doAction();
verify(worker).execute(callableCaptor.capture(), consumerCaptor.capture());
assertEquals((Callable) childService::listClients, callableCaptor.getValue());
assertEquals((Consumer) childService::refreshObjects, consumerCaptor.getValue());
}
Assertion error:
java.lang.AssertionError:
Expected :org.app.services.ParentServiceTest$$Lambda$4/990416209@1786dec2
Actual :org.app.services.ParentServiceTest$$Lambda$1/1340328248@74650e52
at org.junit.Assert.failNotEquals(Assert.java:743)
at org.junit.Assert.assertEquals(Assert.java:118)
at org.junit.Assert.assertEquals(Assert.java:144)
at org.app.services.ParentServiceTest.shouldUseChildService(ParenServiceTest.java:66)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Mockito ArgumentCaptor is used to capture arguments for mocked methods. ArgumentCaptor is used with Mockito verify() methods to get the arguments passed when any method is called.
ArgumentCaptor allows us to capture an argument passed to a method to inspect it. This is especially useful when we can't access the argument outside of the method we'd like to test.
Mockito verify() method can be used to test number of method invocations too. We can test exact number of times, at least once, at least, at most number of invocation times for a mocked method. We can use verifyNoMoreInteractions() after all the verify() method calls to make sure everything is verified.
The first way to create the argument captor is to use annotation @Captor declared on field. If you are using JUnit 4, you can initialize it with Mockito JUnit Runner. @RunWith(MockitoJUnitRunner. class) public class MockitoArgumentCaptorTest { @Captor private ArgumentCaptor<String> stringCaptor; ... }
First Mock your Worker
with Mockito (just as you did). Also mock your ChildService
. Then:
@Test
public void shouldUseChildService() {
ArgumentCaptor<Callable> callableCaptor = ArgumentCaptor.forClass(Callable.class);
ArgumentCaptor<Consumer> consumerCaptor = ArgumentCaptor.forClass(Consumer.class);
parentService.doAction();
verify(worker).execute(callableCaptor.capture(), consumerCaptor.capture());
callableCaptor.getValue().call(); //this will execute whatever was captured
consumerCaptor.getValue().accept(null);//this will execute whatever was captured
// now verify that childService::listClients and childService::refreshObjects have been called
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With