Using Mockito to stub and execute methods for testing
I've asked a couple of jUnit and Mockito oriented questions recently and I'm still really struggling to get the hang of it. The tutorials are all for very simple examples, so I'm struggling to scale up my test cases to work for my classes.
I'm currently trying to write some test cases for a method I have in one of my agents in a webapp. The method interacts with a couple of other methods inside the agent to validate some objects. I just want to test this one method right now.
Here's what I have tried to do:
-
Create a Mockito object of my agent like so:
MyProcessingAgent mockMyAgent = Mockito.mock(MyProcessingAgent.class);
-
Setup stubs(hopefully the right term) using Mockito.when like so:
Mockito.when(mockMyAgent.otherMethod(Mockito.any(arg1)).thenReturn(requiredReturnArg);
-
Try executing my method like so:
List myReturnValue = mockMyAgent.methodThatNeedsTestCase();
I was expecting to things in myReturnValue
, but received 0 instead so I tried to debug. When I call the method, it never executes. I have a debug point at the first line in the method that never gets touched.
If I want to execute the code in one method of a class, but force other methods in the class (one's that try to interact with databases in the outside world) to return faked out values. Is this possible with Mockito?
It appears that my current method of approach is not a correct testing style, but I'm not sure how to move forward. Can I mock my class and have one method be executed like normal while other methods are stubbed to return my given values so that I don't have to deal with data access during testing this one method?
You are confusing a Mock
with a Spy
.
In a mock all methods are stubbed and return "smart return types". This means that calling any method on a mocked class will do nothing unless you specify behaviour.
In a spy the original functionality of the class is still there but you can validate method invocations in a spy and also override method behaviour.
What you want is
MyProcessingAgent mockMyAgent = Mockito.spy(MyProcessingAgent.class);
A quick example:
static class TestClass {
public String getThing() {
return "Thing";
}
public String getOtherThing() {
return getThing();
}
}
public static void main(String[] args) {
final TestClass testClass = Mockito.spy(new TestClass());
Mockito.when(testClass.getThing()).thenReturn("Some Other thing");
System.out.println(testClass.getOtherThing());
}
Output is:
Some Other thing
NB: You should really try to mock the dependencies for the class being tested not the class itself.
So, the idea of mocking the class under test is anathima to testing practice. You should NOT do this. Because you have done so, your test is entering Mockito's mocking classes not your class under test.
Spying will also not work because this only provides a wrapper / proxy around the spied class. Once execution is inside the class it will not go through the proxy and therefore not hit the spy. UPDATE: although I believe this to be true of Spring proxies it appears to not be true of Mockito spies. I set up a scenario where method m1()
calls m2()
. I spy the object and stub m2()
to doNothing
. When I invoke m1()
in my test, m2()
of the class is not reached. Mockito invokes the stub. So using a spy to accomplish what is being asked is possible. However, I would reiterate that I would consider it bad practice (IMHO).
You should mock all the classes on which the class under test depends. This will allow you to control the behavior of the methods invoked by the method under test in that you control the class that those methods invoke.
If your class creates instances of other classes, consider using factories.
You've nearly got it. The problem is that the Class Under Test (CUT) is not built for unit testing - it has not really been TDD'd.
Think of it like this…
- I need to test a function of a class - let's call it myFunction
- That function makes a call to a function on another class/service/database
- That function also calls another method on the CUT
In the unit test
- Should create a concrete CUT or
@Spy
on it - You can
@Mock
all of the other class/service/database (i.e. external dependencies) - You could stub the other function called in the CUT but it is not really how unit testing should be done
In order to avoid executing code that you are not strictly testing, you could abstract that code away into something that can be @Mock
ed.
In this very simple example, a function that creates an object will be difficult to test
public void doSomethingCool(String foo) {
MyObject obj = new MyObject(foo);
// can't do much with obj in a unit test unless it is returned
}
But a function that uses a service to get MyObject is easy to test, as we have abstracted the difficult/impossible to test code into something that makes this method testable.
public void doSomethingCool(String foo) {
MyObject obj = MyObjectService.getMeAnObject(foo);
}
as MyObjectService can be mocked and also verified that .getMeAnObject() is called with the foo variable.