POST

 @Test
    void addEmployeeDetails() throws Exception {
        Employee objEmployee = (Employee) TestTraits.getObjfromJson("json-data/EmployeeObject.json");

        String jsonRequestString = mapper.writeValueAsString(objEmployee);

        when(employeeServiceImpl.saveEmployee(any())).thenReturn("101");

        ResultActions objResultActions = this.mockMvc.perform(post("/empmgmt/employees")
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonRequestString))
                .andDo(print())
                .andExpect(jsonPath("$.empID", is(Matchers.notNullValue())))
                .andExpect(jsonPath("$.empID", is(101)))
                .andExpect(jsonPath("$.empName", is("Mani")))
                .andExpect(status().isCreated());
    }

GET

 @Test
    void getEmployeeDetails() throws Exception {
        ClassLoader loader = Test.class.getClassLoader();
        Employee objEmployee = (Employee) TestTraits.getObjfromJson("json-data/EmployeeObject.json");

        Optional<Employee> objEmp = Optional.ofNullable(objEmployee);
        when(employeeServiceImpl.findEmployee(EMP_ID)).thenReturn(objEmp);

        ResultActions objResultActions = this.mockMvc.perform(get("/empmgmt/employees/{empID}",EMP_ID))
                                                    .andDo(print())
                                                    .andExpect(jsonPath("$.empID", is(101)))
                                                    .andExpect(jsonPath("$.empName", is("Mani")))
                                                    .andExpect(jsonPath("$._links.all-employees.href", is("http://localhost/empmgmt/employees")))
                                                    .andExpect(status().isOk());
    }

DELETE

 @Test
    void deleteEmployeeDetails() throws Exception {
        when(employeeServiceImpl.deleteEmployee(Long.valueOf(EMP_ID))).thenReturn(true);

        this.mockMvc.perform(delete("/empmgmt/employees/{empID}",EMP_ID))
                .andDo(print())
                .andExpect(content().string(containsString("Employee Deleted Successfully")))
                .andExpect(status().isOk());
    }

EmployeeControllerTest.java

package com.mugil.org.controllers;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mugil.org.TestTraits;
import com.mugil.org.models.Employee;
import com.mugil.org.services.EmployeeServiceImpl;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@ExtendWith(SpringExtension.class)
@WebMvcTest(EmployeeController.class)
class EmployeeControllerTest {

    @MockBean
    EmployeeServiceImpl employeeServiceImpl;

    ObjectMapper mapper = new ObjectMapper();

    @Autowired
    private MockMvc mockMvc;

    private static final String EMP_ID = "101";

    @Test
    void getEmployeeDetails() throws Exception {
        ClassLoader loader = Test.class.getClassLoader();
        Employee objEmployee = (Employee) TestTraits.getObjfromJson("json-data/EmployeeObject.json");

        Optional<Employee> objEmp = Optional.ofNullable(objEmployee);
        when(employeeServiceImpl.findEmployee(EMP_ID)).thenReturn(objEmp);

        ResultActions objResultActions = this.mockMvc.perform(get("/empmgmt/employees/{empID}",EMP_ID))
                                                    .andDo(print())
                                                    .andExpect(jsonPath("$.empID", is(101)))
                                                    .andExpect(jsonPath("$.empName", is("Mani")))
                                                    .andExpect(jsonPath("$._links.all-employees.href", is("http://localhost/empmgmt/employees")))
                                                    .andExpect(status().isOk());
    }

    @Test
    void deleteEmployeeDetails() throws Exception {
        when(employeeServiceImpl.deleteEmployee(Long.valueOf(EMP_ID))).thenReturn(true);

        this.mockMvc.perform(delete("/empmgmt/employees/{empID}",EMP_ID))
                .andDo(print())
                .andExpect(content().string(containsString("Employee Deleted Successfully")))
                .andExpect(status().isOk());
    }

