I started to learn about Mockito only today. I wrote some simple test (with JUnit, see below), but I can't figure out how can I use mock object inside Spring's managed beans. What is best practices for working with Spring. How should I inject mocked dependency to my bean?
You can skip this till back to my question.
First of all, what I've learned. This is very good article Mocks Aren't Stubs that explains the basics (Mock's checks behavior verification not state verification). Then there is a good example here Mockito and here Easier mocking with mockito. We have explanation that Mockito's mock objects are both mock and stub.
Here Mockito and here Matchers, you can find more examples.
This test
@Test
public void testReal(){
List<String> mockedList = mock(List.class);
//stubbing
//when(mockedList.get(0)).thenReturn("first");
mockedList.get(anyInt());
OngoingStubbing<String> stub= when(null);
stub.thenReturn("first");
//String res = mockedList.get(0);
//System.out.println(res);
//you can also verify using argument matcher
//verify(mockedList).get(anyInt());
verify(mockedList);
mockedList.get(anyInt());
}
works just fine.
Back to my question. Here Injecting Mockito mocks into a Spring bean somebody tries to use Springs ReflectionTestUtils.setField()
, but then here Spring Integration Tests, Creating Mock Objects we have recommendation to change Spring's context.
I didn't really understand last two links... Can somebody explain to me what problem does Spring have with Mockito? What's wrong with this solution?
@InjectMocks
private MyTestObject testObject
@Mock
private MyDependentObject mockedObject
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
https://stackoverflow.com/a/8742745/1137529
EDIT: I wasn't really clear. I will provide 3 examples of code to clarify my self:
Suppose, we have bean HelloWorld with method printHello()
and bean HelloFacade with method sayHello
that forward calls to HelloWorld's method printHello()
.
First example is using Spring's context and without custom runner, using ReflectionTestUtils for dependency injection (DI):
public class Hello1Test {
private ApplicationContext ctx;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.ctx = new ClassPathXmlApplicationContext("META-INF/spring/ServicesImplContext.xml");
}
@Test
public void testHelloFacade() {
HelloFacade obj = (HelloFacade) ctx.getBean(HelloFacadeImpl.class);
HelloWorld mock = mock(HelloWorld.class);
doNothing().when(mock).printHello();
ReflectionTestUtils.setField(obj, "hello", mock);
obj.sayHello();
verify(mock, times(1)).printHello();
}
}
As @Noam pointed out thereis way to run it wihtout explicit call to MockitoAnnotations.initMocks(this);
. I will also drop using of the Spring's context on this example.
@RunWith(MockitoJUnitRunner.class)
public class Hello1aTest {
@InjectMocks
private HelloFacade obj = new HelloFacadeImpl();
@Mock
private HelloWorld mock;
@Test
public void testHelloFacade() {
doNothing().when(mock).printHello();
obj.sayHello();
}
}
Another way to do this
public class Hello1aTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@InjectMocks
private HelloFacadeImpl obj;
@Mock
private HelloWorld mock;
@Test
public void testHelloFacade() {
doNothing().when(mock).printHello();
obj.sayHello();
}
}
Noth, that in preivious example we have to manually instaniate HelloFacadeImpl and assign it to HelloFacade, beacuse HelloFacade is interface. In the last example, we can just declare HelloFacadeImpl and Mokito will instantiate it for us. The drawback of this approach that now, unit-under-test is impl-class and not interface.
@InjectMocks
(relatively recent, although IIRC before that blog post) so there are circumstances where re-ordering the bean definitions might be necessary. I'm not sure what the question is, ultimately. – Dave Newton