Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capturing method references with mockito

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)
like image 722
Admx Avatar asked Jan 15 '16 08:01

Admx


People also ask

What is capture in Mockito?

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.

What is ArgumentCaptor used for?

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.

What is the use of Mockito verify?

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.

How do I initialize an ArgumentCaptor?

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; ... }


1 Answers

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
}
like image 75
CoronA Avatar answered Nov 14 '22 23:11

CoronA