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

落爺英雄遲暮 提交于 2021-02-18 11:15:09

问题


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!


回答1:


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.




回答2:


I think, adding a new method after class initialization is not possible as long as the method is not specified in the interface or class provided to Mockito. You would change the class signature after initialization and this is something which is not possible.

For stub method calls have a look to: http://code.google.com/p/mockito/. There is a sample on stub method calls.

If you want to get dynamic answers, not static ones, you should not use Mockito. Use a fake object or a stub to get you behavior for testing. See: http://martinfowler.com/articles/mocksArentStubs.html for details on this issue.



来源:https://stackoverflow.com/questions/10124482/mockito-is-it-possible-to-combine-mock-with-a-method-name-to-create-a-methodcal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!