Monday, 7 October 2019

Spring boot: Write unit tests for controller layer


Step 1: Create a test class and annotate it with @RunWith and @WebMvcTest annotations.
@RunWith(SpringJUnit4ClassRunner.class)
@WebMvcTest(EmployeeController.class)
public class EmployeeControllerUnitTest {

    ......
    ......

}


Step 2: Inject MockMvc object.
@Autowired
private MockMvc mockMvc;

Step 3: Mock the service layer beans.
@MockBean
private EmployeeService empService;

Step 4: Specify to junit that you want to inject the mocked beans to contoller object.
@InjectMocks
private EmployeeController employeeController;

Step 5: Initialie all mock objects before starting the test case.
@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

Step 6: Prepare mocked responses in service layer.
Employee emp1 = new Employee();
emp1.setFirstName("Anil");
emp1.setLastName("Kumar");
emp1.setId(1);

when(empService.save(any(Employee.class))).thenReturn(emp1);

Step 7: Call controller APIs using mockMvc object.


Below snippet test the creation of new employee.
String payload = "{\"firstName\" : \"Anil\", \"lastName\" : \"Kumnar\", \"id\" : 1}";
MvcResult mvcResult = mockMvc.perform(post("/api/v1/employees").content(payload).contentType(MediaType.APPLICATION_JSON)).andReturn();

assertEquals(201, mvcResult.getResponse().getStatus());


Step 8: You should specify from where to scan the components using below statement.
@Configuration
@ComponentScan(basePackageClasses = { EmployeeController.class })
public static class TestConfiguration {
}


Total test class looks like below.
@RunWith(SpringJUnit4ClassRunner.class)
@WebMvcTest(EmployeeController.class)
public class EmployeeControllerUnitTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private EmployeeService empService;

    @InjectMocks
    private EmployeeController employeeController;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void newEmployee() throws Exception {
        Employee emp1 = new Employee();
        emp1.setFirstName("Anil");
        emp1.setLastName("Kumar");
        emp1.setId(1);

        when(empService.save(any(Employee.class))).thenReturn(emp1);

        String payload = "{\"firstName\" : \"Anil\", \"lastName\" : \"Kumnar\", \"id\" : 1}";

        MvcResult mvcResult = mockMvc
                .perform(post("/api/v1/employees").content(payload).contentType(MediaType.APPLICATION_JSON))
                .andReturn();

        assertEquals(201, mvcResult.getResponse().getStatus());
        System.out.println(mvcResult.getResponse().getContentAsString());
        // assertEquals("{\"id\":1,\"firstName\":\"Anil\",\"lastName\":\"Kumar\"}",
        // mvcResult.getResponse().getContentAsString());

    }

    @Configuration
    @ComponentScan(basePackageClasses = { EmployeeController.class })
    public static class TestConfiguration {
    }
}

Find the below working application.


Employee.java
package com.sample.app.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "employees")
public class Employee {

 @Id
 @GeneratedValue
 @Column(name = "employee_name")
 private Integer id;

 @Column(name = "first_name")
 private String firstName;

 @Column(name = "last_name")
 private String lastName;

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

}


EmployeeDao.java
package com.sample.app.dao;

import java.util.List;

import com.sample.app.model.Employee;

public interface EmployeeDao {

 public Employee save(Employee emp);
 
 public List<Employee> all();
}


EmployeeDaoImpl.java
package com.sample.app.dao.impl;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.sample.app.dao.EmployeeDao;
import com.sample.app.model.Employee;

@Repository
public class EmployeeDaoImpl implements EmployeeDao {
 @PersistenceContext
 private EntityManager entityManager;

 @Override
 @Transactional
 public Employee save(Employee emp) {
  entityManager.persist(emp);
  entityManager.flush();
  int id = emp.getId();

  return entityManager.find(Employee.class, id);

 }

 @Override
 public List<Employee> all() {
  Query query = entityManager.createQuery("select e from Employee e", Employee.class);
  return query.getResultList();
 }

}


EmployeeService.java
package com.sample.app.service;

import java.util.List;

import com.sample.app.model.Employee;

public interface EmployeeService {
 public Employee save(Employee emp);

 public List<Employee> all();
}


EmployeeServiceImpl.java
package com.sample.app.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sample.app.dao.EmployeeDao;
import com.sample.app.model.Employee;
import com.sample.app.service.EmployeeService;

@Service
public class EmployeeServiceImpl implements EmployeeService {

 @Autowired
 EmployeeDao empDao;

 @Override
 public Employee save(Employee emp) {
  return empDao.save(emp);
 }

 @Override
 public List<Employee> all() {
  return empDao.all();
 }

}


HomeController.java
package com.sample.app.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {
 @RequestMapping("/")
 public String homePage() {
  return "Welcome to Spring boot Application Development";
 }

}


EmployeeController.java
package com.sample.app.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.sample.app.model.Employee;
import com.sample.app.service.EmployeeService;

@RestController
@RequestMapping("api/v1/")
public class EmployeeController {

 @Autowired
 private EmployeeService empService;

 @RequestMapping(value = "employees", method = RequestMethod.GET)
 public ResponseEntity<List<Employee>> allEmployees() {
  return ResponseEntity.ok(empService.all());
 }

