7

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!

Kevin Welker
  • 7,719
  • 1
  • 40
  • 56
Andrew
  • 245
  • 1
  • 3
  • 8
  • 2
    I'm still not sure what you are trying to do. You can "combine a class object and a method into a methodCall" simply using reflection: `method.invoke(someInstanceOfClass)`, but I think you are after something else. If you are looking to do exactly what Mockito does (i.e., creating a proxy of a class and intercepting method calls), they are using CGLib under the covers. – Kevin Welker Apr 12 '12 at 14:25
  • Yes, invoke() is what I am after, but it wouldn't work with a mock... I don't think. I think I've tried this before and it didn't work. I'll try that right now. – Andrew Apr 12 '12 at 14:43
  • If you made your comment as an answer, I would upvote your answer as... well, reflection DOES work. **when(method.invoke(mock)).thenReturn("Hello world.");** is the correct syntax for anyone who stumbles across this. – Andrew Apr 12 '12 at 15:25
  • Give Kevin a little time to do that, but if he doesn't, then answer it yourself and accept the answer so we can see that question is answered. – jhericks Apr 12 '12 at 16:01
  • are you sure the mocking is working? Since you cannot mock a `Method`, I don't see how this would work, since `Mockito.when()` requires a mock/spy. However, in creating the `when` statement, if you pass it a real `Method` instance, then that method will be invoked at the time of the execution of the `when` statement. So maybe you are seeing the method invoked as a side-effect of invoking `when`. – Kevin Welker Apr 12 '12 at 21:40
  • Andrew, if you're happy with Kevin's answer, could you please click the checkbox next to it? That way, people visiting the site can see that there's an accepted answer. It also rewards Kevin with some reputation points. – Dawood ibn Kareem Apr 13 '12 at 06:34

2 Answers2

7

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.

Kevin Welker
  • 7,719
  • 1
  • 40
  • 56
  • I am not mocking the Method class. The method is there to symbolize the actual method I am invoking onto the mock class. For example, I have an **AndrewTest** class with a **getString()** method that returns **"AndrewTest: getString"** with no parameters needed. I can make a **Class clazz** object that is assigned to "AndrewTest.class" and do **Method method = clazz.getDeclaredMethod("getString",null)**. However, to create a mock object I do **AndrewTest mock = mock(AndrewTest.class)**. Doing **when(method.invoke(mock)).thenReturn("Hello world")** will inject the **"Hello world."** string. – Andrew Apr 13 '12 at 11:55
  • ^character limit, and sorry for the ugliness but I can't make spaces in comments. Then as a test, I'll create a **real AndrewTest** object for comparison vs the **AndrewTest mock** object, **AndrewTest andrewReal = new AndrewTest()**. Doing **System.out.println(andrewReal.getString())** will print **"AndrewTest: getString"**, but doing **System.out.println(mock.getString())** will print **"Hello world."** I want to clarify something, in your comments on my question you said that if I passed the invoke to the when statement then the method would be invoked at that time. This is untrue. – Andrew Apr 13 '12 at 11:59
  • ^character limit again! Last post. When you pass any method call, either it be `method.invoke(someInstanceOfClass)` or `someString.charAt(0)` to the when statement, these two methods are being called, yes (I just tested this by adding a print statement to getString()), but that doesn't matter as what we really want is to tell the mock object "when this method call happens to this object, return this". If you play around with Mockito a little I think you will understand how when() can be used. – Andrew Apr 13 '12 at 12:03
  • The simplest thing I can say is that `when(method.invoke(mock)).thenReturn("Hello world.")` is exactly equal to `when(andrewReal.getString()).thenReturn("Hello world.")` but the first when call is what I wanted as the method or the mock objects can change (I needed dynamic, custom when calls). At the moment, my MockFramework is working beautifully. – Andrew Apr 13 '12 at 12:08
  • I'm glad that it is working, but I don't believe you understand how it is working, which is almost more by accident rather than the design by which Mockito was intended to be invoked. I will try to detail more in edits to my answer. – Kevin Welker Apr 13 '12 at 18:31
  • I am working from Eclipse IDE and when I do something like `when(rs.getString(1)).thenReturn("yadda yadda")` where rs is a ResultSet and hover over the **when**, a small description will pop up saying that this specific when() takes a **String methodCall**. Because an actual string such as "" or "hello" or whatever does not actually work despite it saying String methodCall, it actually hit me. It requires a real method call and I wasn't sure how to do that in Java. I even overlooked reflection but then you posted and I was like... – Andrew Apr 13 '12 at 18:58
  • "oh yeah, doing `method.invoke(someClass)` creates a methodCall, doesn't it?", tried it, and it's working. I will admit I'm a bit of a noob when it comes to programming compared to already-existing programmers as I'm just a 4th year CS student, so maybe when I say X works because Y it may be incorrect, but at least it actually works! I even checked the API and doing `method.invoke(someClass)` does return an Object, which I still believe is the actual methodCall that Mockito.when() requires. http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Method.html – Andrew Apr 13 '12 at 19:00
  • Hmm, I just read over your edit. So future changes may break what I did, but what if I simply do not update to these future changes? Wouldn't that keep me safe? – Andrew Apr 13 '12 at 19:05
  • Yes, I think so, but Mockito is a very active project. You might lose the benefits of future enhancements, bug fixes etc. But this is the fun stuff of software engineering: weighing the pros/cons of alternatives, and then making a decision and living with it :-) But, again, yes, I think this should continue to work for you. Of course, I don't have the big picture of your project in mind, so it's possible there are some other "gotcha"s lurking in your solution. Best advice: write a LOT of tests. – Kevin Welker Apr 13 '12 at 19:11