    @Test
    void getAllEmployeeDetails() throws Exception {
        List<Employee> arrEmployees = new ArrayList<>();
        arrEmployees.add(getEmployee());

        when(employeeServiceImpl.findAllEmployee()).thenReturn(arrEmployees);

        ResultActions objResultActions = this.mockMvc.perform(get("/empmgmt/employees")
                .contentType(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(jsonPath("$[0].empID", is(Matchers.notNullValue())))
                .andExpect(jsonPath("$[0].empID", is(101)))
                .andExpect(jsonPath("$[0].empName", is("Shivaji")))
                .andExpect(status().is2xxSuccessful());
    }

    @Test
    void addEmployeeDetails() throws Exception {
        Employee objEmployee = (Employee) TestTraits.getObjfromJson("json-data/EmployeeObject.json");

        String jsonRequestString = mapper.writeValueAsString(objEmployee);

        when(employeeServiceImpl.saveEmployee(any())).thenReturn("101");

        ResultActions objResultActions = this.mockMvc.perform(post("/empmgmt/employees")
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonRequestString))
                .andDo(print())
                .andExpect(jsonPath("$.empID", is(Matchers.notNullValue())))
                .andExpect(jsonPath("$.empID", is(101)))
                .andExpect(jsonPath("$.empName", is("Mani")))
                .andExpect(status().isCreated());
    }

    private Employee getEmployee(){
        Employee objEmployee = new Employee();

        objEmployee.setEmpID(Long.valueOf(101));
        objEmployee.setEmpAge("35");
        objEmployee.setEmpName("Shivaji");

        return objEmployee;
    }
}

TestTraits.java

package com.mugil.org;

import com.google.gson.Gson;
import com.mugil.org.models.Employee;
import org.springframework.core.io.ClassPathResource;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public interface TestTraits {
    public static Object getObjfromJson(String fileLoctaion) throws IOException {
        Gson gson = new Gson();
        Employee objEmployee2 = gson.fromJson(getReader(fileLoctaion), Employee.class);
        return objEmployee2;
    }

    public static Reader getReader(String fileLoctaion) throws IOException {
        return new FileReader(new ClassPathResource(fileLoctaion).getFile());
    }
}

EmployeeObject.json

{
  "empID": 101,
  "empName": "Mani",
  "empAge": "35"
}

EmployeeControllerTest.java
Read JSON Value as from File and Convert to Employee Object and to JSON String

JSON Value from File -> Employee Object -> JSON String from Object

  @Test
    void addEmployeeDetails() throws Exception {
        
        //Read values from JSON  
        Employee objEmployee = (Employee) TestTraits.getObjfromJson("json-data/EmployeeObject.json");

        //Convert Object to JSON String
        String jsonRequestString = mapper.writeValueAsString(objEmployee);

        when(employeeServiceImpl.saveEmployee(any())).thenReturn("101");

        ResultActions objResultActions = this.mockMvc.perform(post("/empmgmt/employees")
                .contentType(MediaType.APPLICATION_JSON)
                .content(jsonRequestString))
                .andDo(print())
                .andExpect(jsonPath("$.empID", is(Matchers.notNullValue())))
                .andExpect(jsonPath("$.empID", is(101)))
                .andExpect(jsonPath("$.empName", is("Mani")))
                .andExpect(status().isCreated());
    }

EmployeeControllerTest.java
Read JSON Value as from File and Convert to Employee Object and to JSON String

JSON Value from File -> Employee Object for Mocking Service Call


@Test
    void getEmployeeDetails() throws Exception {
          
        //Object Construction by reading file
        Employee objEmployee = (Employee) TestTraits.getObjfromJson("json-data/EmployeeObject.json");

        //Making object as Optional
        Optional<Employee> objEmp = Optional.ofNullable(objEmployee);

        //Mocked service response from object created
        when(employeeServiceImpl.findEmployee(EMP_ID)).thenReturn(objEmp);

        ResultActions objResultActions = this.mockMvc.perform(get("/empmgmt/employees/{empID}",EMP_ID))
                                                    .andDo(print())
                                                    .andExpect(jsonPath("$.empID", is(101)))
                                                    .andExpect(jsonPath("$.empName", is("Mani")))
                                                    .andExpect(jsonPath("$._links.all-employees.href", is("http://localhost/empmgmt/employees")))
                                                    .andExpect(status().isOk());
    }

Repo Link
https://bitbucket.org/Mugil/restwithspringboot/branch/feature/7MockMVCTesting_3

} Below is a static util class that can be used to check the list of beans available in the application context

ExampleConfigurationTest.java

package com.mugil.org.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext appContext)
            throws BeansException {
        ctx = appContext;
    }

    public static ApplicationContext getApplicationContext() {
        return ctx;
    }
}

SomeRandomClass.java

class SomeRandomClass
{
    @Autowired
    SpringUtils springUtils;

    
    public void getBeans() 
    {  
        String[] beans = springUtils.getApplicationContext().getBeanDefinitionNames();
        Arrays.sort(beans);
        for (String bean : beans)
        {
            System.out.println(bean + " of Type :: " + springUtils.getApplicationContext().getBean(bean).getClass());
        }
}
}

