Showing posts with label spring data jpa. Show all posts
Showing posts with label spring data jpa. Show all posts

Monday, September 1, 2025

Spring Boot Unit Test For REST API

In this article we'll see how to unit test Spring Boot REST API. As the name "unit test" itself suggests classes should be tested in isolation so in this tutorial we'll write unit tests for Controller class.

Please refer this post- Spring Boot + Data JPA + MySQL REST API CRUD Example to see the Controller class for which we are going to write unit tests.

Annotations and Classes used for writing unit tests in Spring Boot

First of all, ensure spring-boot-starter-test dependency is included. If you are using Maven then this dependency can be included in pom.xml like this.

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

This starter bundles together common testing libraries like JUnit, Mockito, AssertJ, Spring Boot Test etc.

@WebMvcTest Annotation

It is a specialized annotation that can be used for a Spring MVC test that focuses only on Spring MVC components. Using this annotation disables the full auto-configuration and only enables auto-configuration that is relevant to MVC tests. Similarly, component scanning is limited to beans annotated with:

  • @Controller
  • @ControllerAdvice
  • @JsonComponent

It focuses on testing controllers and other web-related components in isolation, without loading the entire Spring application context.

MockMvc class

MockMvc provides support for testing Spring MVC applications. With MockMvc you don't require to run a web server, MockMvc simulates the full Spring MVC request handling using mock request and response objects.

@MockitoBean annotation

@MockitoBean annotation in Spring Framework is used in test classes to create a mock object. Since we are testing Controller class in isolation so any controller's dependencies on service layer, repository layer is handled using mock instances.

Note that Spring framework leverages the Mockito library to create and manage the mock objects which are used to create stub methods and verify returned values.

Note that @MockitoBean annotation was introduced in Spring Framework 6.2 it replaces @MockBean, which is now deprecated.

For example, if you want to test the UserController class then basic setup using the above mentioned annotations and classes would be as given below-

@WebMvcTest(UserController.class)
public class UserControllerTest {
	@Autowired
	private MockMvc mockmvc;
	@MockitoBean
	private UserService userService;

	@Test
	public void testAddUserReturningBadRequest() throws Exception{
		…
		…
  }
}

Writing unit tests for Spring Boot REST API

Here we want to write unit tests for UserController class which has dependency on UserService and provides methods for adding user (POST), getting all users and getting user by id (GET), updating user (PUT) and delete user by id (DELETE).

If you analyze the code for adding a new user, scenarios you can test are-

  1. If User bean fails validation; which results in MethodArgumentNotValidException being thrown.
  2. If same user is added again; which results in UserAlreadyExistsException being thrown.
  3. All values are correct and the new user is added. Response status sent is 201 (Created) and created user is also returned as response body.

Here is the method that is written in UserController annotated with @PostMapping

// Insert user record
@PostMapping
public ResponseEntity<User> addUser(@Valid @RequestBody User user) {
  User createdUser = userService.addUser(user);
  return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}

If you have to write unit tests for the above mentioned scenario 1 and 3 then that is how you can write the tests.

@WebMvcTest(UserController.class)
public class UserControllerTest {
	@Autowired
	private MockMvc mockmvc;
	@Autowired
	private ObjectMapper objectMapper;
	@MockitoBean
	private UserService userService;
	
	@Test
	public void testAddUserReturningBadRequest() throws Exception{
		User user = new User();
		user.setFirstName("");
		user.setLastName("Tiwari");
		user.setUserType("Platinum");
		user.setStartDate(LocalDate.of(2025, 8, 26));
		
		mockmvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON)
				 .content(objectMapper.writeValueAsString(user)))
        		 .andExpect(status().isBadRequest())
        		 .andDo(print());
	}

	@Test
	public void testAddUserReturningCreated() throws Exception{
		User user = new User();
		user.setFirstName("Ram");
		user.setLastName("Tiwari");
		user.setUserType("Platinum");
		user.setStartDate(LocalDate.of(2025, 8, 26));
		// sending Id also
		user.setUserId(1);
		Mockito.when(userService.addUser(any(User.class))).thenReturn(user);
		mockmvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON)
				 .content(objectMapper.writeValueAsString(user)))
        		 .andExpect(status().isCreated())
        		 .andExpect(jsonPath("$.firstName").value("Ram"))
        		 .andExpect(jsonPath("$.userId").value(1))
        		 .andDo(print());        		 		
	}
}

