Difference Between @Mock and @Injectmocks

Mockito how does @InjectMocks works

  1. Qn why mockedAppointments.keySet() doesn't throw a nullPointerException (mockedAppointments is only declared)? maybe because it's a mock? And if the reason is this, why doesn't a mock throw "nullPointerException"?

    Answer is : Because mockedAppointments.keySet().size is 0 and mockedAppointments.keySet() is empty, it is the behavior of the mock
  2. 2.0 Qn System.out.println(this.appointments == null); line prints "false" is i keep the @InjectMocks annotation, "true" otherwise, but why?

    Answer is : In junit or mockito System.out.println won't works, rather use logger.

    2.1 Qn In the first case (@InjectMocks is kept) where is the "appointments" attribute of "agenda" initialized?

    Answer is : it is mocking the class and eg for list it initialize as 0 and keep value as empty, Inaddition to that
@InjectMocks
private MyAgenda agenda;

should be declared like this

2.2 Qn Is it initialized because we inject the value of mockedAppointments into it?

Answer is : mockedAppointments and agenda doesn't have any connection, inaddition to that
Rather this injectMocks you can use @Mocks,it works.

 @Mocks
private MyAgenda agenda;

When should we use @InjectMocks?

Your actual question can't be answered without you providing further code (the code you are showing does not explain the results you claim to observe).

Regarding the underlying question: you probably should not use @InjectMocks; one of its core problems is: if injecting fails, Mockito does not report an error to you.

In other words: you have passing unit tests, and some internal detail about a field changes ... and your unit tests break; but you have no idea why ... because the mocking framework doesn't tell you that the "initial" step of pushing a mock into the class under test is all of a sudden failing. See here for further reading.

But to be clear here: in the end, this almost a pure style question. People used to @InjectMocks will love it; other people do object it. Meaning: there is no clear evidence whether you should use this concept. Instead: you study the concept, you understand the concept, and then you (and the team working with you) make a conscious decision whether you want to use this annotation, or not.

Edit: I think I get your problem now. The idea of @InjectMocks is to inject a mocked object into some object under test.

But: you are doing that manually in both cases:

 service.setUserDao(dao);

Meaning: if injecting works correctly (and there isn't a problem that isn't reported by Mockito) then your example that uses that annotation should also work when you remove that one line. Whereas the testcase that doesn't have @InjectMocks should fail without that line!

In other words: your testcases are both passing because your code does a "manual inject"!

Using @Mock and @InjectMocks

O.K, I got my mistake!!!
I've used the @InjectMocks but initialized the same variable in the init() method...
So what happened was that mockito injected the mock objects to my variable - but seconds later I ran it over - initializing that very same variable!!!

Difference between @InjectMocks and @Autowired usage in mockito?

@InjectMocks is a Mockito mechanism for injecting declared fields in the test class into matching fields in the class under test. It doesn't require the class under test to be a Spring component.

@Autowired is Spring's annotation for autowiring a bean into a production, non-test class.

If you wanted to leverage the @Autowired annotations in the class under test, another approach would be to use springockito which allows you to declare mock beans so that they will be autowired into the class under test the same way that Spring would autowire the bean. But typically that's not necessary.

Difference between @TestSubject and @InjectMocks?

@TestSubject is the annotation of EasyMock that does the same like Mockito's @InjectMocks. If you're using Mockito then you have to use @InjectMocks.

When to use and not use @Mock annotation, @MockBean annotation, @InjectMock annotation & @Autowired annotation in a spring webflux reactive project

@Mock

Used to make Mockito create a mock object.

@InjectMock

When you want Mockito to create an instance of an object and use the mocks annotated with @Mock as its dependencies.

@AutoWired

Used when you want to autowire a bean from the spring context, works exactly the same as in normal code but can only be used in tests that actually creates an application context, such as tests annotated with @WebMvcTest or @SpringBootTest.

@MockBean

Can be used to add mock objects to the Spring application context. The mock will replace any existing bean of the same type in the application context. If no bean of the same type is defined, a new one will be added. Often used together with @SpringBootTest

So normally you either:

  • Use @Mock and @InjectMocks for running tests without a spring
    context, this is preferred as it's much faster.
  • Use @SpringBootTest or @SpringMvcTest to start a spring context together with @MockBean to create mock objects and @Autowired to get an instance of class you want to test, the mockeans will be used for its autowired dependencies. You use this when writing integration tests for code that interact with a database or want to test your REST API.

How to mock an instance created by a static method?

You need to mock constructor of Generator class.


Solution 1: Using Mockito (Main preferable)
Mockito suports mock construction since 3.5 version. Documentation

Add mockito-inline to your dependencies. It provides ability to generate mocks on constructor invocations.

        <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.3.1</version>
<scope>test</scope>
</dependency>

Mock constructor.

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;

import static org.mockito.Mockito.*;

class ConverterTest {
@Test
public void convertTest(){
//create mock for Generator class constructor
try (MockedConstruction<Generator> mockedGenerator = Mockito.mockConstruction(Generator.class,
(mock, context) -> {
when(mock.generate()).thenReturn("123");
})) {

//execute Converter class under the test
String result = Converter.convert();

//ensure that constructor was executed
Assertions.assertFalse(mockedGenerator.constructed().isEmpty());
//ensure the generate method was executed
verify(mockedGenerator.constructed().get(0), times(1)).generate();
//verify return value
Assertions.assertEquals("123", result);
}
}
}

Solution 2: Using PoserMock
PowerMock does not have JUnit 5 support, only Junit 4.

Example of mocking constructor via PowerMock

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(SubConverter.class)
public class ConverterTest {

@Mock
Generator generatorMock; //create Mock

@Test
public void convertTest() throws Exception {
//configure Mock
Mockito.when(generatorMock.generate()).thenReturn("123");
//return mock when constructor is executing
PowerMockito.whenNew(Generator.class).withNoArguments().thenReturn(generatorMock);

//perform Converter under the test
String result = Converter.convert();

//check return value
Assert.assertEquals("123", result);
}
}


Related Topics



Leave a reply



Submit