Use of @InjectMock and difference between Mockito.mock Vs @Mock Vs @MockBean

Use of @InjectMock and difference between Mockito.mock Vs @Mock Vs @MockBean

Mockito.mock() Vs @Mock
If you want to mock a bean, you can use either Mockito.mock() or @Mock Annotation. Both giving the same result. There are slight advantages of @Mock Annotation when compare to Mockito.mock()

We need to do some additional setup when test using Mockito. mock. When you test any Java class, you have to add the following line of code in your test method.

Map<String,String> mocked = mock(Map.class);

When you use @Mock Annotation you don’t have to add the above line for all methods which you want to test but when you use @Mock Annotation, we have to use the following Annotation at the class level based on the JUnit version. This Annotation takes the responsibility of setting up Java objects before testing code.
If you are using JUnit 4 you have to use the following annotation at the class level.

@RunWith(MockitoJUnitRunner.class)

If you are using JUnit 5, you have to use the following annotation at the class level

@ExtendWith(MockitoExtension.class)

When you use Mock. mockito(), the code should look like below

import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.mockito.Mockito.*;

public class MockTest {

    @Test
    public void mockMap() {
        Map<String, String> mocked = mock(Map.class);
        mocked.put("Mockito.mock", "MockType1");
        mocked.put("@Mock", "MockType2");
        verify(mocked, times(1)).put("Mockito.mock", "MockType1");
        verify(mocked, times(1)).put("@Mock", "MockType2");
    }
}

When you use @Mock, your Junit test code should looks like below

@ExtendWith(MockitoExtension.class)
public class MockTest2 {

    @Mock
    Map<String, String> mockMap;

    @Test
    public void mockAtClassLevel() {
        mockMap.put("Mockito.mock", "MockType1");
        mockMap.put("@Mock", "MockType2");
        verify(mockMap, times(1)).put("Mockito.mock", "MockType1");
        verify(mockMap, times(1)).put("@Mock", "MockType2");
    }
}

@InjectMock
This Annotation is for a different purpose and we can’t compare it with @Mock annotation. The purpose of @InjectMock is If you want to test a class and the class is having several dependency classes, in such a case you can use @InjectMock. The dependency classes are must be annotated with @Mock Annotation. For better understanding, I will be using the following example

public class RegistrationService {

    private CustomerService customerService;

    private AddressService addressService;

    public RegistrationService(CustomerService customerService, AddressService addressService) {
        this.customerService = customerService;
        this.addressService = addressService;
    }

    public String doRegistration(){
        System.out.println("Registration Start");
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(customerService.save());
        stringBuffer.append("\n");
        stringBuffer.append(addressService.save());
        System.out.println("Registration completed");
        return stringBuffer.toString();
    }
}

The above class is having two dependants classes, one is CustomerService and another one is AddressService. I am calling the save method of CustomerService and AddressService from the doRegistration() method. I am simply returning a String value in the save method.
Our intention is, we want to test the DoRegistration method. In order to test this method. we need to CustomerService and AddressService. Following are code implementation for CustomerService and AddressService.

public class CustomerService {

    public String save() {
        return  "The Customer saved Successfully";
    }
}
public class AddressService {

    public String save() {
        return "The Address saved Successfully";
    }
}

Testing Code implementation
The class must be Annotated with @ExtendWith(MockitoExtenstion.class) if you are using JUnit 5 otherwise  you should use @RunWith(MockitoJUnitRunner.class) (JUnit 4)

@ExtendWith(MockitoExtension.class)
public class RegistrationServiceTest {

The RegistrationService is my main class to test so I am annotating with @InjectMock like below

@InjectMocks
private RegistrationService registrationService;

This RegistrationService class is having two dependent class called CustomerService, AddressService. These dependent classes must be Annotated with @Mock. like below.

@Mock
private CustomerService customerService;
@Mock
private AddressService addressService;

Through this @InjectMocks Annotation, the Mockito framework understands that the main class is RegistrationService. Through @Mock Annotation, the Mockito framework understands that CustomerService and AddressService are dependant classes for Registration Service. Following is my test method to test doRegistration 

@Test
public void doRegistrationTest() {
    when(customerService.save()).thenReturn("Customer Saved");
    when(addressService.save()).thenReturn("Address Saved");
    String response = registrationService.doRegistration();
    Assertions.assertThat(response).contains("Customer");
    Assertions.assertThat(response).contains("Address");
}

The following line mocks the save() method from CustomerService.  Through this line, I am saying to Mockito framework, please return the “Customer Saved” as return value while calling save method from doRegistration() method. 

when(customerService.save()).thenReturn("Customer Saved");

The Following line mocks the save() method from AddressService. Through this line, I am saying to Mockito framework, please return the “Address Saved” as return value while calling save method from doRegistration() method. 

when(addressService.save()).thenReturn("Address Saved");

When I call the following doRegistration method, the mocked value returned and assigned to the response variable.

String response = registrationService.doRegistration();

Following checking that “Customer ” and “Address” values exist the returned value. if exist, the test case returns success.

Assertions.assertThat(response).contains("Customer");
Assertions.assertThat(response).contains("Address");

I am using AssertJ to asserting the value so make sure your project is having the library, if you are using spring boot, it is available by default.  I hope that so far you understand about @Mock, Mockito.mock() and @InjectMock.  

The final Annotation is @MockBean
If you want to say simply if you want to mock a bean that is deployed in Spring Boot IOC Container, we should use @MockBean. Please take a look at the below method. The next question is When this scenario will come? Let say you are writing Integration Test cases. Integration Test launch entier Spring Context so your all registered Java beans are available. Now I want to replace my Real Java Bean class with Mock Java bean in the IOC container. In such a case we need to use @MockBean. Look at the below class. This is an Integration test case class.

When I call “/items” endpoint, returns product Item from the Database but I don’t want to do real DB call instead I want to mock ProductItemRepository class. This ProductItemRepository class registered in Spring IOC Container, If you mock this class using @MockBean, Mockito Framework replaces the mocking object of ProductItemRepository in Spring IOC Container. if you use @Mock in this scenario, it never works. 

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SampleControllerIT {

    @Autowired
    private TestRestTemplate testRestTemplate;

    @MockBean
    ProductItemRepository productItemRepository;

    @Test
    public void retrieveProductItem() throws Exception{
        when(productItemRepository.findAll()).thenReturn(
                Arrays.asList(new ProductItem(1,"SHIRTS",200,10),
                        new ProductItem(2,"T-SHIRTS",300,20))
        );
        String response = this.testRestTemplate.getForObject("/items",String.class);
        JSONAssert.assertEquals("[{id:1},{id:2}]",response,false);
    }
}

 

 

Close Menu
×
×

Cart