Output

DBConfig of Type :: class com.mugil.org.configs.DBConfig$$EnhancerBySpringCGLIB$$d32ee4ac
dbProperties of Type :: class com.mugil.org.configs.DBProperties
spring.datasource-com.mugil.org.configs.DBProperties of Type :: class com.mugil.org.configs.DBProperties
springUtils of Type :: class com.mugil.org.utils.SpringUtils

In case if you don’t want to create a separate class and wanted to access beans in the context you can implement the ApplicationContextAware interface and specify the for loop in overridden setApplicationContext method.

How to load a single class into ApplicationContext

.
.
ApplicationContextRunner context = new ApplicationContextRunner().withUserConfiguration(ExampleConfiguration.class);
.
.

What @Springbootapplication does?

@SpringBootApplication = @Configuration + @ComponentScan + @EnableAutoConfiguration

  1. @Configuration to enable Java-based configuration
  2. @ComponentScan – This annotation enables component-scanning so that the web controller classes and other components you create will be automatically discovered and registered as beans in Spring’s Application Context. All the@Controller classes you write are discovered by this annotation..
  3. @EnableAutoConfiguration annotation tells Spring Boot to “guess” how you will want to configure Spring, based on the jar dependencies that you have added. For example, If HSQLDB is on your classpath, and you have not manually configured any database connection beans, then Spring will auto-configure an in-memory database.For example, if you add spring-boot-starter-web dependency in your classpath, it automatically configures Tomcat and Spring MVC.
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

What @springboottest annotation should do?

  1. Should check whether Java-based configuration are loaded
  2. Should do component scan and create beans.
  3. Should assign appropriate beans by resolving

@SpringBootTest annotation is used to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests. @SpringBootTest annotation works by creating the ApplicationContext used in our tests through SpringApplication.It starts the embedded server, creates a web environment and then enables @Test methods to do integration testing.

  1. Below we have two class DBConfig.java and DBProperties.java.
  2. DBConfig loads DBProperties bean. DBProperties bean gets its value from application.yml
  3. In the below integration test we are going to check whether all the classes are properly getting loaded into application context.
  4. Classes loaded using @SpringBootTest would be available only in the context of the class in which it is used. Just because you add the list of classes in TestConfigApplicationTests.java it wont be available in other test classes(DatasourceConfigTest.java, ExampleConfigurationTest.java)

DBProperties.java

package com.mugil.org.configs;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.List;

@ConfigurationProperties(prefix = "spring.datasource")
public class DBProperties {   
    private String userName;
    private String password;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

DBConfig.java

@Configuration
@EnableConfigurationProperties(DBProperties.class) // Tells from which class is needed for creation of Beans
public class DBConfig {

    @Bean
    public DBProperties dbProperties(){
        DBProperties objDBProperties = new DBProperties();
        return objDBProperties;
    }
}

application.yml

spring:
  profiles:
    active: dev
---
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    password: root
    url: jdbc:mysql://localhost:3306/db
    username: root
  servers:
    - www.abc.test.com
    - www.xyz.test.com

---
spring:
  config:
    activate:
      on-profile: production
  datasource:
    driver-class-name: org.h2.Driver
    password: sa
    url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
    username: sa
  servers:
    - www.abc.com
    - www.xyz.com

TestConfigApplication.java

package com.mugil.org;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestConfigApplication {
	public static void main(String[] args) {
		SpringApplication.run(TestConfigApplication.class, args);
	}
}

@SpringBootTest loads the DBConfig.java and creates DBProperties bean by reading values from application.yml file. The SpringBootTest ensures that all loading of beans defined in configuration file and all the values needed from application.yml and application.properties are available without any issue.

In case if you want to have any class to be autowired in @SpringBootTest class, the same should be defined in @SpringBootTest(classes = DBConfig.class) annotation.

The below @SpringBootTest takes classes which should be available by the time the application is bootstrapped. So we are expecting the properties to be read from application.yml and the bean gets created. Incase there is a problem the test case would fail below.

TestConfigApplication.java

package com.mugil.org;

import com.mugil.org.configs.DBConfig;
import com.mugil.org.configs.DBProperties;
import com.mugil.org.configs.ExampleConfiguration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = {DBConfig.class})
class TestConfigApplicationTests {
	@Autowired
	DBProperties dbProperties;

	@Test
	void contextLoads() {
		System.out.println(dbProperties.getUserName());
	}
}

Below is a test which check if the value are loaded from yaml and is not null.
DatasourceConfigTest.java