Important points to note here are-

  1. Method testAddUserReturningBadRequest() tests the scenario, if validation fails and method testAddUserReturningCreated() tests the scenario when new user is added.
  2. With Post mapping, you need to send the resource as part of request body in the form of JSON. For that ObjectMapper, provided by Jackson library, is used which converts Java object to and from JSON.
  3. perform method of MockMvc is used to perform a request that uses any Http method (in this case POST), send the type of content and the request body.
  4. You can define expectations by appending one or more andExpect(..) calls after performing a request. These andExpect() methods use MockMvcResultMatchers to assert on the HTTP status, response headers, and response body content. For example andExpect(status().isCreated()), andExpect(jsonPath("$.FIELD_NAME").value("EXPECTED_VALUE"))
  5. andDo(print()) is used to print the details of a MockHttpServletRequest and MockHttpServletResponse to the console during a test execution.
  6. In the method testAddUserReturningCreated(), Mockito.when() is used to specify the return value when the method is called on the mock object. In the above example it specifies; when userService.addUser() is called from the UserController then use the mocked userService instance and return the user object itself.
    Mockito.when(userService.addUser(any(User.class))).thenReturn(user);
    
  7. Notice the use of any() in the userService.addUser() call. It specifies that any user object will do. If you want the exact same user instance to be used then ensure that the User class implements hashCode() and equals() method to enforce object identity. Then you can use Mockito.when() as given below.
    Mockito.when(userService.addUser(user)).thenReturn(user);
    

Same way you can write unit tests for other methods in the UserController class. Here is the complete UserControllerTest class.

UserControllerTest.java

import static org.mockito.ArgumentMatchers.any;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
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.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netjstech.exception.ResourceNotFoundException;
import com.netjstech.model.User;
import com.netjstech.service.UserService;

