Integration Testing of SpringBoot using RestAssured

HOME

In this tutorial, I am going to build an automation framework to test Springboot application with Rest Assured only.

What is Rest Assured?

REST Assured is a Java DSL for simplifying 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 of these requests.

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.

This framework consists of:

  1. Springboot – 2.5.2
  2. Java 11
  3. JUnit – 4.12
  4. Maven – 3.8.1
  5. RestAssured – 4.3.3

Steps to setup Cucumber Test Automation Framework for API Testing using Rest-Assured

  1. Add SpringbootTest and Rest-Assured dependencies to the project
  2. Create a test file under src/test/java and write the test code
  3. Run the tests from JUnit
  4. Run the tests from Command Line

Below is the sample SpringBoot application used for the testing.

The Spring Boot Application class generated with Spring Initializer. This class acts as the launching point for application.


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


@SpringBootApplication
public class SpringBootRestServiceApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringBootRestServiceApplication.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.

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

@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;
    }

    @DeleteMapping("/students/{id}")
    public void deleteStudent(@PathVariable long id) {
        studentRepository.deleteById(id);
    }

    @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();

    }

    @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();
    }

Steps of Integration Testing of SpringBoot with RestAssured

Step 1 – Add SpringbootTest and Rest-Assured dependencies to the project

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

  <!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>4.3.3</version>
            <scope>test</scope>
        </dependency>

Step 2 – Create a test file under src/test/java and write the test code

@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/10003")
                              .then()
                                    .assertThat().log().all().statusCode(200)
                                    .body("id",equalTo(10003))
                                    .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/10002")
                             .then()
                                    .log().all().assertThat().statusCode(204);

        /* Verify that the updated Student has updated PassportNumber */
        validatableResponse1 = given()
                                     .contentType(ContentType.JSON)
                               .when()
                                     .get("/students/10002")
                               .then()
                                     .log().all().assertThat().statusCode(200)
                                     .body("id",equalTo(10002))
                                     .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 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 enbedded 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 below command to run the tests.

mvn clean verify

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s