Mockito 验证特定的 lambda 已作为参数传递给 mock 的方法

2022-01-14 00:00:00 lambda mockito java

我想测试以下方法:

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

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

}

MessageHandler 是一个辅助类,它将接受 lambda 形式的功能接口实现,并将其存储起来以供以后执行.

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

有没有办法用 mockito 验证被模拟的 MessageHandlerdispatchMessage 方法已被特定的 lambda 表达式调用:

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

这是有道理的,因为 mockito 试图比较函数接口的两个不同实现,它们具有不同的哈希码.

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

那么还有其他方法可以验证方法 dispatchMessage() 是否已使用返回 void 的 lambda 调用,并且该方法的主体方法为dispatcher.dispatch("activityId", "ctxId", 1l,); ?

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,); ?

推荐答案

是的,你可以.这里的诀窍是,您必须获取传递给 registerMessage 的 lambda 实例,然后执行该表达式,然后您才能验证结果.

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.

为了一个有意义的示例,我创建了这个 Handler 类,其中包含您要测试的 dispatchMessage:

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

您必须记住的是,lambda 表达式只是将函数传递给方法的简写形式.在这个例子中,函数是 Runnablerun 方法.因此,MessageHandler 接口的方法registerMessageRunnable 作为其参数.我还包含了 Dispatcher 的实现,它是从 registerMessage 中调用的.对此的测试如下所示:

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

我们在 registerMessage 的第一次验证中使用的 lambda 表达式有一个 ArgumentCaptor.在验证之后,我们可以从捕获者那里检索 lambda 表达式.lambda 表达式的类型是 Runnable,在 MessageHandler 接口中定义.因此,我们可以对其调用 run 方法,然后验证 Dispatcher 上的 dispatch 方法是否已使用所有适当的参数调用.

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.

相关文章