Spring Boot Tests for Controller, Service and Dao Layers

Learn how to test the different parts of a Spring Boot web application. We will see some very quick examples (using Junit 5) and configurations for:

  • Verifying that the application has been initialized successfully
  • Unit testing REST Controller with @WebMvcTest
  • Unit testing Service Layer with Mockito
  • Unit testing DAO Layer with @DataJpaTest and @AutoConfigureTestDatabase
  • Integration testing using @SpringBootTest
  • System testing using RestTemplate

For demo purposes, we have created a very simple Employee management application. It has a few CRUD API calls for creating, fetching and deleting the employees from the database.

Do not forget to use @ExtendWith(SpringExtension.class) to initialize and inject the mocks using Mockito in the test classes.

1. Maven

This demo application uses Spring Boot 3 and Java 17. It includes the auto-configuration from the following modules:

  • spring-boot-starter-web
  • spring-boot-starter-validation
  • spring-boot-starter-test with Junit 5
  • spring-boot-starter-data-jpa
<dependencies>

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

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

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

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

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

</dependencies>

2. Testing if Application Bootstraps Correctly

This is the simplest of all. Write a Test class annotated with @SpringBootTest and check for any important eagerly initialized bean if it has been successfully injected into an auto-wired attribute or not.

mvn test
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.howtodoinjava.employees.controllers.EmployeeController;
 
@SpringBootTest
public class EmployeesApplicationTests {
 
  @Autowired
  EmployeeController employeeController;
 
  @Test
  public void contextLoads() {
    Assertions.assertThat(employeeController).isNot(null);
  }
}

3. Unit Testing the REST @Controller

Write a Test class annotated with @WebMvcTest. We can specify which Controller we want to test in the annotation value itself.

@WebMvcTest(EmployeeController.class)
public class StandaloneControllerTests {

  @MockBean
  EmployeeService employeeService;

  @Autowired
  MockMvc mockMvc;

  @Test
  public void testfindAll() throws Exception {
    Employee employee = new Employee("Lokesh", "Gupta");
    List<Employee> employees = Arrays.asList(employee);

    Mockito.when(employeeService.findAll()).thenReturn(employees);

    mockMvc.perform(get("/employee"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$", Matchers.hasSize(1)))
        .andExpect(jsonPath("$[0].firstName", Matchers.is("Lokesh")));
  }

}

4. Unit Testing the Service Layer

To unit test the service layer, we must use mock the DAO layer. Then we can run the tests using MockitoExtension.

@ExtendWith(MockitoExtension.class)
public class ServiceTests {

  @InjectMocks
  EmployeeService service;

  @Mock
  EmployeeRepository dao;

  @BeforeEach
  public void init() {
    MockitoAnnotations.openMocks(this);
  }

  @Test
  void testFindAllEmployees() {
    List<Employee> list = new ArrayList<Employee>();
    Employee empOne = new Employee("John", "John");
    Employee empTwo = new Employee("Alex", "kolenchiski");
    Employee empThree = new Employee("Steve", "Waugh");

    list.add(empOne);
    list.add(empTwo);
    list.add(empThree);

    when(dao.findAll()).thenReturn(list);

    //test
    List<Employee> empList = service.findAll();

    assertEquals(3, empList.size());
    verify(dao, times(1)).findAll();
  }

  @Test
  void testCreateOrSaveEmployee() {
    Employee employee = new Employee("Lokesh", "Gupta");

    service.save(employee);

    verify(dao, times(1)).save(employee);
  }
}

5. Unit testing DAO / Repository Layer

To unit test the DAO layer, we first need an in-memory test database. This we can achieve using @AutoConfigureTestDatabase.

Then we need to use @DataJpaTest which disables full auto-configuration and instead apply only configuration relevant to JPA tests.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class DaoTests {

  @Autowired
  EmployeeRepository employeeRepository;

  @Test
  public void testCreateReadDelete() {
    Employee employee = new Employee("Lokesh", "Gupta");

    employeeRepository.save(employee);

    Iterable<Employee> employees = employeeRepository.findAll();
    Assertions.assertThat(employees).extracting(Employee::getFirstName).containsOnly("Lokesh");

    employeeRepository.deleteAll();
    Assertions.assertThat(employeeRepository.findAll()).isEmpty();
  }
}

6. Integration Testing

Integration tests cover the whole path through the application. In these tests, we send a request to the application and check that it responds correctly and has changed the database state according to our expectations.

The database can be an actual physical database or in-memory database for testing purposes.

import javax.validation.ValidationException;
 
import org.assertj.core.api.Assertions;
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.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
 
import com.howtodoinjava.employees.model.Employee;
 
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class IntegrationTests {
 
  @Autowired
  EmployeeController employeeController;
 
  @Test
  public void testCreateReadDelete() {
    Employee employee = new Employee("Lokesh", "Gupta");
 
    Employee employeeResult = employeeController.create(employee);
 
    Iterable<Employee> employees = employeeController.read();
    Assertions.assertThat(employees).first().hasFieldOrPropertyWithValue("firstName", "Lokesh");
 
    employeeController.delete(employeeResult.getId());
    Assertions.assertThat(employeeController.read()).isEmpty();
  }
 
  @Test
  public void errorHandlingValidationExceptionThrown() {
 
    Assertions.assertThatExceptionOfType(ValidationException.class)
        .isThrownBy(() -> employeeController.somethingIsWrong());
  }
}

7. Integration Testing using TestRestTemplate

We can use TestRestTemplate class to perform system testing. It helps in verifying the application as it looks to the client outside the application.

@SpringBootTest(webEnvironment = RANDOM_PORT)
public class SystemTests {

	@Autowired
	private EmployeeController controller;

	@LocalServerPort
	private int port;

	@Autowired
	private TestRestTemplate restTemplate;

	@Test
	public void testCreateReadDelete() {
		String url = "http://localhost:"+port+"/employee";

		Employee employee = new Employee("Lokesh", "Gupta");
		ResponseEntity<Employee> entity = restTemplate.postForEntity(url, employee, Employee.class);

		Employee[] employees = restTemplate.getForObject(url, Employee[].class);
		Assertions.assertThat(employees).extracting(Employee::getFirstName).containsOnly("Lokesh");

		restTemplate.delete(url + "/" + entity.getBody().getId());
		Assertions.assertThat(restTemplate.getForObject(url, Employee[].class)).isEmpty();
	}

	@Test
	public void testErrorHandlingReturnsBadRequest() {

		String url = "http://localhost:"+port+"/wrong";

		try {
			restTemplate.getForEntity(url, String.class);
		} catch (HttpClientErrorException e) {
			Assertions.assertThat(e.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
		}
	}

}

8. Conclusion

This Spring boot testing tutorial is to provide a short example of how to configure various dependencies; as well as write various kinds of tests using simple examples.

Feel free to modify the above-given code snippets to your requirements.

Happy Learning !!

Download Source Code

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode