Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito: is it possible to combine mock with a method name to create a methodCall inside a when() call?

my first question on StackOverflow. I'd like to be able to do something like:

SomeClass mock = mock(SomeClass.class);

String methodName = "someMethod"; OR Method method = ...someMethod...

Both of these things (the mock and the method) would combine to do the following:

when(mock.someMethod()).thenReturn(null);

Of course, the 'null' value will be changed accordingly for my needs, but I am trying to determine two things:

1) Is it even possible to do something like this in Java? This = combining a class object and a method into a methodCall.

2) How do I do such a thing?

I've researched this endlessly and I can't find anything. The problem is that even if this works with a regular class and a regular method (someClass and someMethod would come together to do someClass.someMethod()), keep in mind that this has to work with a mock object for use inside a when() call.

ANSWERED: when(method.invoke(mock)).thenReturn("Hello world."); is the correct syntax and reflection indeed does work inside a when() call. Thanks Kevin Welker!

like image 440
Andrew Avatar asked Apr 12 '12 13:04

Andrew


People also ask

How do you mock a method call inside another method in the same class?

We can mock runInGround(String location) method inside the PersonTest class as shown below. Instead of using mock(class) here we need to use Mockito. spy() to mock the same class we are testing. Then we can mock the method we want as follows.

Can we use @qualifier with @mock?

@MockBean with @QualifierIf an interface has more than one implementation class then to choose the exact class for dependency injection, @Qualifier annotation is used. Here we will show the demo to use @MockBean with @Qualifier annotation. Find the classes used in our unit test class.

Does Mockito when call the method?

A mock does not call the real method, it is just proxy for actual implementations and used to track interactions with it. A spy is a partial mock, that calls the real methods unless the method is explicitly stubbed. Since Mockito does not mock final methods, so stubbing a final method for spying will not help.


1 Answers

Since you basically asked me to repost my comment, modified by your response, as an answer, here it is:

Try using reflection as in:

when(method.invoke(mock)).thenReturn("Hello world.");

although, I'm not sure how this is working for you, since you cannot mock/spy class Method (it is final). Mockito's when() only works on mocks or spies. If this is really working for you, can you post a little more detail?

If it doesn't work, you can -- as I suggested in my comment in the OP -- go the CGLib route and bypass Mockito. It's really not that difficult as it looks at first. In my OSS project Funcito (not a mocking framework), I stripped down a lot of the Mockito CGLib proxying code and rewrote it for my needs. It gives a much simpler view into the world of proxying classes, and intercepting method calls.

ADDITIONAL RESPONSE TO COMMENTS I see how this is working for you, but I am not sure you really understand how it is working. The reason this might matter is because future changes to the way Mockito itself works could render your solution broken in the future. In essence, the reason it works is almost accidental, but yes it will work.

The way that when() is supposed to work is that what happens in between the parentheses is a method call on a previously created Mockito-generated mock or spy, which is just a fancy proxy of a class, rather than a real instance of the class. The proxies have special logic that intercepts the fake proxied method call and basically add that to a list of registered proxy-method invocations (it is stored in something called an IOngoingStubbing or something like that) for later use. Since Java evaluates parameters before invoking a method, this guarantees that the proxied method call gets registered/remembered before the when() method is actually executed. What the when() does is pops off this IOngoingStubbing, which then becomes the object on which thenReturns() is called.

You are not using this "correctly" but it still works for you. How? Well, all that needs to happen is that a method on the proxy needs to be called in order to be registered in a IOngoingStubbing before when() gets executed. You are not directly invoking a method on a proxy, but you are indirectly invoking a method on a proxy by passing the proxy to Method.invoke(). Hence the criteria is satisfied, and when() already has a proxy-method-call registered in an IOngoingStubbing.

You can see the same kind of "accidental" happiness in the following code, which appears at first to make no sense until you realize how Mockito works:

@Test
public void testSomething() throws Exception {
    List listMock = mock(List.class);
    Method m = List.class.getDeclaredMethod("get", int.class);
    m.invoke(listMock, Mockito.anyInt());

    when(null).thenReturn("Hello World");  // Huh? passing null?

    assertEquals("Hello World", listMock.get(0)); // works!
}

The above test actually passes! Even though the argument to when is null, what counts is that the proxy (i.e., mock) instance had the correct method invoked on it prior to the when statement being invoked.

While it is unlikely that Mockito will change the basic way things work under the covers, there is still the potential for this to break for you sometime in the future. Like I said, it is more or less a happy accident that it works. As long as you understand the way it works and the risk involved, more power to you.

like image 121
Kevin Welker Avatar answered Oct 23 '22 15:10

Kevin Welker