Testing Spring Boot Controllers and Services
Course: Advanced Java Training
Topic: Testing Spring Boot Controllers and Services
Course Link: Advanced Java Training Course
Demo Link: Register for a Free Course Demo
Website: JustAcademy
1. Introduction to Testing Spring Boot Controllers and Services
Testing is an essential part of developing reliable Spring Boot applications. It verifies that individual components behave correctly, business rules produce expected results, HTTP endpoints return appropriate responses, and application components work together as intended.
In a typical Spring Boot application, the controller receives HTTP requests, the service implements business logic, and the repository communicates with the database. Each layer has a distinct responsibility and can be tested using different approaches.
For example, an employee management application may provide endpoints to create, retrieve, update, and delete employees. Controller tests verify HTTP status codes and response bodies, while service tests verify business rules such as salary validation, employee creation, and handling missing employee records.
2. Learning Objectives
- Understand the Spring Boot testing architecture.
- Differentiate unit tests, slice tests, and integration tests.
- Test REST controllers using MockMvc and @WebMvcTest.
- Test service classes using JUnit and Mockito.
- Mock dependencies and verify method interactions.
- Validate HTTP responses, request bodies, and exception handling.
- Test success cases, invalid input, and error scenarios.
- Configure test dependencies and execute tests using Maven.
3. Understanding the Spring Boot Application Layers
A common Spring Boot REST application separates responsibilities into multiple layers.
HTTP Client
|
v
REST Controller
|
v
Service Layer
|
v
Repository Layer
|
v
Database
| Layer |
Responsibility |
Typical Testing Approach |
| Controller |
Handles HTTP requests, input validation, and response formatting. |
@WebMvcTest, MockMvc |
| Service |
Executes business rules and coordinates application operations. |
JUnit, Mockito, @ExtendWith(MockitoExtension.class) |
| Repository |
Reads and writes application data. |
Repository slice tests or integration tests |
| Application integration |
Verifies multiple components working together. |
@SpringBootTest |
4. Types of Testing in Spring Boot
4.1 Unit Testing
Unit testing checks one component in isolation. For example, a service test can mock its repository so that the test focuses on business logic without connecting to a real database.
4.2 Web Layer Testing
Web layer testing focuses on MVC behavior, including request mapping, HTTP status codes, JSON serialization, validation, and exception responses. The @WebMvcTest annotation loads a limited Spring MVC test context.
4.3 Integration Testing
Integration testing verifies that multiple application components work together. The @SpringBootTest annotation can load the full Spring application context. Depending on the test setup, the database may be real, containerized, or replaced with an embedded database.
4.4 Comparison
| Feature |
Unit Test |
Web Slice Test |
Integration Test |
| Primary focus |
Individual component |
MVC and HTTP behavior |
Component collaboration |
| Spring context |
Usually not required |
Limited MVC context |
Full context commonly used |
| Dependency handling |
Mocks or fakes |
Service dependencies mocked |
Real or configured test dependencies |
| Execution speed |
Usually fastest |
Usually fast |
Usually slower |
5. Required Dependencies
For a Maven-based Spring Boot project, the Spring Boot test starter provides common testing tools, including JUnit Jupiter, Mockito, and Spring Test support.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Place the dependency inside the project's <dependencies> section. The exact versions are generally managed by the Spring Boot parent or dependency management configuration. For Spring MVC controller tests, ensure the application includes the appropriate Spring MVC web starter.
6. Example Project Structure
src
├── main
│ └── java/com/example/employee
│ ├── EmployeeApplication.java
│ ├── controller/EmployeeController.java
│ ├── service/EmployeeService.java
│ ├── repository/EmployeeRepository.java
│ └── model/Employee.java
└── test
└── java/com/example/employee
├── controller/EmployeeControllerTest.java
└── service/EmployeeServiceTest.java
Keep tests in the corresponding package structure under src/test/java. This makes test classes easier to discover and maintain.
7. Creating the Employee Model
The following simplified model represents an employee. In a real application, the class might be a JPA entity or a DTO, depending on the role it serves.
package com.example.employee.model;
public class Employee {
private Long id;
private String name;
private String department;
public Employee() {
}
public Employee(Long id, String name, String department) {
this.id = id;
this.name = name;
this.department = department;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
8. Creating the Repository Interface
The repository represents the data access layer. For a Spring Data JPA application, it may extend JpaRepository.
package com.example.employee.repository;
import com.example.employee.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository
extends JpaRepository<Employee, Long> {
}
In unit tests, the repository can be mocked so that database access is not required. Repository behavior itself should be covered separately when database queries and persistence mappings need verification.
9. Creating the Service Layer
The service contains the application's business logic. The example below provides basic employee operations and handles an employee that cannot be found.
package com.example.employee.service;
import com.example.employee.model.Employee;
import com.example.employee.repository.EmployeeRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EmployeeService {
private final EmployeeRepository repository;
public EmployeeService(EmployeeRepository repository) {
this.repository = repository;
}
public List<Employee> getAllEmployees() {
return repository.findAll();
}
public Employee getEmployeeById(Long id) {
return repository.findById(id)
.orElseThrow(() ->
new IllegalArgumentException("Employee not found"));
}
public Employee createEmployee(Employee employee) {
return repository.save(employee);
}
}
Constructor injection makes the service easier to test because a mock repository can be passed directly to the constructor.
10. Creating the REST Controller
The controller exposes HTTP endpoints and delegates business operations to the service.
package com.example.employee.controller;
import com.example.employee.model.Employee;
import com.example.employee.service.EmployeeService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/employees")
public class EmployeeController {
private final EmployeeService service;
public EmployeeController(EmployeeService service) {
this.service = service;
}
@GetMapping
public List<Employee> getAllEmployees() {
return service.getAllEmployees();
}
@GetMapping("/{id}")
public Employee getEmployeeById(@PathVariable Long id) {
return service.getEmployeeById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Employee createEmployee(@RequestBody Employee employee) {
return service.createEmployee(employee);
}
}
This example uses a straightforward exception type to keep the focus on testing. In production applications, prefer domain-specific exceptions and a centralized exception handler to map failures to appropriate HTTP responses.
11. Testing Service Classes with JUnit and Mockito
Service unit tests verify business behavior without starting the Spring application context. JUnit Jupiter provides test lifecycle and assertion features, while Mockito creates mock dependencies.
11.1 Important Annotations
| Annotation |
Purpose |
| @Test |
Marks a method as a test. |
| @ExtendWith(MockitoExtension.class) |
Enables Mockito integration with JUnit Jupiter. |
| @Mock |
Creates a mock dependency. |
| @InjectMocks |
Creates the test subject and injects its mock dependencies. |
| @BeforeEach |
Runs setup logic before each test method. |
11.2 Service Test Example
package com.example.employee.service;
import com.example.employee.model.Employee;
import com.example.employee.repository.EmployeeRepository;
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 java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class EmployeeServiceTest {
@Mock
private EmployeeRepository repository;
@InjectMocks
private EmployeeService service;
@Test
void shouldReturnAllEmployees() {
List<Employee> employees = List.of(
new Employee(1L, "Asha", "IT"),
new Employee(2L, "Rahul", "HR")
);
when(repository.findAll()).thenReturn(employees);
List<Employee> result = service.getAllEmployees();
assertEquals(2, result.size());
assertEquals("Asha", result.get(0).getName());
verify(repository).findAll();
}
@Test
void shouldReturnEmployeeWhenIdExists() {
Employee employee = new Employee(1L, "Asha", "IT");
when(repository.findById(1L))
.thenReturn(Optional.of(employee));
Employee result = service.getEmployeeById(1L);
assertEquals("Asha", result.getName());
assertEquals("IT", result.getDepartment());
verify(repository).findById(1L);
}
@Test
void shouldThrowExceptionWhenEmployeeDoesNotExist() {
when(repository.findById(99L))
.thenReturn(Optional.empty());
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> service.getEmployeeById(99L)
);
assertEquals("Employee not found", exception.getMessage());
verify(repository).findById(99L);
}
@Test
void shouldSaveEmployee() {
Employee input = new Employee(null, "Neha", "Finance");
Employee saved = new Employee(3L, "Neha", "Finance");
when(repository.save(input)).thenReturn(saved);
Employee result = service.createEmployee(input);
assertEquals(3L, result.getId());
assertEquals("Neha", result.getName());
verify(repository).save(input);
}
}
12. Understanding Mockito Stubbing and Verification
Mockito allows a test to define how a dependency behaves and verify how the test subject interacts with it.
Stubbing a Method
when(repository.findById(1L))
.thenReturn(Optional.of(employee));
This configures the mock to return the supplied employee when the specified method is called with ID 1.
Verifying an Interaction
verify(repository).findById(1L);
This verifies that the service called the repository with the expected argument. Use interaction verification when the interaction is important to the behavior being tested; avoid verifying every implementation detail unnecessarily.
Verifying No Interactions
verifyNoInteractions(repository);
This can be useful when invalid input should be rejected before any repository operation. It should only be used when the absence of interactions is part of the intended behavior.
13. Testing Controllers with @WebMvcTest
The @WebMvcTest annotation loads a focused Spring MVC test context rather than the complete application. It is useful for testing request mappings, JSON handling, validation, status codes, and controller advice.
With Spring Boot versions that support it, @MockitoBean can replace a Spring bean with a Mockito mock. Older projects commonly use @MockBean. Select the annotation supported by the Spring Boot version in your project.
13.1 Controller Test Example
package com.example.employee.controller;
import com.example.employee.model.Employee;
import com.example.employee.service.EmployeeService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(EmployeeController.class)
class EmployeeControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@org.springframework.test.context.bean.override.mockito.MockitoBean
private EmployeeService service;
@Test
void shouldReturnAllEmployees() throws Exception {
when(service.getAllEmployees()).thenReturn(
List.of(new Employee(1L, "Asha", "IT"))
);
mockMvc.perform(get("/api/employees"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(
MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$[0].id").value(1))
.andExpect(jsonPath("$[0].name").value("Asha"));
}
@Test
void shouldReturnEmployeeById() throws Exception {
when(service.getEmployeeById(1L))
.thenReturn(new Employee(1L, "Asha", "IT"));
mockMvc.perform(get("/api/employees/{id}", 1L))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.department").value("IT"));
}
@Test
void shouldCreateEmployee() throws Exception {
Employee request = new Employee(null, "Neha", "Finance");
Employee saved = new Employee(3L, "Neha", "Finance");
when(service.createEmployee(org.mockito.ArgumentMatchers.any(Employee.class)))
.thenReturn(saved);
mockMvc.perform(post("/api/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(3))
.andExpect(jsonPath("$.name").value("Neha"));
}
}
Version note: The example uses @MockitoBean, available in newer Spring testing versions. If your Spring Boot version does not provide this annotation, use @MockBean from the corresponding Spring Boot test package. Avoid mixing test annotations from incompatible framework versions.
14. Understanding MockMvc
MockMvc performs simulated HTTP requests through Spring MVC without requiring a real web server. It is commonly used with @WebMvcTest.
| Method |
Purpose |
| perform() |
Executes a simulated HTTP request. |
| get() |
Creates a GET request. |
| post() |
Creates a POST request. |
| put() |
Creates a PUT request. |
| delete() |
Creates a DELETE request. |
| andExpect() |
Checks the resulting HTTP response. |
| jsonPath() |
Checks values inside JSON responses. |
Example:
mockMvc.perform(get("/api/employees/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Asha"));
15. Testing HTTP Status Codes
Controllers should return status codes that match the operation's outcome. Tests should verify these codes explicitly.
| HTTP Status |
Typical Use |
Testing Example |
| 200 OK |
Successful retrieval or update returning a response. |
status().isOk() |
| 201 Created |
A resource was successfully created. |
status().isCreated() |
| 204 No Content |
A successful operation with no response body. |
status().isNoContent() |
| 400 Bad Request |
Invalid input or malformed request. |
status().isBadRequest() |
| 404 Not Found |
The requested resource does not exist. |
status().isNotFound() |
| 500 Internal Server Error |
An unexpected server failure. |
status().isInternalServerError() |
The correct status depends on the API contract. For example, a missing employee is often represented as 404, but the application must explicitly map the relevant exception to that response.
16. Testing Request and Response JSON
REST APIs commonly exchange JSON. Controller tests should verify that the application accepts valid JSON and returns the expected JSON fields.
Request Example
{
"name": "Neha",
"department": "Finance"
}
Response Example
{
"id": 3,
"name": "Neha",
"department": "Finance"
}
Test Assertions
mockMvc.perform(post("/api/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("Neha"))
.andExpect(jsonPath("$.department").value("Finance"));
Using Jackson's ObjectMapper avoids manually building JSON strings and reduces errors caused by escaping or formatting.
17. Testing Validation in Controllers
Spring Boot applications often use Jakarta Bean Validation annotations such as @NotBlank, @Email, @Size, and @Positive to validate incoming data.
For example, a request DTO can define basic employee validation rules:
package com.example.employee.model;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class EmployeeRequest {
@NotBlank(message = "Name is required")
@Size(max = 100, message = "Name must be at most 100 characters")
private String name;
@NotBlank(message = "Department is required")
private String department;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
To activate request validation, add @Valid to the controller parameter and ensure the validation dependency is included in the project.
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Employee createEmployee(
@jakarta.validation.Valid
@RequestBody EmployeeRequest request) {
Employee employee = new Employee(
null, request.getName(), request.getDepartment());
return service.createEmployee(employee);
}
A test can then send an invalid request and verify that the controller returns a 400 response. The exact response body depends on the application's exception handling configuration.
mockMvc.perform(post("/api/employees")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"\",\"department\":\"IT\"}"))
.andExpect(status().isBadRequest());
18. Testing Exception Handling
Applications should return clear, consistent HTTP responses when an operation fails. A global exception handler implemented using @RestControllerAdvice can translate application exceptions into appropriate HTTP status codes.
Example Exception Handler
package com.example.employee.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.bind.annotation.ResponseStatus;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ProblemDetail handleNotFound(
IllegalArgumentException exception) {
return ProblemDetail.forStatusAndDetail(
HttpStatus.NOT_FOUND, exception.getMessage());
}
}
For production code, use a dedicated not-found exception rather than mapping every IllegalArgumentException to 404. Invalid arguments may represent client errors that should instead return 400.
Testing the Error Response
@Test
void shouldReturnNotFoundWhenEmployeeDoesNotExist() throws Exception {
when(service.getEmployeeById(99L))
.thenThrow(new IllegalArgumentException("Employee not found"));
mockMvc.perform(get("/api/employees/99"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.detail")
.value("Employee not found"));
}
This test verifies the behavior of the MVC layer and exception handler together. The response property names depend on the selected error format and Spring version, so align assertions with the API's actual response contract.
19. Testing Controller and Service Responsibilities Separately
A controller test should generally verify HTTP behavior while mocking the service. A service test should verify business rules while mocking repositories or other external dependencies.
| Scenario |
Controller Test |
Service Test |
| Request mapping |
Yes |
No |
| HTTP status and JSON format |
Yes |
No |
| Business calculations |
Usually not directly |
Yes |
| Repository interaction |
Usually mocked indirectly through service |
Yes, when relevant |
| Bean validation |
Yes |
Test domain rules separately if needed |
| Database query correctness |
Not in a controller slice test |
Use repository or integration tests |
20. Testing with @SpringBootTest
The @SpringBootTest annotation loads the Spring application context and is useful when the test must verify that several real components work together.
package com.example.employee;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@SpringBootTest
class EmployeeApplicationTest {
@Test
void applicationContextLoads() {
assertNotNull(true);
}
}
The example demonstrates the basic annotation but is not a meaningful application behavior test. A useful integration test should call a service or endpoint and verify an actual result. For example, it could create an employee through the service and verify that the employee can be retrieved. If the test writes to a database, configure a dedicated test database and clean up data appropriately.
21. Testing a Full HTTP Flow
When an integration test needs to exercise the full application stack, Spring Boot can start the application on a random port.
@SpringBootTest(webEnvironment =
SpringBootTest.WebEnvironment.RANDOM_PORT)
class EmployeeApiIntegrationTest {
// Inject TestRestTemplate or WebTestClient.
// Send requests to the running application.
// Assert status codes and response bodies.
}
Choose TestRestTemplate or WebTestClient based on the project's configuration and testing requirements. Full-stack tests are useful for verifying actual HTTP communication, filters, serialization, and integration between application layers.
22. Common Testing Mistakes
- Testing only successful scenarios: Include invalid input, missing records, and dependency failures.
- Loading the entire context for every test: Use unit tests or MVC slice tests when they meet the testing goal.
- Using a real database in every unit test: Mock repository dependencies for service unit tests and reserve database testing for appropriate integration tests.
- Over-mocking: Mock external dependencies, not the behavior you are trying to verify.
- Ignoring HTTP status codes: Assert the API's intended status code for each scenario.
- Testing private methods directly: Prefer verifying observable behavior through public methods.
- Using shared mutable test data: Create isolated data for each test to avoid order-dependent results.
- Leaving exception behavior undefined: Define and test consistent error responses.
- Relying on deprecated test APIs without checking versions: Match annotations and dependencies to the Spring Boot version used by the project.
23. Best Practices for Spring Boot Testing
- Write small tests that focus on one behavior at a time.
- Use descriptive test names, such as
shouldReturnNotFoundWhenEmployeeDoesNotExist.
- Follow the Arrange, Act, Assert pattern.
- Use constructor injection in application components to simplify unit testing.
- Use
@WebMvcTest for controller behavior and Mockito for service dependencies.
- Use JUnit assertions to verify results and Mockito verification only when relevant.
- Test boundary conditions, invalid data, and exceptional situations.
- Keep test fixtures readable and avoid unnecessary setup.
- Use isolated databases or Testcontainers when verifying database integration.
- Run tests automatically in the project's continuous integration pipeline.
24. Running Tests with Maven
To execute tests using Maven, run the following command from the project root:
mvn test
To build the project and run its verification lifecycle, use:
mvn verify
To execute a specific test class, use:
mvn -Dtest=EmployeeServiceTest test
Review the test report and fix failing assertions, context initialization errors, missing dependencies, or incompatible framework annotations.
25. Practical Project: Employee Management API Testing
Build a small employee management application and create a test suite for its main operations.
Project Requirements
- Retrieve all employees.
- Retrieve an employee by ID.
- Create an employee.
- Validate employee names and department values.
- Return a consistent error response when an employee is not found.
- Keep controller and service tests separate.
- Add integration tests for persistence and end-to-end behavior.
Suggested Test Cases
| Test Case |
Expected Result |
| Retrieve an existing employee |
200 OK with correct JSON fields |
| Retrieve an unknown employee |
404 Not Found according to the API contract |
| Create a valid employee |
201 Created with the saved employee |
| Create an employee with a blank name |
400 Bad Request when validation is configured |
| Service receives a repository result |
Expected employee is returned |
| Service cannot find the requested employee |
Expected domain exception is thrown |
| Repository save returns a saved entity |
Service returns the saved entity |
26. Exercises for Practice
- Write a service unit test that verifies the result of
getAllEmployees().
- Write a test that verifies the service throws an exception for a missing employee.
- Create a controller test for the GET endpoint using MockMvc.
- Test the POST endpoint with a valid JSON request.
- Add validation to an employee request DTO and test invalid input.
- Create a global exception handler and test the response for a missing employee.
- Write an integration test that verifies an employee can be saved and retrieved.
- Run the full test suite through Maven and inspect the reports.
27. Interview Questions and Answers
Q1. What is unit testing in Spring Boot?
Unit testing verifies an individual class or component independently, usually with its dependencies mocked or replaced by simple test doubles.
Q2. What is the purpose of @WebMvcTest?
It loads a focused Spring MVC test context to test controllers, request mapping, validation, serialization, and related web behavior without loading the complete application.
Q3. What is MockMvc?
MockMvc performs simulated HTTP requests through Spring MVC and allows assertions against status codes, headers, and response bodies without starting a real server.
Q4. What is Mockito used for?
Mockito creates mock dependencies, defines their responses, and verifies interactions with those dependencies.
Q5. What is the difference between @Mock and @InjectMocks?
@Mock creates a mock dependency. @InjectMocks creates the test subject and attempts to inject its mock dependencies.
Q6. What is @SpringBootTest?
It loads the Spring Boot application context and is useful for tests that require multiple real application components to work together.
Q7. How do you test a controller without calling the database?
Use @WebMvcTest and replace the service dependency with a mock so that controller behavior can be tested independently of persistence.
Q8. How do you test an exception in a service?
Use JUnit's assertThrows() and verify the expected exception type and, where appropriate, its message.
Q9. How do you test JSON response fields?
Use MockMvc's jsonPath() assertions to verify values at specific paths in the response JSON.
Q10. What is the purpose of @RestControllerAdvice?
It provides centralized handling of controller exceptions and can return consistent HTTP status codes and error response bodies.
Q11. Why should controller and service tests be separate?
Separating tests clarifies responsibility, makes failures easier to diagnose, and allows each layer to be tested at the appropriate level.
Q12. What is an integration test?
An integration test checks whether multiple components or systems work together correctly, such as a service, repository, and database.
Q13. How can controller validation be tested?
Send an invalid request through MockMvc and assert the expected validation status and error response.
Q14. How do you run Spring Boot tests with Maven?
Use mvn test to execute the project's tests, or mvn verify to run the verification lifecycle.
Q15. Should every test use @SpringBootTest?
No. Unit tests and MVC slice tests are usually faster and more focused. Use a full application context when integration behavior needs to be verified.
28. Quick Revision Checklist
- Understand controller, service, and repository responsibilities.
- Use JUnit for test execution and assertions.
- Use Mockito to isolate dependencies in service unit tests.
- Use
@WebMvcTest and MockMvc for HTTP-layer tests.
- Assert status codes, JSON fields, validation, and exception responses.
- Use
@SpringBootTest for tests requiring the full application context.
- Match testing annotations to the Spring Boot version.
- Run tests using Maven and review the results.
29. Conclusion
Testing Spring Boot controllers and services helps ensure that an application handles requests correctly, follows business rules, validates input, and responds consistently to errors. JUnit and Mockito are suitable for focused service unit tests, while MockMvc and @WebMvcTest are useful for controller tests. Integration tests using @SpringBootTest can verify that multiple layers work together.
A balanced test suite combines fast unit tests, focused web tests, and selected integration tests. This approach improves code quality, reduces regressions, and makes Spring Boot applications easier to maintain.
Learn More with JustAcademy
Explore the Advanced Java Training Course to learn more about Java development, Spring Boot, application architecture, and testing.
Register for a Course Demo
Visit the JustAcademy Website for additional learning resources and training information.