Mockito verify that a specific lambda has been passed as an argument in mock's method

旧街凉风 提交于 2020-01-10 20:23:31

问题


I want to test the following method:

    public void dispatchMessage(MessageHandler handler, String argument1, String argument2, Long argument3) {

    handler.registerMessage(() -> {
        dispatcher.dispatch(argument1,
                argument2,
                argument3);
    });

}

Where MessageHandler is a helper class which will accept a Functional Interface implementation in the form a lambda, and store it for later execution.

Is there a way to verify with mockito that the dispatchMessage method of the mocked MessageHandler has been called with the specific lambda expression:

Meaning, can I write such a test:

        @Test
public void testDispatchMessage_Success() throws Exception {

    myMessageDispatcher.dispatchMessage(handler, "activityId", "ctxId", 1l, );

    verify(handler, times(1)).dispatchMessage(() -> {
        dispatcher
            .dispatch("activityId", "ctxId", 1l,);
    });

}

}

This test will result in assertion error: Argument(s) are different! Wanted:

......Tests$$Lambda$28/379645464@48f278eb

Actual invocation has different arguments:

..........Lambda$27/482052083@2f217633

which makes sense since mockito tries to compare two different implementations of the functional interface, which have a different hash code.

So is there some other way to verify that the method dispatchMessage() has been called with a lambda that returns void and has a body method of dispatcher.dispatch("activityId", "ctxId", 1l,); ?


回答1:


Yes, you can. The trick here is that you have to get to the instance of the lambda that is passed to the registerMessage and then execute that expression and then you can verify the result.

For the purpose of a meaningful example I created this Handler class that contains the dispatchMessage that you want to test:

public class Handler {

    private Dispatcher dispatcher = new Dispatcher();

    public void dispatchMessage(MessageHandler handler, String argument1, String argument2, Long argument3) {

        handler.registerMessage(() -> {
            dispatcher.dispatch(argument1,
                    argument2,
                    argument3);
        });

    }

    interface MessageHandler {
        void registerMessage(Runnable run);
    }

    static class Dispatcher {
        void dispatch(String a, String b, long c){
            // Do dispatch
        }
    }
}

What you have to remember is that a lambda expression is just a short hand form to pass a function to a method. In this example the function is the run method of a Runnable. Therefore the method registerMessage of the interface for MessageHandler takes a Runnable as it's argument. I also included an implementation for the Dispatcher, which is called from within registerMessage. The test for this looks like this:

@RunWith(MockitoJUnitRunner.class)
public class HandlerTest {
    @Mock
    private Dispatcher dispatcher;
    @InjectMocks
    private Handler classUnderTest;
    @Captor
    private ArgumentCaptor<Runnable> registerMessageLambdaCaptor;

    @Test
    public void shouldCallDispatchMethod() {
        final String a = "foo";
        final String b = "bar";
        final long c = 42L;

        MessageHandler handler = mock(MessageHandler.class);

        classUnderTest.dispatchMessage(handler, a, b, c);

        verify(handler).registerMessage(registerMessageLambdaCaptor.capture());

        Runnable lambda = registerMessageLambdaCaptor.getValue();

        lambda.run();

        verify(dispatcher).dispatch(a, b, c);
    }
}

There is an ArgumentCaptor for the lambda expression which we use in the first verification of the registerMessage. After that verification we can retrieve the lambda expression from the captor. The type of the lambda expression is Runnable, as defined in the MessageHandler interface. Hence we can call the run method on it and then verify that the dispatch method on the Dispatcher was called with all the appropriate arguments.




回答2:


Outside of the specific lambda, you can verify that your method was called with any lambda expression was called like this:

    verify(handler).registerMessage(any())

    private fun <T> any(): T {
        Mockito.any<T>()
        return null as T
    }


来源:https://stackoverflow.com/questions/41462913/mockito-verify-that-a-specific-lambda-has-been-passed-as-an-argument-in-mocks-m

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!