Mockito:验证是否使用功能参数调用了方法 [英] Mockito: Verifying a method was called with a functional parameter

查看:89
本文介绍了Mockito:验证是否使用功能参数调用了方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的场景,其中尝试验证调用方法时的某些行为(即,使用给定参数(此场景中的函数指针)调用了某个方法).以下是我的课程:

I have a simple scenario in which am trying to verify some behavior when a method is called (i.e. that a certain method was called with given parameter, a function pointer in this scenario). Below are my classes:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

        AppBootStrapper bootStrapper = context.getBean(AppBootStrapper.class);
        bootStrapper.start();
    }
}

@Component
public class AppBootStrapper {
    private NetworkScanner networkScanner;
    private PacketConsumer packetConsumer;

    public AppBootStrapper(NetworkScanner networkScanner, PacketConsumer packetConsumer) {
        this.networkScanner = networkScanner;
        this.packetConsumer = packetConsumer;
    }

    public void start() {
        networkScanner.addConsumer(packetConsumer::consumePacket);
        networkScanner.startScan();
    }
}

@Component
public class NetworkScanner {
    private List<Consumer<String>> consumers = new ArrayList<>();

    public void startScan(){
        Executors.newSingleThreadExecutor().submit(() -> {
            while(true) {
                // do some scanning and get/parse packets
                consumers.forEach(consumer -> consumer.accept("Package Data"));
            }
        });
    }

    public void addConsumer(Consumer<String> consumer) {
        this.consumers.add(consumer);
    }
}

@Component
public class PacketConsumer {
    public void consumePacket(String packet) {
        System.out.println("Packet received: " + packet);
    }
}

@RunWith(JUnit4.class)
public class AppBootStrapperTest {

    @Test
    public void start() throws Exception {
        NetworkScanner networkScanner = mock(NetworkScanner.class);
        PacketConsumer packetConsumer = mock(PacketConsumer.class);
        AppBootStrapper appBootStrapper = new AppBootStrapper(networkScanner, packetConsumer);

        appBootStrapper.start();

        verify(networkScanner).addConsumer(packetConsumer::consumePacket);
        verify(networkScanner, times(1)).startScan();
    }
}

我想通过注册数据包使用者来验证bootStrapper是否确实进行了正确的设置(稍后可能会注册其他使用者,但这是强制性的),然后调用startScan.执行测试用例时,出现以下错误消息:

I want to verify that bootStrapper did in fact do proper setup by registering the packet consumer(there might be other consumers registered later on, but this one is mandatory) and then called startScan. I get the following error message when I execute the test case:

Argument(s) are different! Wanted:
networkScanner bean.addConsumer(
    com.spring.starter.AppBootStrapperTest$$Lambda$8/438123546@282308c3
);
-> at com.spring.starter.AppBootStrapperTest.start(AppBootStrapperTest.java:24)
Actual invocation has different arguments:
networkScanner bean.addConsumer(
    com.spring.starter.AppBootStrapper$$Lambda$7/920446957@5dda14d0
);
-> at com.spring.starter.AppBootStrapper.start(AppBootStrapper.java:12)

显然,从异常来看,函数指针是不同的.

From the exception, clearly the function pointers aren't the same.

我采用正确的方法吗?我缺少一些基本的东西吗?我随便玩耍,让一个消费者注入PacketConsumer只是为了看看它是否有所作为,这还可以,但是我知道那当然不是正确的方法.

Am I approaching this the right way? Is there something basic I am missing? I played around and had a consumer injected into PacketConsumer just to see if it made a different and that was OK, but I know that's certainly not the right way to go.

任何帮助,我们将不胜感激.

Any help, perspectives on this would be greatly appreciated.

推荐答案

Java没有函数指针"的任何概念.当您看到:

Java doesn't have any concept of "function pointers"; when you see:

networkScanner.addConsumer(packetConsumer::consumePacket);

Java实际编译的是(等同于):

What Java actually compiles is (the equivalent of):

networkScanner.addConsumer(new Consumer<String>() {
  @Override void accept(String packet) {
    packetConsumer.consumePacket(packet);
  }
});

此匿名内部类恰好被称为AppBootStrapper$$Lambda$7.因为它没有(也不应该)定义equals方法,所以它永远不会等于编译器在您的测试中生成的匿名内部类(恰好称为AppBootStrapperTest$$Lambda$8).这与方法主体是相同的,并且是通过相同的方法引用以相同的方式构建的事实无关.

This anonymous inner class happens to be called AppBootStrapper$$Lambda$7. Because it doesn't (and shouldn't) define an equals method, it will never be equal to the anonymous inner class that the compiler generates in your test, which happens to be called AppBootStrapperTest$$Lambda$8. This is regardless of the fact that the method bodies are the same, and are built in the same way from the same method reference.

如果您在测试中显式生成使用者,并将其另存为static final Consumer<String>字段,则可以在测试中传递该引用并进行比较;在这一点上,引用相等应该成立.这应该可以很好地使用lambda表达式或方法引用.

If you generate the Consumer explicitly in your test and save it as a static final Consumer<String> field, then you can pass that reference in the test and compare it; at that point, reference equality should hold. This should work with a lambda expression or method reference just fine.

更合适的测试可能是verify(packetConsumer, atLeastOnce()).consumePacket(...),因为lambda的内容是实现细节,您实际上更关心的是组件如何与其他组件协作.这里的抽象应该在consumePacket级别上,而不是在addConsumer级别上.

A more apt test would probably verify(packetConsumer, atLeastOnce()).consumePacket(...), as the contents of the lambda are an implementation detail and you're really more concerned about how your component collaborates with other components. The abstraction here should be at the consumePacket level, not at the addConsumer level.

此SO问题上查看评论和答案.

这篇关于Mockito:验证是否使用功能参数调用了方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