Last Updated On
In this tutorial, I am going to build an automation framework to test Springboot application with Rest Assured and JUnit4 only.
Table Of Contents
Project Structure

What is Rest Assured?
REST Assured is a Java DSL for simplifying the testing of REST-based services built on top of HTTP Builder. It supports POST, GET, PUT, DELETE, OPTIONS, PATCH, and HEAD requests and can be used to validate and verify the response to these requests.
The rest-Assured library also provides the ability to validate the HTTP Responses received from the server. For e.g. we can verify the Status code, Status message, Headers, and even the Body of the response. This makes Rest-Assured a very flexible library that can be used for testing.
Dependency List:
- Springboot – 3.2.3
- Java 17
- JUnit – 4.13.2
- Maven – 3.9.6
- RestAssured – 5.3.2
- Junit Vintage
Sample SpringBoot Application
Below is the sample SpringBoot application used for the testing.
The Spring Boot Application class is generated with Spring Initializer. This class acts as the launching point for the application.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
The JPA Entity is any Java POJO, which can represent the underlying table structure. As our service is based on the Student table, we will create a Student Entity object.
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Entity
public class Student {
@Id
@GeneratedValue
private Long id;
@NotNull
@Size(min = 4, message = "Name should have atleast 4 characters")
private String name;
@NotBlank(message = "passportNumber is mandatory")
private String passportNumber;
public Student() {
super();
}
public Student(Long id, String name, String passportNumber) {
super();
this.id = id;
this.name = name;
this.passportNumber = passportNumber;
}
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 getPassportNumber() {
return passportNumber;
}
public void setPassportNumber(String passportNumber) {
this.passportNumber = passportNumber;
}
}
The Repository represents the DAO layer, which typically does all the database operations. Thanks to Spring Data, who provides the implementations for these methods. Let’s have a look at our StudentRepository, which extends the JpaRepository. There are no method declarations here in the StudentRepository. That is because Spring Data’s JpaRepository has already declared basic CRUD methods.
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface StudentRepository extends JpaRepository<Student, Long>{
}
Spring Rest Controller exposes all services on the student resource. RestController used for the below example is shown below.
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@RestController
public class StudentController {
@Autowired
private StudentRepository studentRepository;
@GetMapping("/students")
public List<Student> retrieveAllStudents() {
return studentRepository.findAll();
}
@GetMapping("/students/{id}")
public EntityModel<Student> retrieveStudent(@PathVariable long id) {
Optional<Student> student = studentRepository.findById(id);
if (!student.isPresent())
throw new StudentNotFoundException("id-" + id);
EntityModel<Student> resource = EntityModel.of(student.get());
WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllStudents());
resource.add(linkTo.withRel("all-students"));
return resource;
}
@PostMapping("/students")
public ResponseEntity<Object> createStudent(@Valid @RequestBody Student student) {
Student savedStudent = studentRepository.save(student);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(savedStudent.getId()).toUri();
return ResponseEntity.created(location).build();
}
@DeleteMapping("/students/{id}")
public void deleteStudent(@PathVariable long id) {
studentRepository.deleteById(id);
}
@PutMapping("/students/{id}")
public ResponseEntity<Object> updateStudent(@Valid @RequestBody Student student, @PathVariable long id) {
Optional<Student> studentOptional = studentRepository.findById(id);
if (!studentOptional.isPresent())
return ResponseEntity.notFound().build();
student.setId(id);
studentRepository.save(student);
return ResponseEntity.noContent().build();
}
}
StudentNotFoundException
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class StudentNotFoundException extends RuntimeException {
public StudentNotFoundException(String exception) {
super(exception);
}
}
application.properties
spring.jpa.defer-datasource-initialization=true
data.sql
insert into student values(10001,'Annie', 'E1234567');
insert into student values(20001,'John', 'A1234568');
insert into student values(30001,'David','C1232268');
insert into student values(40001,'Amy','D213458');
Implementation Steps
Step 1 – Add SpringbootTest and Rest-Assured dependencies to the project
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBoot_Demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<junit.version>4.13.2</junit.version>
<rest-assured.version>5.3.2</rest-assured.version>
<maven.compiler.plugin.version>3.12.1</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.2.3</maven.surefire.plugin.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<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-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.plugin.version}</version>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 2 – Create a test file under src/test/java and write the test code
package org.example;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringbootDemoTests {
private final static String BASE_URI = "http://localhost";
@LocalServerPort
private int port;
private ValidatableResponse validatableResponse;
private ValidatableResponse validatableResponse1;
@Before
public void configureRestAssured() {
RestAssured.baseURI = BASE_URI;
RestAssured.port = port;
}
/* Get operation - Get the details of a Student */
@Test
public void listUsers() {
validatableResponse = given()
.contentType(ContentType.JSON)
.when()
.get("/students")
.then()
.assertThat().statusCode(200);
}
/* Get operation - Get the details of a Student */
@Test
public void listAUser() {
validatableResponse = given()
.contentType(ContentType.JSON)
.when()
.get("/students/30001")
.then()
.assertThat().log().all().statusCode(200)
.body("id",equalTo(30001))
.body("name",equalTo("David"))
.body("passportNumber",equalTo("C1232268"));;
}
/* Create operation - Create a new Student */
@Test
public void createAUser() throws JSONException {
JSONObject newStudent = new JSONObject();
newStudent.put("name", "Timmy");
newStudent.put("passportNumber", "ZZZ12345");
validatableResponse = given()
.contentType(ContentType.JSON).body(newStudent.toString())
.when()
.post("/students")
.then()
.log().all().assertThat().statusCode(201);
/* Verify that a new Student is created */
validatableResponse1 = given()
.contentType(ContentType.JSON)
.when()
.get("/students/1")
.then()
.log().all().assertThat().statusCode(200)
.body("id",equalTo(1))
.body("name",equalTo("Timmy"))
.body("passportNumber",equalTo("ZZZ12345"));
}
/* Update operation - Update PassportNumber of a Student */
@Test
public void updateAUser() throws JSONException {
JSONObject newStudent = new JSONObject();
newStudent.put("name", "John");
newStudent.put("passportNumber", "YYYY1234");
validatableResponse = given()
.contentType(ContentType.JSON).body(newStudent.toString())
.when()
.put("/students/20001")
.then()
.log().all().assertThat().statusCode(204);
/* Verify that the updated Student has updated PassportNumber */
validatableResponse1 = given()
.contentType(ContentType.JSON)
.when()
.get("/students/20001")
.then()
.log().all().assertThat().statusCode(200)
.body("id",equalTo(20001))
.body("name",equalTo("John"))
.body("passportNumber",equalTo("YYYY1234"));
}
/* Delete operation - Delete a Student */
@Test
public void deleteAUser() throws JSONException {
validatableResponse = given()
.contentType(ContentType.JSON)
.when()
.delete("/students/10003")
.then()
.log().all().assertThat().statusCode(200);
/* Verify that the deleted Student Request returns STATUS 404 */
validatableResponse1 = given()
.contentType(ContentType.JSON)
.when()
.get("/students/10003")
.then()
.log().all().assertThat().statusCode(404);
}
}
When a class is annotated with @RunWith or extends a class annotated with @RunWith, JUnit will invoke the class it references to run the tests in that class instead of the runner built into JUnit.
SpringRunner is an alias for the SpringJUnit4ClassRunner. Here, we have simply annotated a JUnit 4-based test class with @RunWith(SpringRunner.class). The Spring TestContext Framework provides generic, annotation-driven unit and integration testing support that is agnostic of the testing framework in use (JUnit, TestNG).
We build the test class with @SpringBootTest annotation which starts up an Application Context used throughout our test. In the classes property of @SpringBootTest annotation, we can specify which configuration classes build our Application Context. By default, @SpringBootTest annotation does not provide any web environment.
In order to set up a test web server we need to use @SpringBootTest’s webEnvironment annotation.
There are a few modes in which the web server can be started.
- RANDOM_PORT – this is a recommended option where a real, embedded web server starts on a random port
- DEFINED_PORT – web server will start on an 8080 or a port defined in application.properties
- MOCK – loads a mock web environment where embedded servers are not started up.
Step 3 – Run the tests from JUnit
Right-click Run as JUnit Tests (Eclipse)
Right Click and select Run SpringBootDemoTests (IntelliJ)

Step 4 – Run the tests from Command Line
Open a command prompt and use the below command to run the tests.
mvn clean test
To run the tests from the command line, we need to add junit-vintage-engine dependency. Starting with Spring Boot 2.4, JUnit 5’s vintage engine has been removed from the spring-boot-starter-test.

The complete code can be found on GitHub.
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!