 @RequestMapping(value = "employees", method = RequestMethod.POST)
 public ResponseEntity<Employee> save(@RequestBody Employee emp) {
  Employee persistedEmp = empService.save(emp);
  return ResponseEntity.status(HttpStatus.CREATED).body(persistedEmp);
 }

}


App.java
package com.sample.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class App {

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

}


application.properties
logging.level.root=WARN
logging.level.org.hibernate=ERROR

## H2 specific properties
spring.h2.console.enabled=true
spring.h2.console.path=/h2

spring.datasource.url=jdbc:h2:file:~/db/myOrg.db;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;

spring.datasource.username=krishna
spring.datasource.password=password123

spring.datasource.driverClassName=org.h2.Driver

## JPA specific properties
# Creates the schema, destroying previous data.
spring.jpa.hibernate.ddl-auto=create

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

## Database connection pooling properties
# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.max-wait=10000

# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=3

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true


application-test.properties
logging.level.root=WARN
logging.level.org.hibernate=ERROR

spring.datasource.url=jdbc:h2:file:~/db/appTest.db;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;


pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>springTest</groupId>
 <artifactId>springTest</artifactId>
 <version>1</version>


 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.6.RELEASE</version>
 </parent>

 <name>springbootApp</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>


  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>

 </dependencies>
</project>

Test classes are given below.


EmployeeServiceUnitTest.java
package com.sample.app.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

import com.sample.app.dao.impl.EmployeeDaoImpl;
import com.sample.app.model.Employee;
import com.sample.app.service.impl.EmployeeServiceImpl;

@RunWith(MockitoJUnitRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class EmployeeServiceUnitTest {

 @Mock
 private EmployeeDaoImpl empDao;

 @InjectMocks
 private EmployeeServiceImpl empService;

 @Before
 public void setup() {
  MockitoAnnotations.initMocks(this);
 }

 @Test
 public void newEmployee() {
  Employee emp1 = new Employee();
  emp1.setId(1);
  emp1.setFirstName("Anil");
  emp1.setLastName("Kumar");

  when(empDao.save(any(Employee.class))).thenReturn(emp1);

  Employee persistedEmployee = empService.save(emp1);

  assertNotNull(persistedEmployee);
  assertNotNull(persistedEmployee.getId());
  assertEquals(emp1.getFirstName(), persistedEmployee.getFirstName());
  assertEquals(emp1.getLastName(), persistedEmployee.getLastName());
 }
}


EmployeeServiceIntegrationTest.java
package com.sample.app.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.sample.app.model.Employee;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@ActiveProfiles("test")
public class EmployeeServiceIntegrationTest {
 
 @Autowired
 EmployeeService empService;
 
 @Test
 public void newEmployee() {
  Employee emp1 = new Employee();
  emp1.setFirstName("Anil");
  emp1.setLastName("Kumar");
  
  Employee persistedEmployee = empService.save(emp1);
  
  assertNotNull(persistedEmployee);
  assertNotNull(persistedEmployee.getId());
  assertEquals(emp1.getFirstName(), persistedEmployee.getFirstName());
  assertEquals(emp1.getLastName(), persistedEmployee.getLastName());
 }

}


EmployeeControllerIntegrationTest.java
package com.sample.app.controller;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.sample.app.model.Employee;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class EmployeeControllerIntegrationTest {

 @Autowired
 private EmployeeController empController;

 @Test
 public void newEmployee() {
  Employee emp1 = new Employee();
  emp1.setFirstName("Anil");
  emp1.setLastName("Kumar");

  ResponseEntity<Employee> response = empController.save(emp1);

  Employee persistedEmployee = response.getBody();

  assertNotNull(persistedEmployee);
  assertNotNull(persistedEmployee.getId());
  assertEquals(emp1.getFirstName(), persistedEmployee.getFirstName());
  assertEquals(emp1.getLastName(), persistedEmployee.getLastName());
 }

}


EmployeeControllerUnitTest.java

package com.sample.app.controller;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
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.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import com.sample.app.model.Employee;
import com.sample.app.service.EmployeeService;

@RunWith(SpringJUnit4ClassRunner.class)
@WebMvcTest(EmployeeController.class)
public class EmployeeControllerUnitTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private EmployeeService empService;

    @InjectMocks
    private EmployeeController employeeController;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void newEmployee() throws Exception {
        Employee emp1 = new Employee();
        emp1.setFirstName("Anil");
        emp1.setLastName("Kumar");
        emp1.setId(1);

        when(empService.save(any(Employee.class))).thenReturn(emp1);

        String payload = "{\"firstName\" : \"Anil\", \"lastName\" : \"Kumnar\", \"id\" : 1}";

        MvcResult mvcResult = mockMvc
                .perform(post("/api/v1/employees").content(payload).contentType(MediaType.APPLICATION_JSON))
                .andReturn();

        assertEquals(201, mvcResult.getResponse().getStatus());
        System.out.println(mvcResult.getResponse().getContentAsString());
        // assertEquals("{\"id\":1,\"firstName\":\"Anil\",\"lastName\":\"Kumar\"}",
        // mvcResult.getResponse().getContentAsString());

    }

    @Configuration
    @ComponentScan(basePackageClasses = { EmployeeController.class })
    public static class TestConfiguration {
    }
}

Total project structure looks like below.



You can download complete working application from this link.


Previous                                                    Next                                                    Home

No comments:

Post a Comment