【问题标题】:Mockito verify that a specific lambda has been passed as an argument in mock's methodMockito 验证特定的 lambda 已作为参数传递给 mock 的方法
【发布时间】:2017-05-18 16:34:24
【问题描述】:

我想测试以下方法:

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

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

}

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

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

意思,我能不能写这样一个测试:

        @Test
public void testDispatchMessage_Success() throws Exception {

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

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

}

}

此测试将导致断言错误: 论据不同!通缉:

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

实际调用有不同的参数:

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

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

那么有没有其他方法可以验证 dispatchMessage() 方法是否被调用了一个返回 void 的 lambda 并且有一个 body 方法 dispatcher.dispatch("activityId", "ctxId", 1l,); ?

【问题讨论】:

    标签: java lambda mockito


    【解决方案1】:

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

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

    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 接口的方法registerMessage 采用Runnable 作为它的参数。 我还包含了Dispatcher 的实现,它是从registerMessage 中调用的。 对此的测试如下所示:

    @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 方法是否已使用所有适当的参数调用。

    【讨论】:

    • 这正是我的想法。感谢您的宝贵时间。
    • 同样的情况,但它表示参数不同,尽管它需要一个 Runnable 并且测试的代码通过了一个 Lambda。
    • 有同样的场景,并且有很多基于函数的代码,这对解决我的模拟测试很有帮助
    • 为 lambda 调用测试找到的最佳答案之一!非常感谢。
    • 在我的情况下,Runnable 不起作用(测试文件是 kotlin),而是我需要 Function1 作为捕获的类型,因为我的函数接受了一个参数并返回了一个。
    【解决方案2】:

    在特定的 lambda 之外,您可以验证您的方法是否使用 任何 lambda 表达式被调用,如下所示:

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

    【讨论】:

    • 那只会检查它是否被调用,不会捕获任何映射错误。很可能您不想发送 null、null、null 作为参数。 any() 不会明白这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-08
    • 1970-01-01
    相关资源
    最近更新 更多