简体   繁体   中英

How to verify a java.lang.reflect.Method known only from reflection was called on an object

Given a java.lang.reflect.Method object obtained through reflection, how can I test that this Method was called on a given object?

Here is an example code:

MyClass obj = mock(MyClass.class);
injectInTestedObject(obj, testedObj);

String myMethodIWantToCheckName = retrieveMethodNameBySomeMeans();
Method myMethodIWantToCheck =
  Arrays.asList(MyClass.class.getMethods())
    .stream()
    .filter(method -> method.getName().equals(myMethodIWantToCheckName))
    .findFirst()
    .get();

Now I'd like to check if myMethodIWantToCheck was called on obj .

Basically it's the same functionality as verify from Mockito but without knowing the method at compile time.

Is there any way? Any library already implementing this?

No, there's apparently no short and easy way to do so using only the libraries provided by the Java SE. And I couldn't be happier about this!

The naive approach

Let's imagine that what you're trying to achieve was actually possible and a supported operation in the java.lang.reflect -API. What kind of information and mechanisms would be required to provide such a functionality? In case one should be able to retrieve whether a certain method has already been invoked (on a certain object) at least once at any time (with no kind of "start delimiter"), the Java Virtual Machine would be required to keep track of any invocation ever made; effectively forcing it to persist all stack frames ever created, Introducing such a start delimiter may possibly reduce the runtime overhead, but would still require an extremely complex form of stack frame auditing. built into every JVM.

How Mockito is doing it

But hey, Mockito is able to provide such a functionality, right? Indeed, but I think you're underestimating the complexity of the way they're doing that.

First, there's a very important, conceptual difference between your and Mockito's approach. It already becomes apparent when taking a look at the very first example on the Mockito website :

import static org.mockito.Mockito.*;

// mock creation
List mockedList = mock(List.class);

// using mock object - it does not throw any "unexpected interaction" exception
mockedList.add("one");
mockedList.clear();

// selective, explicit, highly readable verification
verify(mockedList).add("one");
verify(mockedList).clear();

Users of the framework always have to mock the types of interest first; either explicitly like here or in a more implicit manner by using annotations. As I'm not very familiar with the Mockito framework, there may be even more elegant ways to do so; but it all boils down to the very same concept.

But what does "mocking a type" even mean and what should it be good for? Actually, Mockito has to somehow intercept any call that is being made to the object returned by the mocking method (or the other mechanisms, respectively). Fortunately (keeping the beginning of this post in mind), Mockito isn't able to do so directly, so the developers had to come up with something else: Creating new types dynamically at runtime that are not only delegating the method calls to the actual implementation, but that also keep track of the methods that are being invoked on that particular type.

Looking at the framework's source code , it seems like that the real magic is happening in the org.mockito.internal.creation.bytebuddy package. In SubclassBytecodeGenerator , you can find Mockito's type creation logic. As the package name already implies, Mockito uses the Byte Buddy framework for the bytecode generation and loading process.

Library support

As you've also asked for a library which you may use to achieve the same behavior: As far as I can tell, there's no really well-known library out there.

To be honest: I'm not surprised that that's the case. I mean, what should one use such a library for? Checking whether a method has been invoked is something one should do in tests; other use cases are quite limited.

So, yeah. Your options are quite limited, I suppose.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM