Spring Boot TestRestTemplate - Testing CRUD REST APIs

This tutorial will guide you through using TestRestTemplate in Spring Boot for testing CRUD operations on a User entity. We will use an H2 in-memory database for data persistence and create the necessary service, repository, and controller layers.

What is TestRestTemplate?

TestRestTemplate is a template class provided by Spring Boot for integration testing that involves a running server. It is used to make RESTful calls to an actual server and is ideal for full-stack integration testing.

Advantages of TestRestTemplate

Full-Stack Testing: TestRestTemplate is used for end-to-end testing of the application, including the web layer, server, and often the database.

Realistic Scenarios: It is closer to real-world scenarios where the application is running on an actual server, making it ideal for testing the complete stack.

Ease of Use: It offers a straightforward approach for making REST calls, simplifying the testing of RESTful APIs.

Ideal Use Cases for TestRestTemplate

Testing Spring Boot CRUD REST APIs using TestRestTemplate

In this tutorial, we'll create a Spring Boot application that performs CRUD operations on a User entity, using an H2 in-memory database for persistence. We'll then test these CRUD operations using TestRestTemplate. The application will be structured into three layers: Repository, Service, and Controller.

Project Setup

Ensure you have the following dependencies in your pom.xml:

    <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>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>

The User Entity

import jakarta.persistence.*;

    @Entity
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String firstName;
        private String lastName;
        private String email;
    
        // Constructors, Getters, Setters
    }

The UserRepository

import org.springframework.data.jpa.repository.JpaRepository;

    public interface UserRepository extends JpaRepository<User, Long> {
    }

The UserService

import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    import java.util.Optional;
    
    @Service
    public class UserService {
    
        private final UserRepository userRepository;
    
        @Autowired
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        public User createUser(User user) {
            return userRepository.save(user);
        }
    
        public Optional<User> getUser(Long id) {
            return userRepository.findById(id);
        }
    
        public User updateUser(Long id, User userDetails) {
            User user = userRepository.findById(id).orElseThrow();
            user.setFirstName(userDetails.getFirstName());
            user.setLastName(userDetails.getLastName());
            user.setEmail(userDetails.getEmail());
            return userRepository.save(user);
        }
    
        public void deleteUser(Long id) {
            userRepository.deleteById(id);
        }
    
        public List<User> getAllUsers() {
            return userRepository.findAll();
        }
    }

The UserController

import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    
    @RestController
    @RequestMapping("/users")
    public class UserController {
    
        private final UserService userService;
    
        @Autowired
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        @PostMapping
        public ResponseEntity<User> createUser(@RequestBody User user) {
            return ResponseEntity.ok(userService.createUser(user));
        }
    
        @GetMapping("/{id}")
        public ResponseEntity<User> getUser(@PathVariable Long id) {
            return userService.getUser(id)
                    .map(ResponseEntity::ok)
                    .orElse(ResponseEntity.notFound().build());
        }
    
        @GetMapping
        public ResponseEntity<List<User>> getAllUsers() {
            return ResponseEntity.ok(userService.getAllUsers());
        }
    
        @PutMapping("/{id}")
        public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
            return ResponseEntity.ok(userService.updateUser(id, user));
        }
    
        @DeleteMapping("/{id}")
        public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
            userService.deleteUser(id);
            return ResponseEntity.ok().build();
        }
    }

Writing Tests with TestRestTemplate

First, configure TestRestTemplate in your test class:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class UserControllerTest {
    
        @Autowired
        private TestRestTemplate testRestTemplate;
    
        // Test methods go here
    }

Now, write tests for each CRUD operation. Create, retrieve, update, and delete User entities, and assert the responses using TestRestTemplate.

Create (POST)

Testing the creation of a new User.

@Test
    public void createUserTest() {
        User newUser = new User(null, "Alice", "Smith", "alice.smith@example.com");
        ResponseEntity<User> response = testRestTemplate.postForEntity("/users", newUser, User.class);
    
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertNotNull(response.getBody().getId());
        assertEquals("Alice", response.getBody().getFirstName());
    }

Read (GET)

Testing retrieval of a User.

@Test
    public void getUserTest() {
        // Create a user to retrieve
        User newUser = new User(null, "Bob", "Jones", "bob.jones@example.com");
        User createdUser = testRestTemplate.postForObject("/users", newUser, User.class);
    
        ResponseEntity<User> response = testRestTemplate.getForEntity("/users/" + createdUser.getId(), User.class);
    
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals("Bob", response.getBody().getFirstName());
    }

Update (PUT)

Testing the update of a User.

@Test
    public void updateUserTest() {
        // Create a user to update
        User newUser = new User(null, "Charlie", "Brown", "charlie.brown@example.com");
        User createdUser = testRestTemplate.postForObject("/users", newUser, User.class);
    
        User updatedUser = new User(null, "Charles", "Brown", "charlie.brown@example.com");
        testRestTemplate.put("/users/" + createdUser.getId(), updatedUser);
    
        ResponseEntity<User> response = testRestTemplate.getForEntity("/users/" + createdUser.getId(), User.class);
    
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals("Charles", response.getBody().getFirstName());
    }

Delete (DELETE)

Testing the deletion of a User.

@Test
    public void deleteUserTest() {
        // Create a user to delete
        User newUser = new User(null, "Dave", "Wilson", "dave.wilson@example.com");
        User createdUser = testRestTemplate.postForObject("/users", newUser, User.class);
    
        testRestTemplate.delete("/users/" + createdUser.getId());
    
        ResponseEntity<User> response = testRestTemplate.getForEntity("/users/" + createdUser.getId(), User.class);
    
        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
    }

Conclusion

In this tutorial, we've demonstrated how to use TestRestTemplate for testing CRUD operations on a User entity in a Spring Boot application, backed by an H2 in-memory database. This approach is ideal for full-stack integration testing, providing a realistic testing environment while ensuring the correct behavior of the RESTful service.