package com.mugil.org.configs;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertNotNull;

@SpringBootTest(classes = DBConfig.class)
class DatasourceConfigTest{

    //This is same as using context.run method where the beans would be initialized
    //context.run method in our case is invoked because of @SpringBootTest defined class
    @Autowired
    DBProperties dbProperties;

    @Test
    public void dbConfigTest() throws Exception {
        final String name = dbProperties.getUserName();

        //We can check whether the property is getting loaded and its not null only not null
        assertNotNull(name);
    }
}
  1. The @SpringBootTest annotation tells Spring Boot to look for a main configuration class (one with @SpringBootApplication , for instance) and use that to start a Spring application context.
  2. Spring Boot Integration Testing is running an application in ApplicationContext and run tests.spring-boot-starter-test which will internally use spring-test and other dependent libraries
  3. @SpringBootTest annotation works by creating the ApplicationContext used in our tests through SpringApplication.It starts the embedded server, creates a web environment and then enables @Test methods to do integration testing.

ExampleConfiguration.java

package com.mugil.org.configs;

import com.mugil.org.services.ExampleService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ExampleConfiguration {

    @Bean
    ExampleService exampleService() {
        ExampleService exampleService = new ExampleService();
        return exampleService;
    }
}

Below is the testing of the same functionality in a different way. Instead of using @SpringBootTest we are loading the class with help of ApplicationContextRunner and checking whether the loaded class – ExampleConfiguration.java dependencies – ExampleService.java in turn gets loaded

How to check if bean from particular configuration is loaded
ExampleConfigurationTest.java

package com.mugil.org.configs;

import com.mugil.org.services.ExampleService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

import static org.assertj.core.api.Assertions.assertThat;

class ExampleConfigurationTest {

    /*
     * I setup a context runner with the class ExampleConfiguration
     * in it. For that, I use ApplicationContextRunner#withUserConfiguration()
     * methods to populate the context.
     */
    ApplicationContextRunner context = new ApplicationContextRunner()
            .withUserConfiguration(ExampleConfiguration.class);

    @Test
    public void should_check_presence_of_example_service() {
        /*
         * We start the context, and we will be able to trigger
         * assertions in a lambda receiving a
         * AssertableApplicationContext
         */

        context.run(it -> {
            /*
             * I can use assertThat to assert on the context
             * and check if the @Bean configured is present
             * (and unique)
             */
            assertThat(it).hasSingleBean(ExampleService.class);
        });
    }
}

new ApplicationContextRunner() creates a new context and creates the bean by loading the configuration

For further reference check the code in the Link

Below is a static util class which can be used to check the list of beans available in the application context

ExampleConfigurationTest.java

package com.mugil.org.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext appContext)
            throws BeansException {
        ctx = appContext;
    }

    public static ApplicationContext getApplicationContext() {
        return ctx;
    }
}

TestTraits.java

public interface TestTraits {
    String EMPLOYEE_ID = "1";
    Long EMPLOYEE_ID_LONG = 1L;

    default EmployeeRpy getEmployee(){
        EmployeeRpy objEmpRpy = new EmployeeRpy();
        objEmpRpy.setEmpName("Mugil");
        objEmpRpy.setEmpAge("34");
        return objEmpRpy;

     default EmployeeDetailsResource createMockedEmployeeDetails() 
          throws IOException(){
       EmployeeDetailsResource objEmployeeDetailsResource = 
              new Gson.fromJson(getReader("json-data/EmployeeDetails.json"), EmployeeDetails.class);    
       return objEmployeeDetailsResource; 
     } 

     default Reader getReader(String filePath) throws IOException{
       return new FileReader(new ClassPathResource(filePath).getFile());  
     }
}

EmployeeDetails.json

  {
     "empID" : "101",
     "empName" : "Mugilvannan",
     "empAge" : "30"
  } 

EmployeeMgmtTest.java

 @ExtendWith({MockitoExtension.class})
 class EmployeeMgmtTest implements TestTraits{
    .
    . 
    Code for Testing Goes Here
    .
    .
 }

How to Pass Interface or Abstract Class as Matcher?
Person.java

public interface Person {
}

StudentUtils.java

 public String methodWithInterfaceArg(Person person)
    {
        //This method takes Interface Implementation as Parameter
        Somemethod(person);
        return "Test Success";
    }