@WebMvcTest(UserController.class)
public class UserControllerTest {
  @Autowired
  private MockMvc mockmvc;
  @Autowired
  private ObjectMapper objectMapper;
  @MockitoBean
  private UserService userService;
  
//  @Test
//  void test() {
//    fail("Not yet implemented");
//  }
  
  
  @Test
  public void testAddUserReturningBadRequest() throws Exception{
    User user = new User();
    user.setFirstName("");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    

    mockmvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON)
         .content(objectMapper.writeValueAsString(user)))
             .andExpect(status().isBadRequest())
             .andDo(print());    
  }

  @Test
  public void testAddUserReturningCreated() throws Exception{
    User user = new User();
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    // sending Id also
    user.setUserId(1);
    Mockito.when(userService.addUser(any(User.class))).thenReturn(user);
    mockmvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON)
         .content(objectMapper.writeValueAsString(user)))
             .andExpect(status().isCreated())
             .andExpect(jsonPath("$.firstName").value("Ram"))
             .andExpect(jsonPath("$.userId").value(1))
             .andDo(print());                 
  }
  
  @Test
  public void testGetAllUsers() throws Exception{
    List<User> userList = new ArrayList<>();
    User user1 = new User();
    user1.setUserId(1);
    user1.setFirstName("Ram");
    user1.setLastName("Tiwari");
    user1.setUserType("Platinum");
    user1.setStartDate(LocalDate.of(2025, 8, 26));
    
    User user2 = new User();
    user2.setUserId(2);
    user2.setFirstName("Shyam");
    user2.setLastName("Sharma");
    user2.setUserType("Gold");
    user2.setStartDate(LocalDate.of(2025, 7, 22));
    userList.add(user1);
    userList.add(user2);
    Mockito.when(userService.getAllUsers()).thenReturn(userList);
    
    mockmvc.perform(get("/user/allusers"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$[0].firstName").value("Ram"))
        .andExpect(jsonPath("$[1].firstName").value("Shyam"))
        .andDo(print());
  }
  
  @Test
  public void testGetUserReturningResourceNotFound() throws Exception{
    int userId = 1;
    
    Mockito.when(userService.getUserById(userId)).thenThrow(new ResourceNotFoundException("User with ID " + userId + " not found"));
    
    mockmvc.perform(get("/user/"+userId)).andExpect(status().isNotFound())
    .andExpect(content().json("{\"message\": \"Resource Not Found\"}"))
    .andExpect(jsonPath("$.exceptionMessage").value("User with ID " + userId + " not found"))
    .andDo(print());
    
  }
  
  @Test
  public void testGetUserReturningOK() throws Exception{
    int userId = 1;
    User user = new User();
    user.setUserId(1);
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    Mockito.when(userService.getUserById(userId)).thenReturn(user);
    
    mockmvc.perform(get("/user/"+userId)).andExpect(status().isOk())
    .andExpect(jsonPath("$.firstName").value("Ram"))
    .andExpect(jsonPath("$.lastName").value("Tiwari"))
    .andDo(print());
  }
  
  @Test
  public void testUpdateUserReturningResourceNotFound() throws Exception{
    User user = new User();
    user.setUserId(100);
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    
    Mockito.when(userService.updateUser(user))
          .thenThrow(new ResourceNotFoundException("User with ID " + user.getUserId() + " not found"));
    
    mockmvc.perform(put("/user").contentType(MediaType.APPLICATION_JSON)
         .content(objectMapper.writeValueAsString(user)))
         .andExpect(status().isNotFound())
         .andExpect(content().json("{\"message\": \"Resource Not Found\"}"))
         .andExpect(jsonPath("$.exceptionMessage").value("User with ID " + user.getUserId() + " not found"))
         .andDo(print());
    
  }
  
  @Test
  public void testUpdateUserReturningOK() throws Exception{
    User user = new User();
    user.setUserId(1);
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    
    User updatedUser = new User();
    updatedUser.setUserId(1);
    updatedUser.setFirstName("Ram");
    updatedUser.setLastName("Tiwari");
    updatedUser.setUserType("Gold");
    updatedUser.setStartDate(LocalDate.of(2025, 8, 26));
    
    Mockito.when(userService.updateUser(user))
          .thenReturn(updatedUser);
    
    mockmvc.perform(put("/user").contentType(MediaType.APPLICATION_JSON)
         .content(objectMapper.writeValueAsString(user)))
         .andExpect(status().isOk())
         .andExpect(jsonPath("$.userType").value("Gold"))
         .andDo(print());
    
  }
  
  @Test
  public void testDeleteUserReturningResourceNotFound() throws Exception{
    int userId = 100;
    Mockito.doThrow(new ResourceNotFoundException("User with ID " + userId + " not found"))
    .when(userService).deleteUserById(userId);
    
    mockmvc.perform(delete("/user/"+userId))
         .andExpect(status().isNotFound())
         .andExpect(content().json("{\"message\": \"Resource Not Found\"}"))
         .andExpect(jsonPath("$.exceptionMessage").value("User with ID " + userId + " not found"))
         .andDo(print());
    
  }
  
  @Test
  public void testDeleteUserReturningOK() throws Exception{
    int userId = 1;

        Mockito.doNothing().when(userService).deleteUserById(userId);
        //Mockito.verify(userService, Mockito.times(1)).deleteUserById(userId);
        mockmvc.perform(delete("/user/"+userId))
         .andExpect(status().isNoContent())
         .andDo(print());
    
  }

}

Some of the points to note here are-

  1. In the method testGetUserReturningResourceNotFound(), Mockito.when().thenThrow() is used to specify the exception that has to be thrown by the mock object to test user id not found scenario.
  2. Method testGetUserReturningResourceNotFound() shows another way to extract properties from JSON other than using jsonPath.
    andExpect(content().json("{\"message\": \"Resource Not Found\"}"))
    
  3. Method deleteUserById() in the actual UserServiceImpl class doesn't return any value so unit test for that also needs to mock userservice call with that expectation.
    Mockito.doNothing().when(userService).deleteUserById(userId);
    

That's all for this topic Spring Boot Unit Test For REST API. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot spring-boot-starter-parent
  2. Spring Boot + Spring Security JWT Authentication Example
  3. Spring Data JPA - Spring Data Tutorial
  4. Spring Boot Observability - Distributed Tracing, Metrics
  5. Spring Bean Life Cycle

You may also like-

  1. BeanPostProcessor in Spring Framework
  2. Transaction Management in Spring
  3. Spring Web Reactive Framework - Spring WebFlux Tutorial
  4. Spring JdbcTemplate Insert, Update And Delete Example
  5. JVM Run-Time Data Areas - Java Memory Allocation
  6. Comparing Enum to String in Java
  7. Python Lambda Functions With Examples
  8. Angular ngClass Directive With Examples

Tuesday, August 26, 2025

Spring Boot + Data JPA + MySQL REST API CRUD Example

In this post Spring Data tutorial we got to know about Spring Data and how to use it. In this tutorial we'll create a Spring Boot REST API CRUD application using Spring Data JPA and MySQL database. We'll also see how to do exception handling with Spring Boot REST API.


Thursday, August 17, 2023

Spring Data JPA - Spring Data Tutorial

Though using Spring framework reduces the amount of code and configuration needed to work with any persistence store but you still, generally need to write a DAO interface and a DAO implementation class. Use of Spring Data Repository abstraction over the persistence store significantly reduces the amount of boilerplate code required to implement data access layers for various persistence stores.

With Spring Data you just create an interface extending the framework provided interface (Repository, CrudRepository, JpaRepoitory). Spring framework creates the implementation class automatically, you don't need to write DAO implementation class yourself. In this Spring Data tutorial we'll get to know about the core concepts of data repository, Spring data modules and how to use data repository.

Spring data modules

Spring data is an umbrella project which contains many subprojects that are specific to a given database. Some of the main modules are listed here.

Data Module Description
Spring Data CommonsCore Spring concepts underpinning every Spring Data module
Spring Data JDBCSpring Data repository support for JDBC
Spring Data JPASpring Data repository support for JPA
Spring Data KeyValueMap based repositories and SPIs to easily build a Spring Data module for key-value stores
Spring Data LDAPSpring Data repository support for Spring LDAP
Spring Data MongoDBSpring based, object-document support and repositories for MongoDB
Spring Data RedisEasy configuration and access to Redis from Spring applications
Spring Data RESTExports Spring Data repositories as hypermedia-driven RESTful resources
Spring Data for Apache CassandraEasy configuration and access to Apache Cassandra or large scale, highly available, data-oriented Spring applications

Spring Data Repositories

The central interface in the Spring Data repository abstraction is Repository. It takes the domain class to manage as well as the identifier type of the domain class as type arguments. Repository is a marker interface.

public interface Repository<T, ID> {

}

CrudRepository

CrudRepository interface extends Repository and provides sophisticated CRUD functionality for the entity class that is being managed.

public interface CrudRepository<T, ID> extends Repository<T, ID> {
  <S extends T> S save(S entity);
  <S extends T> Iterable<S> saveAll(Iterable<S> entities);
  Optional<T> findById(ID id);
  boolean existsById(ID id);
  Iterable<T> findAll();
  Iterable<T> findAllById(Iterable<ID> ids);
  long count();
  void deleteById(ID id);
  void delete(T entity);
  void deleteAllById(Iterable<? extends ID> ids);
  void deleteAll(Iterable<? extends T> entities);
  void deleteAll();
}

Explanation for some of the methods.

  1. save(S entity)- Saves the given entity.
  2. findById(ID id)- Returns the entity identified by the given ID.
  3. findAll()- Returns all entities.
  4. count()- Returns the number of entities.
  5. delete(T entity)- Deletes the given entity.
  6. existsById(ID id)- Indicates whether an entity with the given ID exists.

PagingAndSortingRepository

PagingAndSortingRepository abstraction adds additional methods to ease paginated access to entities.

public interface PagingAndSortingRepository<T, ID> extends Repository<T, ID> {
  Iterable<T> findAll(Sort sort);
  Page<T> findAll(Pageable pageable);
}

module-specific interfaces

Then there are module specific interfaces also, for example, if you are using JPA then you will use Spring Data JPA dependency and the interface is JpaRepository.

If you are using MongoDB then you will use Spring Data MongoDB dependency and the interface is MongoRepository.

JpaRepository

JpaRepository provides JPA specific extensions like flushing all pending changes to DB and deleting records in batch.

public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

  void flush();

  <S extends T> S saveAndFlush(S entity);

  <S extends T> List<S> saveAllAndFlush(Iterable<S> entities);

  @Deprecated
  default void deleteInBatch(Iterable<T> entities) {
    deleteAllInBatch(entities);
  }

  void deleteAllInBatch(Iterable<T> entities);

  void deleteAllByIdInBatch(Iterable<ID> ids);

  void deleteAllInBatch();

  @Deprecated
  T getOne(ID id);

  @Deprecated
  T getById(ID id);

  T getReferenceById(ID id);

  @Override
  <S extends T> List<S> findAll(Example<S> example);

  @Override
  <S extends T> List<S> findAll(Example<S> example, Sort sort);
}
Spring Data

How to use Spring Data Repository

In order to use Spring data repository, you need to follow the following steps. Note that Spring Data JPA is used here.

For complete example using Spring Boot and Spring Data JPA check this post- Spring Boot + Data JPA + MySQL REST API CRUD Example

1. Adding dependency- If you are using Spring boot and you want to use Spring data JPA then you need to add dependency for spring-boot-starter-data-jpa.

If you are using Spring framework then you need to add dependency for spring-data-jpa.

2. Creating Repository- For example if there is an entity class Customer where the field annotated with @Id is of type Long.

@Entity
@Table(name="customer")
public class Customer {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String name;
	private int age;
	private String city;
}

Then you need to declare an interface extending Repository or one of its sub-interfaces and type it to the domain class and ID type that it should handle.

public interface CustomerRepository extends JpaRepository<Customer, Long>{

}

If you don't need the full functionality provided by JpaRepository you can also extend the CrudRepository.

That's all you need to provide; Spring framework automatically generates the implementation class implementing all the methods available in JpaRepository interface.

You can also provide other methods in your interface and Spring framework provides implementation for those methods too, deriving it by the method name. For example, if you want to find customer by city.

public interface CustomerRepository extends JpaRepository<Customer, Long>{
  List<Customer> findByCity(String city);
} 

3. Enabling Spring Data JPA repositories support- If you are using Spring Boot then you don't need to do any thing to enable data repository support. Seeing spring-data-jpa jar in project's class path Spring Boot automatically configures the application to use Spring data jpa repository.

