Mockito, JUnit, and Spring

I began to learn about Mokito only today. I wrote some simple tests (with JUnit, see below), but I cannot figure out how I can use the mock object inside Spring controls beans. Which is best for working with Spring. How should I mislead addiction to my bean?

You can skip this up to back to my question .

First of all, what I found out. This is a very good article by Mocks Are not Stubs that explains the basics (Mock checks for validation checking ). Then there is a good example of Mockito. Here It is easier to mock with mockito . We have an explanation that Mockito mock objects are both mock-ups and stubs.

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 great.

Back to my question. Here Mockito Injection mocks Spring bean , someone tries to use Springs ReflectionTestUtils.setField() , but here Spring Integration tests, creating Mock Objects , we recommend changing the Spring context.

I did not quite understand the last two links ... Can someone explain to me what the problem is with Spring with Mockito? What is wrong with this decision?

 @InjectMocks private MyTestObject testObject @Mock private MyDependentObject mockedObject @Before public void setup() { MockitoAnnotations.initMocks(this); } 

stack overflow

EDIT : I was not entirely clear. I will give 3 code examples to clarify myself: Suppose we have a HelloWorld bean with the printHello() method and a HelloFacade bean with the sayHello method that redirects the HelloWorld printHello() method.

The first example uses the Spring context without a special runner, using ReflectionTestUtils to inject dependencies (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 that there is a way to start it with an explicit call to MockitoAnnotations.initMocks(this); . I will also stop using Spring context in 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 it

 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 the previous example we need to manually install HelloFacadeImpl and assign it HelloFacade, beacuse HelloFacade is the interface. In the last example, we can simply declare HelloFacadeImpl, and Mokito will create it for us. The disadvantage of this approach is that now a single test refers to the impl class rather than the interface.

+68
java spring unit-testing mockito
Jun 6 2018-12-06T00:
source share
7 answers

Honestly, I'm 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 cases, you should not worry about Spring. You rarely have to have spring involved in writing your unit test. In the normal case, you only need to create an instance of the system under test (SUT, target audience) in unit test, and also establish the SUT dependencies in the test. Dependencies are usually layouts / stubs.

Your original proposed method, and Example 2, 3 exactly does what I describe above.

In some rare cases (for example, integration tests or some special unit tests) you need to create a spring application context and get the SUT from the application context. In this case, I believe you can:

1) Create your SUT in the spring ctx application, get a link to it and attach layouts 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 directions given in your link Spring Integration tests, creating mock objects . This approach is to create mocks in the context of the spring application, and you can get the mock object from the ctx application to do your validation / validation:

 @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 that the first case will have dependencies introduced after going through the spring life cycle, etc. (For example, bean initialization), while the last case is introduced before calls. For example, if your SUT implements spring InitializingBean, and the initialization procedure includes dependencies, you will see the difference between the two approaches. I believe that for these two approaches there is no right or wrong if you know what you are doing.

Just adding, @Mock, @Inject, MocktoJunitRunner, etc. not needed to use mockito. These are just utilities to help you type Mockito.mock (Foo.class) and a bunch of setter calls.

+49
Jun 07 2018-12-12T00:
source share

Your question seems to be asking which of the three examples you cited is preferred.

Example 1 using Reflection TestUtils is not suitable for unit testing. You really don't want to load the spring context at all for the unit test. Just mock and enter what is required, as shown in your other examples.

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

Example 2 is a valid approach, and using @RunWith(MockitoJUnitRunner.class) eliminates the need to specify the @Before method and explicitly call MockitoAnnotations.initMocks(this);

Example 3 is another valid approach that @RunWith(...) does not use. You did not instantiate the class under the HelloFacadeImpl test explicitly, but you could do the same with Example 2.

My suggestion is to use Example 2 to test your device, as this reduces code clutter. You can return to a more detailed configuration if and when you are forced to do so.

+6
Jun 07 2018-12-12T00:
source share

The introduction of some new testing tools in Spring 4.2.RC1 allows you to run one of the Spring integration tests, which are independent of SpringJUnit4ClassRunner . Check out this part of the documentation.

In your case, you can write your Spring integration test and still use ones like:

 @RunWith(MockitoJUnitRunner.class) @ContextConfiguration("test-app-ctx.xml") public class FooTest { @ClassRule public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); @Rule public final SpringMethodRule springMethodRule = new SpringMethodRule(); @Autowired @InjectMocks TestTarget sut; @Mock Foo mockFoo; @Test public void someTest() { // .... } } 
+4
May 26 '15 at 15:01
source share

You do not need MockitoAnnotations.initMocks(this); if you are using mockito 1.9 (or newer) - all you need is:

 @InjectMocks private MyTestObject testObject; @Mock private MyDependentObject mockedObject; 

The @InjectMocks will add all your mocks to the MyTestObject .

+3
Jun 06 2018-12-06T00:
source share

Here is my short summary.

If you want to write unit test, do not use the Spring application, because you do not need any real dependencies introduced into the class that you are testing on the module. Instead, use mocks with either the @RunWith annotation @RunWith(MockitoJUnitRunner.class) on top of the class, or using MockitoAnnotations.initMocks(this) in the @Before method.

If you want to write an integration test, use:

 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("yourTestApplicationContext.xml") 

To customize the application context using a database in memory, for example. Usually you do not use mocks in integration tests, but you can do this using the MockitoAnnotations.initMocks(this) approach described above.

+2
Jan 27 '15 at 9:50
source share

The difference is whether to instantiate the @InjectMocks annotated field is in the Mockito version, and not whether you use MockitoJunitRunner or MockitoAnnotations.initMocks . In 1.9, which will also handle some of the injections of your @Mock fields @Mock , it will do the creation for you. In earlier versions, you must create the instance yourself.

This is how I do unit testing of my Spring beans. No problems. People are confused when they want to use Spring's configuration files to actually inject layouts that cross the point of unit tests and integration tests.

And, of course, the unit under test is Impl. You need to check the real concrete thing, right? Even if you declared it an interface, you would need to create the real thing to test it. Now you can get into spies, which are stubs / layouts around real objects, but this should be for corner cases.

0
Jun 06 '12 at 22:26
source share

If you port the project to Spring Boot 1.4, you can use the new @MockBean annotation to fake MyDependentObject . With this function, you can remove the annotations of Mockito @Mock and @InjectMocks from your test.

0
Sep 05 '16 at 13:30
source share



All Articles