Mockito:验证是否使用功能参数调用了方法 [英] Mockito: Verifying a method was called with a functional parameter
问题描述
我有一个简单的场景,其中尝试验证调用方法时的某些行为(即,使用给定参数(此场景中的函数指针)调用了某个方法).以下是我的课程:
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屋!