With Spring framework you need to add @EnableJpaRepositories annotation if you are using Java configuration.

@Configuration
@EnableJpaRepositories("com.netjstech.datademo.dao.repository")
@EnableTransactionManagement
public class JPAConfig {
  ...
  ...
}

With XML configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jpa="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  <jpa:repositories base-package=" com.netjstech.datademo.dao.repository " />

</beans>

4. Using the repository- Inject the repository in the class where you need to use it and start calling the methods. Implementation of these methods will be there!

@Service
public class CustomerService {
  @Autowired
  private CustomerRepository repository;

  public Customer getCustomerById(long id) {
    return repository.findById(id).get();
  }

  …
  …

}

That's all for this topic Spring Data JPA - Spring Data Tutorial. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot + Data JPA + MySQL REST API CRUD Example
  2. Spring JdbcTemplate Select Query Example
  3. Spring NamedParameterJdbcTemplate Insert, Update And Delete Example
  4. Spring Transaction Management Example - @Transactional Annotation and JDBC

You may also like-

  1. Spring Boot Microservice - Service Registration and Discovery With Eureka
  2. Circular Dependency in Spring Framework
  3. @FunctionalInterface Annotation in Java
  4. Java join() Method - Joining Strings
  5. What if run() Method Called Directly Instead of start() Method - Java Multi-Threading
  6. AtomicInteger in Java With Examples