    public void Somemethod(Person p){
        System.out.println("Hello There");
    }

StudentUtilsTest.java

@Test
    @DisplayName("Test for method which takes interface as argument")
    void methodWithInterfaceArg() 
    {
        Person objPerson = Mockito.mock(Person.class);

        // Using spy we are creating a new mock and call the method to be tested
        StudentUtils studentUtils= Mockito.spy(StudentUtils.class);

        doNothing().when(studentUtils).Somemethod(objPerson);

        // While testing we should call the method to be tested using new spyObject rather than systemUnderTest
        // This is because only in the Spy object studentUtils we have created a mock definition for Somemethod
        // Hence when methodWithInterfaceArg is called using systemUnderTest the mock  definition created using spy would be lost
        assertEquals("Test Success", studentUtils.methodWithInterfaceArg(objPerson));
    }

How to doNothing when internal method which returns void is gets called?
StudentUtils.java

    public String methodWithInterfaceArg(Person person)
    {
        //This method has void return type
        Somemethod(person);
        return "Test Success";
    }

    public void Somemethod(Person p){
        System.out.println("Hello There");
    }

StudentUtilsTest.java

    @Test
    @DisplayName("Test for internal method which is of void return type")
    void methodWithInterfaceArg() {
        Person objPerson = Mockito.mock(Person.class);

        // Using spy we are creating a new mock and call the method to be tested
        StudentUtils studentUtils= Mockito.spy(StudentUtils.class);

        //With Object Created using spy we can mock the actual method to do nothing
        doNothing().when(studentUtils).Somemethod(objPerson);

        assertEquals("Test Success", studentUtils.methodWithInterfaceArg(objPerson));
    }

How to doNothing when method which returns value is called?
StudentUtils.java

public class StudentUtils {
    private List arrStudents = new ArrayList();
    private String Name = "Mugil";

    public String dummymethod(Student objStudent)
    {
        System.out.println("Dummy Method Called");
        return "Dummy Method";
    }

    public String methodCallingAnotherMethodReturningVal()
    {
        String something = dummymethod(new Student("102" ,"abc"));
        return "Dummy Method returned "+ something;
    }
}

StudentUtilsTest.java

 @Test
    @DisplayName("Test for Method calling another method which returns value")
    void methodCallingAnotherMethodReturningVal() {
        StudentUtils studentUtils= Mockito.spy(StudentUtils.class);

        Mockito.when(studentUtils.dummymethod(any())).thenReturn("Test Student");
        Assert.assertEquals("Dummy Method returned Test Student", studentUtils.methodCallingAnotherMethodReturningVal());
    }

How to mock methods of private and public instance class variables?

  1. The below scenario we are going to mock instance of method which belongs to other class initialized over by public and private class variables
  2. We Create a new mock and initialize using reflectionUtils for the objDBConnectionFactory which is private whereas we use a spy to directly change the method definition of objDBConnectionFactory2

DBConnectionFactory.java

package com.mugil.org;

public class DBConnectionFactory {
    public DBConnectionFactory() {
    }

    public <T> T  readDetails(byte[] src, Class<People> valueType){
        return (T)new Student();
    }

    public <T> T  readDetails2(String src){
        return (T)new Student();
    }
}

MockPrivateFileds.java

package com.mugil.org;

import org.springframework.web.client.HttpStatusCodeException;

public class MockPrivateFileds {
    private DBConnectionFactory objDBConnectionFactory = new DBConnectionFactory();

    public DBConnectionFactory objDBConnectionFactory2 = new DBConnectionFactory();

    private Student getDetails(HttpStatusCodeException httpStatusCodeException) {
        return objDBConnectionFactory.readDetails(httpStatusCodeException.getResponseBodyAsByteArray(), People.class);
    }

    public Student getDetails2(HttpStatusCodeException httpStatusCodeException) {
        return objDBConnectionFactory2.readDetails2("String");
    }
}

MockPrivateFiledsTest.java

package com.mugil.org;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.HttpStatusCodeException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.mockito.ArgumentMatchers.*;

@ExtendWith({MockitoExtension.class})
class MockPrivateFiledsTest {
    @InjectMocks
    MockPrivateFileds systemUnderTest;

    @Test
    void getDetailsTest() {
        HttpStatusCodeException httpStatusCodeException = Mockito.mock(HttpStatusCodeException.class);

        /*The Below two lines of code are needed because the objDBConnectionFactory created doesnot contain any definition of methods
        * So the definition could be set by creating new mock object as below. If there is Setter we can use setter to set the newly created mock object
        * Since there is no setter and its private we are going to use reflectionUtils */
        DBConnectionFactory dbConnectionFactory = Mockito.mock(DBConnectionFactory.class);
        ReflectionTestUtils.setField(systemUnderTest, "objDBConnectionFactory", dbConnectionFactory);
        Mockito.when(dbConnectionFactory.readDetails(nullable(byte[].class), eq(People.class))).thenReturn(new Student());

        assertThat(ReflectionTestUtils.invokeMethod(systemUnderTest, "getDetails", httpStatusCodeException), instanceOf(Student.class));
    }

