Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito, JUnit and Spring

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.

like image 476
alexsmail Avatar asked Jun 06 '12 00:06

alexsmail


People also ask

How is Mockito used with spring?

Summary. Mocking in unit testing is extensively used in Enterprise Application Development with Spring. By using Mockito, you can replace the @Autowired components in the class you want to test with mock objects. You will be unit testing controllers by injecting mock services.

Is JUnit and Mockito are same?

JUnit is the Java library used to write tests (offers support for running tests and different extra helpers - like setup and teardown methods, test sets etc.). Mockito is a library that enables writing tests using the mocking approach.

What is Mockito in spring testing?

Mockito is an open-source testing framework used for unit testing of Java applications. It plays a vital role in developing testable applications. Mockito is used to mock interfaces so that a dummy functionality can be added to a mock interface that can be used in Unit Testing.

Is Mockito part of spring boot?

The spring-boot-starter-test dependency includes JUnit 5 dependencies, and Mockito.


2 Answers

Honestly I am not sure if I really understand your question :P I will try to clarify as much as I can, from what I get from your original question:

First, in most case, you should NOT have any concern on Spring. You rarely need to have spring involved in writing your unit test. In normal case, you only need to instantiate the system under test (SUT, the target to be tested) in your unit test, and inject dependencies of SUT in the test too. The dependencies are usually a mock/stub.

Your original suggested way, and example 2, 3 is precisely doing what I am describing above.

In some rare case (like, integration tests, or some special unit tests), you need to create a Spring app context, and get your SUT from the app context. In such case, I believe you can:

1) Create your SUT in spring app ctx, get reference to it, and inject mocks to it

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("test-app-ctx.xml") public class FooTest {      @Autowired     @InjectMocks     TestTarget sut;      @Mock     Foo mockFoo;      @Before     /* Initialized mocks */     public void setup() {         MockitoAnnotations.initMocks(this);     }      @Test     public void someTest() {          // ....     } } 

or

2) follow the way described in your link Spring Integration Tests, Creating Mock Objects. This approach is to create mocks in Spring's app context, and you can get the mock object from the app ctx to do your stubbing/verification:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("test-app-ctx.xml") public class FooTest {      @Autowired     TestTarget sut;      @Autowired     Foo mockFoo;      @Test     public void someTest() {          // ....     } } 

Both ways should work. The main difference is the former case will have the dependencies injected after going through spring's lifecycle etc. (e.g. bean initialization), while the latter case is injected beforehands. For example, if your SUT implements spring's InitializingBean, and the initialization routine involves the dependencies, you will see the difference between these two approach. I believe there is no right or wrong for these 2 approaches, as long as you know what you are doing.

Just a supplement, @Mock, @Inject, MocktoJunitRunner etc are all unnecessary in using Mockito. They are just utilities to save you typing the Mockito.mock(Foo.class) and bunch of setter invocations.

like image 162
Adrian Shum Avatar answered Oct 10 '22 05:10

Adrian Shum


Your question seems to be asking about which of the three examples you have given is the preferred approach.

Example 1 using the Reflection TestUtils is not a good approach for Unit testing. You really don't want to be loading the spring context at all for a unit test. Just mock and inject what is required as shown by your other examples.

You do want to load the spring context if you want to do some Integration testing, however I would prefer using @RunWith(SpringJUnit4ClassRunner.class) to perform the loading of the context along with @Autowired if you need access to its' beans explicitly.

Example 2 is a valid approach and the use of @RunWith(MockitoJUnitRunner.class) will remove the need to specify a @Before method and an explicit call to MockitoAnnotations.initMocks(this);

Example 3 is another valid approach that doesn't use @RunWith(...). You haven't instantiated your class under test HelloFacadeImpl explicitly, but you could have done the same with Example 2.

My suggestion is to use Example 2 for your unit testing as it reduces the code clutter. You can fall back to the more verbose configuration if and when you're forced to do so.

like image 26
Brad Avatar answered Oct 10 '22 05:10

Brad