0

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.

Rick-Rainer Ludwig
  • 2,371
  • 1
  • 26
  • 42
  • I'm not 100% sure what you mean by "specified in the class" but someMethod would be an existing method in someClass so doing someClass.someMethod() would be valid and thus mock.someMethod() would be valid as well. – Andrew Apr 12 '12 at 13:43
  • You mention you want to combine a method and the mock. I understood you want to add a new method to the mock after initialization. So, I do not understand you qyestion... Have a look to: http://code.google.com/p/mockito/. There is a sample 'Or stub method calls'. Is this want you want!? – Rick-Rainer Ludwig Apr 12 '12 at 14:00
  • It's not adding a new method to the mock after initialization. I am working on a mocking framework where you can pass it a class and it'll create a mock object for you via Mockito. Then, via the framework, if you want to inject some data into that mock object, then you'd pass the framework either a String that resembles the name of the method or the Method object of the method. The best explanation I can give is how to recreate the **mock.someMethod()** in **when(mock.someMethod()).thenReturn(null);** via Java rather than actually typing it/hardcoding it. I am seeking dynamicism. – Andrew Apr 12 '12 at 14:02
  • Dynamicism and mock does not fit together. A mock is static. You can reassign the value which is returned by the method, but to have a dynamic output, you can create an implementation of your interface as a stub or fake (see:http://martinfowler.com/articles/mocksArentStubs.html). Mockito is for mocks. ;-) – Rick-Rainer Ludwig Apr 12 '12 at 14:21
  • I did not mean dynamic output. The idea is that I would call **mockFramework.injectData("someMethod")** and then inside **injectData(String methodName)** it would call **when(mock + methodName).thenReturn(null);**. I want a way to dynamically create a when call to the mock, but I'm not sure exactly how to put together the mock and the method call (**someMethod()**). Like I said, I want a way to literally recreate the entirety of **mock.SomeMethod()** in **when(mock.someMethod()).thenReturn(null);**. The idea is that mock.someMethod() can change per every injectData call. – Andrew Apr 12 '12 at 14:28
  • Another example of using the framework is: MockFramework mf = new MockFrameWork("SomeClass.class"); mf.injectData("someMethod"); mf.injectData("someMethod2"); The framework would create the mock object over the SomeClass.class. This is the easy part. The harder part is getting injectData to be able to make a when call to the mock object depending on the arguments given. Giving it "someMethod" would make it call when(mock.someMethod()).thenReturn(null); on the mock. Giving it "someMethod2" would make it call when(mock.someMethod2()).thenReturn(null); on the mock. – Andrew Apr 12 '12 at 14:28
  • I think, you should clarify the question. I am completely lost, what you want to achieve. I think, you mix different concepts up into something too complicated to implement which would be much easier be done in another way. That's my impression. Maybe you should refine your question above, provide more details and maybe a real world use case for the functionality you want to have. This would help much more, than the abstract concepts at the very moment. – Rick-Rainer Ludwig Apr 12 '12 at 14:37
  • The real idea behind this is not about the mock object, it's about combining together a class object and a method call and I don't even know if it's possible in java or not. Another example would be having a String str = "hi"; and having some String str2 = "charAt(0)" and then putting them together to do str.charAt(0) and, if printed, the output would be 'h'. How exactly do I put it together? That is my question. – Andrew Apr 12 '12 at 14:39
  • That is the concept of closures you want, I guess: http://de.wikipedia.org/wiki/Closure. This is not possible in Java, yet. I comes with Java 8 next year, when Oracle keeps it plan. – Rick-Rainer Ludwig Apr 12 '12 at 14:48