    @Test
    void getDetailsTest2() {
        HttpStatusCodeException httpStatusCodeException = Mockito.mock(HttpStatusCodeException.class);
        systemUnderTest = Mockito.spy(MockPrivateFileds.class);

        DBConnectionFactory dbConnectionFactory2 = Mockito.spy(DBConnectionFactory.class);
        ReflectionTestUtils.setField(systemUnderTest, "objDBConnectionFactory2", dbConnectionFactory2);

        /*Below is a mocking of a public method through its instance by using spy. The below case is similar to above
        * scenario with the only difference we can use spy to gain access to readDetails2 */
        Mockito.when(systemUnderTest.objDBConnectionFactory2.readDetails2(anyString())).thenReturn(new Student());

        assertThat(systemUnderTest.getDetails2(httpStatusCodeException), instanceOf(Student.class));
    }
}

Below is a class StudentUtils which contains only two Method – doNothingWhenCalled and dummymethod. We are going to unit test doNothingWhenCalled method.

The conclusion would be to use @Spy when we mock methods of same class tested to doNothing and use @Mock to mock method to doNothing call to other classes.

StudentUtils.java

package com.mugil.org;

import java.util.ArrayList;
import java.util.List;

public class StudentUtils {
    private List arrStudents = new ArrayList();
    private String Name = "Mugil";

    public StudentUtils(){
        arrStudents.add(new Student("101", "Mugil"));
    }

    public Student doNothingWhenCalled()
    {
        dummymethod(new Student("102" ,"abc"));
        return new Student("103", "StudentName");
    }

    public String dummymethod(Student objStudent)
    {
        System.out.println("Dummy Method Called");
        return "Dummy Method";
    }

@InjectMocks
StudentUtilsTest.java

@ExtendWith({MockitoExtension.class})
class StudentUtilsTest {
    @InjectMocks
    StudentUtils systemUnderTest;

    @Test
    void doNothingCalled() {
        Assertions.assertThat(systemUnderTest.doNothingWhenCalled()).isInstanceOf(Student.class);
    }
}

Output

Dummy Method Called

@Mocks
StudentUtilsTest.java

@ExtendWith({MockitoExtension.class})
class StudentUtilsTest {
    @InjectMocks
    StudentUtils systemUnderTest;

    @Test
    void doNothingCalled() {
        systemUnderTest = Mockito.mock(StudentUtils.class);
        Mockito.when(systemUnderTest.dummymethod(any())).thenReturn("Dummy Method");

        Assertions.assertThat(systemUnderTest.doNothingWhenCalled()).isInstanceOf(Student.class);
    }
}

You can see below we are trying to call method to be tested under empty mock instance which doesnot have default method definition. There is no point in mocking method definition since systemUnderTest.doNothingWhenCalled() would try to invoke test method which has no idea about.
Output

java.lang.AssertionError: 
Expecting actual not to be null

@Spy
StudentUtilsTest.java

@ExtendWith({MockitoExtension.class})
class StudentUtilsTest {
    @InjectMocks
    StudentUtils systemUnderTest;

    @Test
    void doNothingCalled() {
        systemUnderTest = Mockito.spy(StudentUtils.class);
        Assertions.assertThat(systemUnderTest.doNothingWhenCalled()).isInstanceOf(Student.class);
    }
}

Output

Dummy Method Called

Below is the Debug of Object created using @Mock, @InjectMock and @Spy.As you can see the one with Mock contains only MockInterceptor which does not have definition of methods on its own where as @InjectMock created instance of TestClass. @Spy contains both MockInterceptor and Instance enabling partial mock.

While Using InjectMock you can see the MockitoInterceptor in scope

While Using InjectMock you can see the class variables getting initialized

While Using Spy you can see both class variables getting initialized and MockitoInterceptor in scope

Below we have class – ClassA which contains the actual method – MethodA to be tested. MethodA has call to two dependent
ClassA.java

package com.mugil.org;

public class ClassA {
    public ClassADep1 dependency1;
    public ClassADep2 dependency2;

