Testing Spring Boot Applications with REST Assured

Introduction

REST-assured is a powerful Java library for testing RESTful web services. It simplifies the process of writing tests for REST APIs by providing a domain-specific language (DSL) for making HTTP requests and validating responses. In this tutorial, we will demonstrate how to use REST-assured to test a Spring Boot application, covering all CRUD operations. 

Prerequisites

  1. Java Development Kit (JDK) 17 or later
  2. Apache Maven installed
  3. An IDE like IntelliJ IDEA or Eclipse

Step 1: Create a Spring Boot Project

You can create a Spring Boot project using Spring Initializr or your IDE.

Using Spring Initializr

  1. Go to Spring Initializr.
  2. Select the following options:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 3.0.0 or later
    • Group: com.example
    • Artifact: rest-assured-demo
    • Name: rest-assured-demo
    • Package name: com.example.restassureddemo
    • Packaging: Jar
    • Java: 17 or later
  3. Add the following dependencies:
    • Spring Web
    • Spring Data JPA
    • H2 Database
    • Spring Boot Starter Test
  4. Click "Generate" to download the project zip file.
  5. Extract the zip file and open the project in your IDE.

Step 2: Add REST Assured Dependency

Add the following dependency to your pom.xml file:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>5.1.1</version>
    <scope>test</scope>
</dependency>

Step 3: Configure Application Properties

Add the following properties to src/main/resources/application.properties:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

Step 4: Create the Entity Class

Create a new Java class named User in the com.example.restassureddemo package:

package com.example.restassureddemo;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    // Getters and Setters
    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 getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

Explanation: The User class is a JPA entity that represents a user in our system. It has an id, name, and email field. The @Entity annotation specifies that this class is an entity and is mapped to a database table.

Step 5: Create the Repository Interface

Create a new Java interface named UserRepository in the com.example.restassureddemo package:

package com.example.restassureddemo;

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

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

Explanation: The UserRepository interface extends JpaRepository, which provides JPA related methods for standard data access operations. This interface will be used to interact with the database.

Step 6: Create the Service Class

Create a new Java class named UserService in the com.example.restassureddemo package:

package com.example.restassureddemo;

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

import java.util.List;
import java.util.Optional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public List<User> findAll() {
        return userRepository.findAll();
    }

    public Optional<User> findById(Long id) {
        return userRepository.findById(id);
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }
}

Explanation: The UserService class contains methods for CRUD operations. It uses UserRepository to interact with the database. The @Service annotation indicates that this class is a service component in the Spring context.

Step 7: Create the Controller Class

Create a new Java class named UserController in the com.example.restassureddemo package:

package com.example.restassureddemo;

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 {

    @Autowired
    private UserService userService;

    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.findById(id)
                .map(existingUser -> {
                    user.setId(existingUser.getId());
                    return ResponseEntity.ok(userService.save(user));
                })
                .orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        return userService.findById(id)
                .map(user -> {
                    userService.deleteById(id);
                    return ResponseEntity.noContent().build();
                })
                .orElse(ResponseEntity.notFound().build());
    }
}

Explanation: The UserController class handles HTTP requests and responses. It uses UserService to perform CRUD operations. 

The @RestController annotation indicates that this class is a RESTful controller. 

The @RequestMapping("/users") annotation maps HTTP requests to /users to the methods in this controller.

Step 8: Create the Main Application Class

Create a main application class named RestAssuredDemoApplication in the com.example.restassureddemo package:

package com.example.restassureddemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RestAssuredDemoApplication {

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

Explanation: The RestAssuredDemoApplication class contains the main method, which is the entry point of the Spring Boot application. 

The @SpringBootApplication annotation is a convenience annotation that adds all the following:

  • @Configuration: Tags the class as a source of bean definitions for the application context.
  • @EnableAutoConfiguration: Tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
  • @ComponentScan: Tells Spring to look for other components, configurations, and services in the specified package.

Step 9: Create REST Assured Tests

Create a new test class named UserControllerTest in the src/test/java/com/example/restassureddemo package:

package com.example.restassureddemo;

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {

    @LocalServerPort
    private int port;

    @BeforeEach
    public void setUp() {
        RestAssured.port = port;
    }

    @Test
    public void testCreateUser() {
        given()
            .contentType(ContentType.JSON)
            .body(new User(null, "Amit Sharma", "amit.sharma@example.com"))
        .when()
            .post("/users")
        .then()
            .statusCode(201)
            .body("id", notNullValue())
            .body("name", equalTo("Amit Sharma"))
            .body("email", equalTo("amit.sharma@example.com"));
    }

    @Test
    public void testGetAllUsers() {
        given()
            .contentType(ContentType.JSON)
        .when()
            .get("/users")
        .then()
            .statusCode(200);
    }

    @Test
    public void testGetUserById() {
        Long userId = createUser("Ravi Kumar", "ravi.kumar@example.com");

        given()
            .contentType(ContentType.JSON)
        .when()
            .get("/users/{id}", userId)
        .then()
            .statusCode(200)
            .body("id", equalTo(userId.intValue()))
            .body("name", equalTo("Ravi Kumar"))
            .body("email", equalTo("ravi.kumar@example.com"));
    }

    @Test
    public void testUpdateUser() {
        Long userId = createUser("Priya Singh", "priya.singh@example.com");

        given()
            .contentType(ContentType.JSON)
            .body(new User(userId, "Priya Sharma", "priya.sharma@example.com"))
        .when()
            .put("/users/{id}", userId)
        .then()
            .statusCode(200)
            .body("id", equalTo(userId.intValue()))
            .body("name", equalTo("Priya Sharma"))
            .body("email", equalTo("priya.sharma@example.com"));
    }

    @Test
    public void testDeleteUser() {
        Long userId = createUser("Neha Gupta", "neha.gupta@example.com");

        given()
            .contentType(ContentType.JSON)
        .when()
            .delete("/users/{id}", userId)
        .then()
            .statusCode(204);
    }

    private Long createUser(String name, String email) {
        return given()
            .contentType(ContentType.JSON)
            .body(new User(null, name, email))
        .when()
            .post("/users")
        .then()
            .statusCode(201)
            .extract()
            .path("id");
    }
}

Explanation: This test class uses REST-assured to test the CRUD operations of the UserController

Each test method performs a specific CRUD operation and verifies the response. 

The @SpringBootTest annotation tells Spring Boot to look for a main configuration class and use that to start a Spring application context. 

The @LocalServerPort annotation injects the port number the application is running on, and the @BeforeEach method sets the port for REST-assured.

Step 10: Run the Tests

Run the tests using your IDE or by executing the following command in the terminal:

mvn test

You should see an output indicating that all tests have passed successfully.

Conclusion

In this tutorial, we demonstrated how to use REST-assured to test a Spring Boot application. We created a simple Spring Boot application with CRUD functionality, added REST-assured as a dependency, and wrote tests to validate the API endpoints. 

By following these steps, you can efficiently test your Spring Boot applications and ensure that your REST APIs work as expected. This approach ensures robustness and reliability in your API endpoints, helping you catch issues early in the development process.

Comments