    public ClassA(ClassADep1 dependency1, ClassADep2 dependency2) {
        this.dependency1 = dependency1;
        this.dependency2 = dependency2;
    }

    public boolean MethodA(){
        System.out.println("I am going to call Dep1Method");
        dependency1.Dep1Method();

        System.out.println("I am going to call Dep2Method");
        dependency2.Dep2Method();

        return true;
    }
}

ClassADep1.java

package com.mugil.org;

public class ClassADep1 {
    public void Dep1Method(){
        System.out.println("This is ClassADep1 Method getting called");
    }
}

ClassADep2.java

package com.mugil.org;

public class ClassADep2 {
    public void Dep2Method(){
        System.out.println("This is ClassADep2 Method getting called");
    }
}

Use @Mock when you want to just test the functionality externally without actually calling that method.If you want to be safe and avoid calling external services and just want to test the logic inside of the unit, then use mock.

Using @Mock annotation for Dependency
ClassATest.java

package com.mugil.org;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.*;

@ExtendWith({MockitoExtension.class})
class ClassATest {
    @InjectMocks
    ClassA classA;

    @Mock
    ClassADep1 dependency1;

    @Mock
    ClassADep2 dependency2;

    @Test
    void methodA() {
        assertEquals(true, classA.MethodA());
    }
}

Output

I am going to call Dep1Method
I am going to call Dep2Method

Use @Spy when you want to test the functionality externally + internally with the very method being called. If you want to call an external service and perform calling of real dependencies, or simply say, you want to run the program as it is and just stub specific methods, then use spy.

Using @Spy annotation for Dependency
ClassATest.java

package com.mugil.org;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;

@ExtendWith({MockitoExtension.class})
class ClassATest {
    @InjectMocks
    ClassA classA;

    @Spy
    ClassADep1 dependency1;

    @Spy
    ClassADep2 dependency2;

    @Test
    void methodA() {
        assertEquals(true, classA.MethodA());
    }
}

Output

I am going to call Dep1Method
This is ClassADep1 Method getting called
I am going to call Dep2Method
This is ClassADep2 Method getting called

Below we have a Student class with Private Class Variable(id and name), Service and Private Methods.

Student.java

public class Student {
    private Integer id;
    private String name;

    @Autowired
    private StudentService studentService;

    private String getStudentDetails(){
      return "id: " + getId() + "; name: " + getName();
    }  
}

Mocking a Private Field
we cannot access the private field id to assign a value for testing, because there isn’t a public setter method for it.
We can then use ReflectionTestUtils.setField method to assign a value to the private member id

StudentTest.java

@Test
public void setValueWithNoSetter() {
    Student student = new Student();
    ReflectionTestUtils.setField(student, "id", 101); 
    assertTrue(student.getId().equals(101));
}

Mocking a Private Method
In a similar way we can invoke private method – getStudentDetails() as below in student class
StudentTest.java

@Test
public void whenNonPublicMethod_thenReflectionTestUtilsInvokeMethod() {
    Student student= new Student ();
    ReflectionTestUtils.setField(student, "id", 101);
    ReflectionTestUtils.setField(student, "name", "Mugil");
 
    assertTrue(ReflectionTestUtils.invokeMethod(student, "employeeToString").equals
                 ("id: 101; name: Mugil"));
}

Mocking a Private Dependencies
StudentTest.java

@Test
public void whenNonPublicMethod_thenReflectionTestUtilsInvokeMethod() 
{
   Student student = new Student();
   StudentService studService = mock(StudentService.class);
   when(studService.getStudentStatus(student.getId())).thenReturn("Active");

   ReflectionTestUtils.setField(student, "studentService", studService);

.
.
}
  1. What is Difference between @Injectmock and @Mock?
    @Mock creates a mock. @InjectMocks creates an instance of the class and injects the mocks that are created with the @Mock (or @Spy) annotations into this instance.
    Note you must use @RunWith(MockitoJUnitRunner.class) to initialize these mocks and inject them (JUnit 4).With JUnit 5, you must use @ExtendWith(MockitoExtension.class).

    @RunWith(MockitoJUnitRunner.class) // JUnit 4
    // @ExtendWith(MockitoExtension.class) for JUnit 5
    public class SomeManagerTest {
    
        @InjectMocks
        private SomeManager someManager;
    
        @Mock
        private SomeDependency someDependency; // this will be injected into someManager
     
         //tests...
    }
    

  2. When should I use spy vs Mock?
    Use @Mock when you want to just test the functionality externally without actually calling that method. Use @Spy when you want to test the functionality externally + internally with the very method being called. Spy is a combination of Mock(methodInterceptors) and InjectMock(Instance of class Tested).
  3. What happens when unstubbed method called?
    Mockito will return null or primitive value or empty collection
  4. Does mockito when accepts combination of specific and generic matchers?
    No. Mockito matchers should be either generic or specific.
  5. What is BDD?
    Behaviour Driven Development (Given Scenario -> When the action happens -> Then this should be the case)

    Given 3 Items in Cart ->  When One Item gets Deleted -> Then 2 Items would be in Cart
    
  6. How to Verify whether the Service or method call has been made?
    Verify is used to check whether the call to the method is done or not.

    .
    .
     Mockito.verify(employeeDB, times(1)).getEmployeeByIdFromDB("101");
     Mockito.verify(employeeDB, never()).getEmployeesFromDB();
     Mockito.verify(employeeDB, atleast(2)).getEmployeeByIdFromDB("101");
    .
    .
    
  7. How to check the arguments passed to the method?
    ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
    verify(mock).doSomething(argument.capture());
    assertEquals("John", argument.getValue().getName());
    

    (or)

    //capturing varargs:
    ArgumentCaptor<Person> varArgs = ArgumentCaptor.forClass(Person.class);
    verify(mock).varArgMethod(varArgs.capture());
    List expected = asList(new Person("John"), new Person("Jane"));
    assertEquals(expected, varArgs.getAllValues());
    
  8. @SpringBootTest vs @runwith(springrunner.class) or @ExtendWith
    Enables spring boot features like @Autowire, @MockBean etc.. during junit testing. This is used to provide a bridge between Spring Boot test features and JUnit. Whenever we are using any Spring Boot testing features in our JUnit tests, this annotation will be required.@RunWith is an old annotation from JUnit 4 to use test runners. If you’re using JUnit 5 (Jupiter), you should use @ExtendWith to use JUnit extensions

    @SpringBootTest : This annotation is used to load complete application context for end to end integration testing.The @SpringBootTest annotation can be used when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests. So this would be the annotation used while testing bootstrap class of Spring Boot Application. load application.properties and give me all the Spring Boot goodness

    We pass list of classes as parameter to @SpringBootTest whose configs are loaded from application.properties when the application is bootstrapped.

    RestWithSpringBootApplicationTests.java

    @SpringBootTest(classes = {RestWithSpringBootApplication.class, EmployeeMgmtConfig.class})
    class RestWithSpringBootApplicationTests {
      @Test
      void contextLoads() {
      }
    }
    
  9. How Methods are mocked Internally?
    Mockito works by storing and retrieving method invocation details on mocks using method interception
    When we invoke the when() method we are in fact recalling the last registered method call from that context, In our case it is Dep2Method() is saved and returned.The recorded behaviour is played back when our test invokes the mocked method again which triggers the interceptor to recall the return value we provided.In Mockito source code, we see when method does not use the parameter methodCall. But it tries to get the OngoingStubbing from an instance of MockingProgress. This OngoingStubbing is nothing but mocking context above.

     @Test
        void doIt() {
            ClassADep1.Dep2Method();
            Mockito.when("Some Blah Blah").thenReturn("Mocked!");
            assertEquals(objTarget.doIt("Sample"), "Mocked!");
        }
    
  10. What is the difference between @ExtendWith(SpringExtension.class) and @ExtendWith(MockitoExtension.class)?
    If you want to use Spring test framework features in your tests like for example @MockBean, then you have to use @ExtendWith(SpringExtension.class). It replaces the deprecated JUnit4 @RunWith(SpringJUnit4ClassRunner.class)

    When NOT involving Spring – If you just want to involve Mockito and don’t have to involve Spring, for example, when you just want to use the @Mock / @InjectMocks annotations, then you want to use @ExtendWith(MockitoExtension.class), as it doesn’t load in a bunch of unneeded Spring stuff. It replaces the deprecated JUnit4 @RunWith(MockitoJUnitRunner.class).

  11. When to use @Extendwith and @Runwith?
    If you are using Junit version < 5, so you have to use @RunWith(SpringRunner.class) or @RunWith(MockitoJUnitRunner.class) etc.If you are using Junit version = 5, so you have to use @ExtendWith(SpringExtension.class) or @ExtendWith(MockitoExtension.class) Junit4 uses *Runner.class in annotation with @RunWith

    SpringRunner.class and MockitoJunitRunner.class for Junit4
    SpringExtension.class and MockitoExtension.class for Junit5