Spring Boot 3.0.4 requires Java 17 and is compatible with and including Java 19. Spring Framework 6.0.6 or above is also required.
Explicit build support is provided for the following build tools:
Maven – 3.5+
Gradle – 7.x (7.5 or later) and 8.x
This framework consists of
SpringBoot Starter Parent – 3.1.0
Serenity Rest Assured – 3.6.12
Spring
Java 17
Gradle – 7.6.1
JUnit Jupiter API – 5.9.2
JUnit Jupiter Engine – 5.9.2
Serenity JUnit5 – 3.6.12
What is SpringBoot Application?
Spring Boot is an open-source micro-framework that provides Java developers with a platform to get started with an auto-configurable production-grade Spring application.
Comes with embedded HTTP servers like Tomcat orJetty to test web applications.
Adds many plugins that developers can use to work with embedded and in-memory databases easily. Spring allows you to easily connect with database and queue services like Oracle, PostgreSQL, MySQL, MongoDB, Redis, Solr, ElasticSearch, Rabbit MQ, and others.
Project Directory Structure
Implementation Steps
Create a source folder – src/test/resources to create properties file
AddSpringBootTest, SerenityRest Assured, and Serenity-JUnit5 dependencies to the project
Create the Test classes.
Create an application.properties file in src/test/resources
Run the tests from JUnit5
Run the tests from Command Line
Serenity Report Generation
Step 1 – Create a source folder – src/test/resources to create test scenarios in the Feature file
Right-click on the test directory and select New->Directory and select resources (Maven Source Directories).
Step 2 – Add SpringBootTest,Rest Assured, and allure dependencies to the project
We have added SpringBootTest, SpringBoot Web, Tomcat, Spring Web, Rest Assured, and Serenity-JUnit5 dependencies to the build.gradle.
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0-SNAPSHOT'
id 'io.spring.dependency-management' version '1.1.0'
id 'net.serenity-bdd.serenity-gradle-plugin' version '3.6.7'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-tomcat'
implementation 'org.springframework:spring-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'net.serenity-bdd:serenity-junit5:3.6.12'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
testImplementation 'net.serenity-bdd:serenity-core:3.6.12'
testImplementation 'net.serenity-bdd:serenity-rest-assured:3.6.12'
testImplementation 'net.serenity-bdd:serenity-spring:3.6.12'
}
tasks.named('test') {
useJUnitPlatform() {}
testLogging {
showStandardStreams = true
}
systemProperties System.getProperties()
}
gradle.startParameter.continueOnFailure = true
test.finalizedBy(aggregate)
Step 3 – Create the Test classes
uses @SpringBootTest annotation which loads the actual application context.
uses WebEnvironment.RANDOM_PORT to create and run the application at some random server port.
@LocalServerPort gets the reference of the port where the server has started. It helps in building the actual request URIs to mimic real client interactions.
Below is the Test Class, created in the src/test/java directory.
import io.restassured.response.ValidatableResponse;
import net.serenitybdd.junit5.SerenityJUnit5Extension;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
@ExtendWith(SerenityJUnit5Extension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringBootDemoDefinitions {
private final static String BASE_URI = "http://localhost:";
@LocalServerPort
private int port;
@Value("${server.servlet.context-path}")
private String basePath;
private ValidatableResponse response;
@Test
public void verifyController1() throws Exception {
response = given().contentType("application/json")
.header("Content-Type", "application/json")
.when().get(BASE_URI + port + basePath+ "/").then().statusCode(200);
String Actual = response.extract().asString();
System.out.println("Result :"+Actual);
Assertions.assertEquals("Hello World, Spring Boot!", Actual);
}
@Test
public void verifyController2() throws Exception {
response = given().contentType("application/json")
.header("Content-Type", "application/json")
.when().get(BASE_URI + port + basePath+ "/qaautomation").then().statusCode(200);
String Actual = response.extract().asString();
System.out.println("Result :"+Actual);
Assertions.assertEquals("Hello QA Automation!", Actual);
}
}
This class sends the request and receives a response after performing the GET operation. Here, the validation of the response also takes place by asserting the expected and actual response
Step 4 – Create an application.properties file in src/test/resources
Application.properties is created under src/ test/java.
spring.profiles.active – property to specify which profiles are active. The default profile is always active. server.port – By default, the embedded server starts on port 8080. Now the server will start on port 8090 server.servlet.context-path – the context path in Spring Boot can be changed by setting a property, server.servlet.context-path.
Step 5 – Run the tests from JUnit5
Right-click on the Test class and select Run ‘SpringBoot_Tests’.
The output of the above program is
This image shows that the profile name is “test”. Application is started on port – “65221” and the context path is “/demo”.
Step 6 – Run the tests from Command Line
Run the tests from the command line by using the below command
gradle clean test
The output of the above program is
Step 7 – Serenity Report Generation
The best part about Serenity is the report generation by it. The Reports contain all possible type of information, you can think of with minimal extra effort. There is multiple types of reports are generated. We are interested in index.html .
Below is the new Serenity Report.
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
Spring Boot is an open-source micro-framework that provides Java developers with a platform to get started with an auto-configurable production-grade Spring application.
Comes with embedded HTTP servers like Tomcat orJetty to test web applications.
Adds many plugins that developers can use to work with embedded and in-memory databases easily. Spring allows you to easily connect with database and queue services like Oracle, PostgreSQL, MySQL, MongoDB, Redis, Solr, ElasticSearch, Rabbit MQ, and others.
Project Directory Structure
What is RestController?
HTTP requests are handled by a controller in Spring’s approach to building RESTful web services. The @RestController annotation identifies these components, and the GreetingController shown below (from src/main/java/com/example/springboot_demo/HelloController.java) handles GET requests for / and /qaautomation by returning a new instance of the Greeting class. Spring RestController takes care of mapping request data to the request-defined handles method.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping(path="/")
String hello() {
return "Hello World, Spring Boot!";
}
@GetMapping(path="/qaautomation")
String qaautomation() {
return "Hello QA Automation!";
}
}
Implementation Steps
Create a source folder – src/test/resources to create properties file
AddSpringBootTest, Serenity and JUnit5 dependencies to the project
Create the Test and Helper classes.
Create an application.properties file in src/test/resources
Create serenity.properties at the root of the project
Run the tests from JUnit5
Run the tests from Command Line
Serenity Report Generation
Step 1 – Create a source folder – src/test/resources to create test scenarios in the Feature file
Right-click on the test directory and select New->Directory and select resources (Maven Source Directories).
Step 2 – Add SpringBootTest, Serenity, and JUnit5 dependencies to the project
We have added SpringBootTest, Serenity, Rest Assured, and JUnit5 dependencies to pom.xml.
uses @SpringBootTest annotation which loads the actual application context.
uses WebEnvironment.RANDOM_PORT to create and run the application at some random server port.
@LocalServerPort gets the reference of the port where the server has started. It helps in building the actual request URIs to mimic real client interactions.
Below is the code of the StepDefinition and Helper class. These classes are created in the src/test/java directory.
This class sends the request and receives a response after performing the GET operation. Here, the validation of the response also takes place by asserting the expected and actual response
Step 4 – Create an application.properties file in src/test/resources
Application.properties is created under src/test/resources for the test profile. If you want to run the SpringBootApplication from DEV profile, then create application.properties file in src/main/resources.
spring.profiles.active – property to specify which profiles are active. The default profile is always active. server.port – By default, the embedded server starts on port 8080. Now the server will start on port 8090 server.servlet.context-path – the context path in Spring Boot can be changed by setting a property, server.servlet.context-path.
Step 5 – Create serenity.properties at the root of the project
serenity.project.name = Testing of SpringBoot Application with Serenity and JUnit5 Demo
Step 6 – Run the tests from JUnit5
Right-click on the Test class and select Run ‘SpringBootDemoApplicationTests’.
The output of the above program is
This image shows that the profile name is “test”. Application is started on port – “58458” and the context path is “/demo”.
Step 7 – Run the tests from Command Line
Run the tests from the command line by using the below command
mvn clean verify
The output of the above program is
Step 8 – Serenity Report Generation
The serenity test reports are generated under target/site/serenity.
Below is the sample Index.html Report.
Go to Test Results, present at the top left of the index.html page.
Spring Boot 3.0.4 requires Java 17 and is compatible with and including Java 19. Spring Framework 6.0.6 or above is also required.
Explicit build support is provided for the following build tools:
Maven – 3.5+
Gradle – 7.x (7.5 or later) and 8.x
This framework consists of
SpringBoot Starter Parent – 3.1.0
Serenity – 3.6.12
Serenity Cucumber – 3.6.12
Serenity JUnit4 – 3.6.12
Serenity Rest Assured – 3.6.12
Spring
Java 17
Gradle – 7.6.1
What is SpringBoot Application?
Spring Boot is an open-source micro-framework that provides Java developers with a platform to get started with an auto-configurable production-grade Spring application.
Comes with embedded HTTP servers like Tomcat orJetty to test web applications.
Adds many plugins that developers can use to work with embedded and in-memory databases easily. Spring allows you to easily connect with database and queue services like Oracle, PostgreSQL, MySQL, MongoDB, Redis, Solr, ElasticSearch, Rabbit MQ, and others.
What is RestController?
HTTP requests are handled by a controller in Spring’s approach to building RESTful web services. The @RestController annotation identifies these components, and the HelloController shown below (from src/main/java/com/example/springboot_demo/HelloController.java) handles GET requests for / and /qaautomation by returning a new instance of the Greeting class. Spring RestController takes care of mapping request data to the request-defined handles method.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping(path="/")
String hello() {
return "Hello World, Spring Boot!";
}
@GetMapping(path="/qaautomation")
String qaautomation() {
return "Hello QA Automation!";
}
}
Project Directory Structure
Implementation Steps
Create a source folder – src/test/resources to create properties file
AddSpringBootTest,Rest Assured, and JUnit4 dependencies to the project
Create a feature file in src/test/resources
Create the StepDefinition and Helper classes.
Create a Serenity Runner class in the src/test/java directory
Create an application.properties file in src/test/resources
Create a serenity.properties at the root level of the project
Run the tests from Command Line
Serenity Report Generation
CucumberReport Generation
Step 1 – Create a source folder – src/test/resources to create test scenarios in the Feature file
Right-click on the test directory and select New->Directory and select resources (Maven Source Directories).
Step 2 – Add SpringBootTest,Rest Assured, and other dependencies to the project
We have added SpringBootTest, SpringBoot Web, Tomcat, Spring Web, Rest Assured, and JUnit4 dependencies to the build.gradle.
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0-SNAPSHOT'
id 'io.spring.dependency-management' version '1.1.0'
id "net.serenity-bdd.serenity-gradle-plugin" version "3.6.7"
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-tomcat'
implementation 'org.springframework:spring-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'net.serenity-bdd:serenity-core:3.6.12'
testImplementation 'net.serenity-bdd:serenity-cucumber:3.6.12'
testImplementation 'net.serenity-bdd:serenity-rest-assured:3.6.12'
testImplementation 'net.serenity-bdd:serenity-spring:3.6.12'
testImplementation 'net.serenity-bdd:serenity-junit:3.6.12'
testImplementation 'org.junit.vintage:junit-vintage-engine'
}
tasks.named('test') {
useJUnit() {}
testLogging {
showStandardStreams = true
}
systemProperties System.getProperties()
}
gradle.startParameter.continueOnFailure = true
test.finalizedBy(aggregate)
Step 3 – Create a feature file in src/test/resources
Below is an example of a feature file that shows a sample test scenario. Feature file should end with .feature. It contains the test scenarios in the form of simple English using the terms Given, When, Then, And.
Feature: SpringBoot Request
@ReceiveCorrectResponse
Scenario Outline: Send a valid Request to get correct response
Given I send a request to the URL "<url>"
Then the response will return "<response>"
Examples:
| url | response |
| / | Hello World, Spring Boot! |
| /qaautomation | Hello QA Automation! |
Step 4 – Create the StepDefinition and Helper classes.
Below is the code of the StepDefinition and Helper class. These classes are created in the src/test/java directory.
uses @SpringBootTest annotation which loads the actual application context.
uses WebEnvironment.RANDOM_PORT to create and run the application at some random server port.
@LocalServerPort gets the reference of the port where the server has started. It helps in building the actual request URIs to mimic real client interactions.
This class sends the request and receives a response after performing the GET operation. Here, the validation of the response also takes place by asserting the expected and actual response
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.restassured.response.Response;
import net.serenitybdd.rest.SerenityRest;
import net.thucydides.core.annotations.Steps;
import org.junit.Assert;
public class SpringBootDemoDefinitions {
@Steps
AbstractRestAssuredHelper helper;
private Response response;
@Given("I send a request to the URL {string}")
public void iSendARequest(String endpoint) throws Exception {
response = helper.getAnonymousRequest()
.header("Content-Type", "application/json").when().get(endpoint);
}
@Then("the response will return {string}")
public void extractResponse(String Expected ) {
SerenityRest.restAssuredThat(response -> response.statusCode(200));
String Actual = response.asString();
System.out.println("Result :"+Actual);
Assert.assertEquals(Expected, Actual);
}
}
Step 5 – Create a Serenity Runner class in the src/test/java directory
We cannot run a Feature file on its own in cucumber-based framework. We need to create a Java class that will run the Feature File. It is the starting point for JUnit to start executing the tests. TestRunner class is created under src/test/java. When you run the tests with serenity, you use the CucumberWithSerenitytest runner.
spring.profiles.active – property to specify which profiles are active. The default profile is always active. server.port – By default, the embedded server starts on port 8080. Now the server will start on port 8090 server.servlet.context-path – the context path in Spring Boot can be changed by setting a property, server.servlet.context-path.
Step 7 – Create a serenity.properties at the root level of the project
serenity.project.name = Testing of Gradle SpringBoot Application with Serenity and JUnit4 Demo
Step 8 – Run the tests from Command Line
Run the tests from the command line by using the below command
gradle clean test
The output of the above program is
This image shows that the profile name is “dev”. Application is started on port – “54462” and the context path is “/demo”.
Step 9 – Serenity Report Generation
By default, the test report generated by Serenity is placed under target/site/serenity/index.html. Below is the sample Serenity Report.
Below is the sample Serenity Report.
Step 10 – CucumberReport Generation
A Cucumber Report can be generated by adding publish=true in SpringRunnerTests as shown in the above example. Click on the link provided in the execution status.
Cucumber Report
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
In the previous tutorial, I explained about Integration Testing of SpringBoot Application with Serenity BDD, Cucumber and JUnit4. This one provides a comprehensive tutorial on integration testing of a SpringBoot application using SpringBoot Test and TestNG. It covers essential topics like SpringBoot application, RestController, prerequisites, dependency list, project directory structure, and detailed test implementation steps.
Spring Boot is an open-source micro-framework that provides Java developers with a platform to get started with an auto-configurable production-grade Spring application.
Comes with embedded HTTP servers like Tomcat orJetty to test web applications.
Adds many plugins that developers can use to work with embedded and in-memory databases easily. Spring allows you to easily connect with database and queue services like Oracle, PostgreSQL, MySQL, MongoDB, Redis, Solr, ElasticSearch, Rabbit MQ, and others.
What is RestController?
HTTP requests are handled by a controller in Spring’s approach to building RESTful web services. The @RestController annotation identifies these components, and the GreetingController shown below (from src/main/java/com/example/HelloController.java) handles GET requests for / and /qaautomation by returning a new instance of the Greeting class. Spring RestController takes care of mapping request data to the request-defined handles method.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping(path="/")
String hello() {
return "Hello World, Spring Boot!";
}
@GetMapping(path="/qaautomation")
String qaautomation() {
return "Hello QA Automation!";
}
}
In this tutorial, I will explain the IntegrationTesting of the SpringBoot Application using SpringBoot Test and TestNG.
Prerequisite
Spring Boot 3.0.4 requires Java 17 and is compatible with and including Java 19. Spring Framework 6.0.6 or above is also required.
Explicit build support is provided for the following build tools:
Maven – 3.5+
Gradle – 7.x (7.5 or later) and 8.x
Dependency List
SpringBoot Starter Parent – 3.2.5
TestNG – 7.10.2
Rest Assured – 5.4.0
Java 17
Maven – 3.9.6
Project Directory Structure
Test Implementation Steps
Step 1 – Create a source folder – src/test/resources
Create a source folder – src/test/resources to properties file in it.
Right-click on the test directory and select New->Directory and select resources (Maven Source Directories).
Step 2 – Add dependencies to the project
We have added SpringBootTest, SpringBoot Tomcat, SpringBoot Web, Spring Web, Rest Assured, and TestNG dependencies to the pom.xml.
uses @SpringBootTest annotation which loads the actual application context.
uses WebEnvironment.RANDOM_PORT to create and run the application at some random server port.
@LocalServerPort gets the reference of the port where the server has started. It helps in building the actual request URIs to mimic real client interactions.
Below is the code of the sample Test class. These classes are created in the src/test/java directory.
import io.restassured.response.ValidatableResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringBootDemoTests extends AbstractTestNGSpringContextTests {
private final static String BASE_URI = "http://localhost:";
@LocalServerPort
private int port;
@Value("${server.servlet.context-path}")
private String basePath;
private ValidatableResponse response;
@Test
public void verifyController1() {
response = given().contentType("application/json")
.header("Content-Type", "application/json")
.when().get(BASE_URI + port + basePath + "/").then().statusCode(200);
String Actual = response.extract().asString();
System.out.println("Result :"+Actual);
Assert.assertEquals("Hello World, Spring Boot!", Actual);
}
@Test
public void verifyController2() {
response = given().contentType("application/json")
.header("Content-Type", "application/json")
.when().get(BASE_URI + port + basePath + "/qaautomation").then().statusCode(200);
String Actual = response.extract().asString();
System.out.println("Result :"+Actual);
Assert.assertEquals("Hello QA Automation!", Actual);
}
}
The AbstractTestNGSpringContextTests is an abstract base class having the ApplicationContext supported in the testNG explicitly.
This class sends the request and receives a response after performing the GET operation. Here, the validation of the response also takes place by asserting the expected and actual response.
Step 4 – Create an application.properties file in src/test/resources
Application.properties is created under src/ test/java.
spring.profiles.active – property to specify which profiles are active. The default profile is always active. server.port – By default, the embedded server starts on port 8080. Now the server will start on port 8089 server.servlet.context-path – the context path in Spring Boot can be changed by setting a property, server.servlet.context-path.
Step 5 – Run the tests from Test Class
Right-click on the Test class and select Run ‘SpringBootDemoTests’.
The output of the above program is
This image shows that the profile name is “test”. Application is started on port – “62954” and the context path is “/demo”.
Step 6 – Run the tests from testng.xml
First, we need to create a testng.xml at the root of the project.
<?xml version = "1.0"encoding = "UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name = "Suite1"> <test name = "TestNG Demo"> <classes> <class name = "com.example.tests.SpringBootDemoTests"/> </classes> </test> </suite>
Right-click on testng.xml and select Run ‘…\testng.xml’.
The output of the above program is
Step 7 – TestNG Report Generation
The test report generated by TestNG is placed under test-output/index.html.
Index.html
TestNG produces an “index.html” report, and it resides under the test-output folder. The below image shows index.html report. This report contains a high-level summary of the tests.
Emailable-Report.html
Test-Output folder also contains Emailable-Report.html. Open “emailable-report.html“, as this is an HTML report open it with the browser. The below image shows emailable-report.html.
Step 8 – Run the tests through Maven Command Line
Starting from SpringBoot 3.1.0 version, we need to add the below dependency to the surefire plugin to run the tests through Maven Command Line.
In this tutorial, I am going to build an automation framework to test theSpringboot application with Cucumber, Rest Assured, and TestNG.
What is Springboot?
Spring Boot is an open-source micro-framework maintained by a company called Pivotal. It provides Java developers with a platform to get started with an auto-configurable production-grade Spring application. With it, developers can get started quickly without losing time on preparing and configuring their Spring application.
What is Cucumber?
Cucumber is a software tool that supports behavior-driven development (BDD). Cucumber can be defined as a testing framework, driven by plain English. It serves as documentation, automated tests, and development aid – all in one.
This framework consists of:
Springboot – 2.5.2
Cucumber – 7.3.4
Java 11
TestNG – 7.3.4
Maven – 3.8.1
RestAssured – 5.1.1
Steps to setup Cucumber Test Automation Framework for API Testing using Rest-Assured
Add SpringbootTest, Rest-Assured, JUnit, and Cucumber dependencies to the project
Create a source folder src/test/resources and create a feature file under src/test/resources
Create the Step Definition class or Glue Code for the Test Scenario under the src/test/java directory
Create a Cucumber Runner class under the src/test/java directory
Run the tests from Cucumber Test Runner
Run the tests from Command Line
Run the tests from TestNG
Generation of TestNG Reports
Cucumber Report Generation
Below is the structure of a SpringBoot application project
We need the below files to create a SpringBoot Application.
SpringBootRestServiceApplication.java
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 SpringBootRestServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootRestServiceApplication.class, args);
}
}
Student.java
This is JPA Entity for Student class
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.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;
}
}
StudentRepository.java
This is JPA Repository for Student. This is created using Spring Data JpaRepository.
Spring Boot automatically loads the application.properties whenever it starts up. You can de-reference values from the property file in the java code through the environment.
spring.jpa.defer-datasource-initialization=true
data.sql
Data is loaded from data.sql into the Student table. Spring Boot would execute this script after the tables are created from the entities.
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');
Test Automation Framework Implementation
Step 1 – Add SpringbootTest, Cucumber, Rest-Assured, and TestNG dependencies to the project (Maven project)
Step 2 – Create a source folder src/test/resources and create a feature file under src/test/resources
By default, the Maven project has an src/test/java directory only. Create a new Source Folder under src/test with the name of resources. Create a folder name as Features within the src/test/resources directory.
Create a feature file to test the Springboot application. Below is a sample feature file.
Feature: Verify springboot application using Cucumber and TestNG
@ReceiveUserDetails
Scenario Outline: Send a valid Request to get user details
Given I send a request to the URL "/students" to get user details
Then The response will return status 200
And The response contains id <studentID> and names "<studentNames>" and passport_no "<studentPassportNo>"
Examples:
|studentID |studentNames |studentPassportNo|
|10001 |Annie |E1234567 |
|20001 |John |A1234568 |
|30001 |David |C1232268 |
|40001 |Amy |D213458 |
@CreateUser
Scenario: Send a valid Request to create a user
Given I send a request to the URL "/students" to create a user with name "Annie" and passportNo "E1234567"
Then The response will return status 201
And Resend the request to the URL "/students" and the response returned contains name "Annie" and passport_no "E1234567"
Step 3 – Create the Step Definition class or Glue Code for the Test Scenario under src/test/java
The corresponding step definition file of the above feature file is shown below.
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import org.json.JSONObject;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.spring.CucumberContextConfiguration;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringbootDefinitions {
private final static String BASE_URI = "http://localhost";
@LocalServerPort
private int port;
private ValidatableResponse validatableResponse, validatableResponse1;
private void configureRestAssured() {
RestAssured.baseURI = BASE_URI;
RestAssured.port = port;
}
protected RequestSpecification requestSpecification() {
configureRestAssured();
return given();
}
@Given("I send a request to the URL {string} to get user details")
public void getStudentDetails(String endpoint) throws Throwable {
validatableResponse = requestSpecification().contentType(ContentType.JSON).when().get(endpoint).then();
System.out.println("RESPONSE :" + validatableResponse.extract().asString());
}
@Given("I send a request to the URL {string} to create a user with name {string} and passportNo {string}")
public void createStudent(String endpoint, String studentName, String studentPassportNumber) throws Throwable {
JSONObject student = new JSONObject();
student.put("name", studentName);
student.put("passportNumber", studentPassportNumber);
validatableResponse = requestSpecification().contentType(ContentType.JSON).body(student.toString()).when()
.post(endpoint).then();
System.out.println("RESPONSE :" + validatableResponse.extract().asString());
}
@Then("The response will return status {int}")
public void verifyStatusCodeResponse(int status) {
validatableResponse.assertThat().statusCode(equalTo(status));
}
@Then("The response contains id {int} and names {string} and passport_no {string}")
public void verifyResponse(int id, String studentName, String passportNo) {
validatableResponse.assertThat().body("id", hasItem(id)).body(containsString(studentName))
.body(containsString(passportNo));
}
@Then("Resend the request to the URL {string} and the response returned contains name {string} and passport_no {string}")
public void verifyNewStudent(String endpoint, String studentName, String passportNo) {
validatableResponse1 = requestSpecification().contentType(ContentType.JSON).when().get(endpoint).then();
System.out.println("RESPONSE :" + validatableResponse1.extract().asString());
validatableResponse1.assertThat().body(containsString(studentName)).body(containsString(passportNo));
}
}
To make Cucumber aware of your test configuration you can annotate a configuration class on your glue path with @CucumberContextConfiguration and with one of the following annotations: @ContextConfiguration, @ContextHierarchy, or @BootstrapWith.It is imported from:
By default, @SpringBootTest does not start the webEnvironment to refine further how your tests run. It has several options: MOCK(default), RANDOM_PORT, DEFINED_PORT, NONE.
RANDOM_PORT loads a WebServerApplicationContext and provides a real web environment. The embedded server is started and listens on a random port. LocalServerPort is imported from the package:
Step 4 – Create a Cucumber TestNG Runner class under src/test/java
A runner will help us to run the feature file and acts as an interlink between the feature file and StepDefinition Class. The TestRunner should be created within the directory src/test/java.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(features = {"src/test/resources/Features"}, glue = {"com.example.demo.definitions"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
The @CucumberOptions annotation is responsible for pointing to the right feature package, configuring the plugin for a better reporting of tests in the console output, and specifying the package where extra glue classes may be found. We use it to load configuration and classes that are shared between tests.
Step 5 – Run the tests from Cucumber Test Runner
You can execute the test script by right-clicking onTestRunner class -> Run As TestNG in Eclipse.
In case you are using IntelliJ, select “Run CucumberRunnerTests“.
SpringBootTest creates an application context containing all the objects we need for the Integration Testing It, starts the embedded server, creates a web environment, and then enables methods to do Integration testing.
Step 6 – Run the tests from Command Line
Use the below command to run the tests through the command line.
mvn clean test
Step 7 – Run the tests from TestNG
Create a testng.xml in the project as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name = "Suite1">
<test name = "SpringBoot Cucumber TestNG Demo">
<classes>
<class name = "com.example.demo.runner.CucumberRunnerTests"/>
</classes>
</test>
</suite>
Step 8 – Generation of TestNG Reports
TestNG generates various types of reports underthe test-output folder like emailable-report.html, index.html, testng-results.xml.
We are interested in the “emailable-report.html” report. Open “emailable-report.html”, as this is an HTML report, and open it with the browser. The below image shows emailable-report.html.
TestNG also produce “index.html” report, and it resides under test-output folder. The below image shows index.html report.
Step 9 – Cucumber Report Generation
Add cucumber.properties under src/test/resources and add the below instruction in the file.
cucumber.publish.enabled=true
The link to the Cucumber Report is present in the execution status.
Below is the image of the Cucumber Report generated using the Cucumber Service.
Complete Source Code: Refer to GitHub for the source code.
Congratulations!! We are able to build a test framework to test the SpringBoot application using Cucumber, Rest Assured, and TestNG.
This tutorial describes how to create and run a Spring application in IntelliJ IDEA. For this purpose, we need IntelliJ IDEA Ultimate Version. The Ultimate edition is commercial version (which has trial version for 30 days post which you needs license).
IntelliJ Ultimate will create a Spring Boot Maven project generated by Spring Initializr. This is the quickest way to create a Spring application, and IntelliJ IDEA provides a dedicated project wizard for it.
Steps to create a new Spring Boot project
Step 1 – From the main menu, select File -> New -> Project.
Step 2 – In the left pane of the New Project wizard, select Spring Initializr.
From the Project SDK list, select the JDK that you want to use in the project. I have used – 11 Amazon Corretto version 11.0.10.
If the JDK is installed on your computer, but not defined in the IDE, select Add JDK and specify the path to the JDK home directory.
If you don’t have the necessary JDK on your computer, please Download JDK from here.
To check if you have Java installed on your machine or not, please use the below command in command prompt.
java -version
This shows that Java 11 is already installed on my machine.
Step 4 – Mention the Group and Artifact name. Other Information is auto populated. Change them, if you want something different than already mentioned. Click the Nextbutton.
Step 5 – Select the Spring Web dependency under Web and click the Next button. I have used this combination because I want to create a RESTful application using Apache Tomcat.
Step 6 – A new window appears where mention the location where you want to save the new project. I have created a folder springbootdemo and save the project files in that folder.
Step 7 – Below is the structure of new project on local machine
Step 8 – This is how the project looks in IntelliJ.
Spring Initializr generates a valid project structure with the following files:
A build configuration file – pom.xml for Maven.
A class with the main() method to bootstrap the application – SpringbootdemoApplication.
An empty JUnit test class – SpringbootdemoApplicationTests.
An empty Spring application configuration file – application.properties.
Step 9 – To run the application, Right click on the SpringbootdemoApplication.java class and select Run SpringbootdemoApplication
The springbootdemoApplication is started and this is the image of the execution screen.
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.
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
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 @SpringBootTestannotation which starts up an Application Contextused 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 Spring framework enables automatic dependency injection. In other words, by declaring all the bean dependencies in a Spring configuration file, Spring container can autowire relationships between collaborating beans. This is called Spring bean Autowiring.
Now, let me explain this in simple language. Autowired provide us the object initialization.
Suppose, I have a Controller class called as UserController and a helper class called UserDaoService where I can write all the methods needed for UserController.
Now, I’ll annotate the helper class UserDaoService as @Component. I have created 3 methods in the helper class UserDaoService which are findAll(), save() and getOne().
Using findAll(), I will get data of all the users and in save() I will add the user in the list and findOne() will find a particular user from the list.
Objects in Spring Container is called Beans. When a class is annotated as @Component, it will create an object (bean) for that class. Here, in our example when we have annotated UserDaoService as @component , so at the time of application is in run mode it will create a bean for UserDaoService class in spring container.
In our controller class (UserController) there is dependency for Helper class (UserDaoService), so how our controller class beans know that there is helper class bean present in container. We need to told controller class bean to find out helper class bean and use it, so here @Autowired comes in picture. It will start to find bean for helper class and inject it into that variable so your variable in Initialized and ready to use.
Controller Class (UserResource)
@RestController
public class UserController
{
@Autowired
private UserDaoService service;
@GetMapping("/users")
public List<User> retriveAllUsers()
{
return service.findAll();
}
}
Helper Class (UserDaoService)
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.stereotype.Component;
@Component
public class UserDaoService
{
public static int usersCount=5;
//creating an instance of ArrayList
private static List<User> users=new ArrayList<>();
//static block
static
{
//adding users to the list
users.add(new User(1, "John", new Date()));
users.add(new User(2, "Tom", new Date()));
users.add(new User(3, "Perry", new Date()));
}
//method that retrieve all users from the list
public List<User> findAll()
{
return users;
}
//method that add the user in the list
public User save(User user)
{
if(user.getId()==null)
{
//increments the user id
user.setId(++usersCount);
}
users.add(user);
return user;
}
//method that find a particular user from the list
public User findOne(int id)
{
for(User user:users)
{
if(user.getId()==id)
return user;
}
return null;
}
}
In the above example, UserDaoService is Autowired in UserController class using by property. Using Autowired, I have called method findAll().
@Autowired
private UserDaoService service;
Use of @Autowired in Integration Testing of SpringBoot Application
Lets imagine a condition where different tests need to have a common test step. Suppose I have created 2 feature files where 1 feature file contain the tests related to valid Request/Response and another feature file create test scenarios related to various service errors. In both the cases, a test step where we have to send an access token to the requests is common. If I create this step in both StepDefinition files, it is duplicacy of code. To avoid this situation, we can create a Helper Class name as CommonStepDefinitions and declare it as @Componenet and @Autowired this helper class to specific StepDefinition class.
In the below example, I have created an access Token and I need to pass this access Token to the SpringBoot request to proceed further. I have @Autowired the CommonDefinition class to ValidRequestDefinitions and get the value of access token here and pass it to the request.
CommonStepDefinitions.class
@Component
@SpringBootTest
public class CommonStepDefinitions {
@Given("^I generate an auth token to pass bearer token to request$")
public String generateToken() throws JSONException {
validatableResponse = given().auth().basic(username, password).param("grant_type", grant_type).when()
.post(authUrl).then();
JSONObject token = new JSONObject(validatableResponse.extract().asString());
accessToken = token.get("access_token").toString();
return accessToken;
}
Main StepDefinition.class (ValidRequestDefinitions)
@SpringBootTest
public class ValidRequestDefinitions{
private ValidatableResponse validatableResponse;
private String bearerToken ;
//Autowired on Property
@Autowired
CommonStepDefinitions helper;
@When("^I send a valid request to the URL (.*)$")
public void sendRequestToGenerateProcess(String endpoint) {
bearerToken = helper.generateToken();
validatableResponse = given().header("Authorization", "Bearer " + bearerToken).contentType(ContentType.JSON)
.body(toString()).when().get(endpoint).then();
}
That’s it! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
WireMock is a library for stubbing and mocking web services. It constructs a HTTP server that act as an actual web service. When a WireMock server is in action, we have the option to set up expectations, call the service, and then verify the behavior of service. It allows us to:-
Capture the incoming HTTP requests and write assertions for the captured HTTP requests.
Configure the response returned by the HTTP API when it receives a specific request.
Identify the stubbed and/or captured HTTP requests by using request matching.
Configure request matchers by comparing the request URL, request method, request headers, cookies, and request body with the expected values.
Use WireMock as a library or run it as a standalone process.
Why do we need to Wiremock a SpringBoot Application
Suppose your SpringBoot Application calls an external application and this call to external service bear some cost. You can’t test the application without calling the external service, So in this case, we can use WireMock to mock the external application while testing the REST service that you are developing in SpringBoot.
Spring Cloud Contract WireMock
The Spring Cloud Contract WireMock modules allow us to use WireMock in a Spring Boot application.
To use WireMock in SpringBoot, add Maven dependency
We can use JUnit @Rules to start and stop the server. To do so, use the WireMockRule convenience class to obtain optionsinstance, as the following example shows:
@Rule
public WireMockRule wireMockRule = new WireMockRule(options().port(8081));
Wiremock runs as a stub server, and you can register stub behavior by using a Java API or by using static JSON declarations as part of your test.
Let us take the example of Student SpringBoot Application. The Response JSON of Student is shown below.
Creating a first WireMock mock service
Let us create a simple wiremock service with return status code as 200 and statsus message as “OK”.
@SpringBootTest
public class Wiremock_Example {
@Rule
public WireMockRule wireMockRule = new WireMockRule(options().port(8081));
@Test
public void test() {
stubFor(get(urlPathEqualTo("/students"))
.willReturn(aResponse().withHeader("Content-Type", "application/json")
.withStatus(200).withStatusMessage("OK")));
}
}
options is imported from package static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
@Rule & @Test are from junit
WireMockRule is from com.github.tomakehurst.wiremock.junit.WireMockRule
The following code will configure a response with a status of 200 and status message of “OK” to be returned when the relative URL exactly matches /students (including query parameters). The body of the response will be “Tom” and Content-Type header will be sent with a value of application/json.
To create the stub described above via the JSON API, the following document can either be posted tohttp://<host>:<port>/__admin/mappings or placed in a file with a .json extension under the mappings directory:
Dynamic port numbers
Wiremock has the facility to pick free HTTP and HTTPS ports in SpringBoot application, which is a good idea if we need to run the applications concurrently.
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().dynamicPort());
Below is an example which shows how you can mock an applictaion on dynamic ports. In the below example, the localhost port is dynamic here (57099).
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class Wiremock_Example {
@Rule
public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort());
@Test
public void test() {
stubFor(get(urlPathEqualTo("/students")).willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withStatus(200).withStatusMessage("OK").withBody("Tom")));
}
}
How to run and test Mock Service?
In the below example, I will use Rest Assured tests to validate the behaviour of Mock Service. First, I need to get the mock service up and running and then use it in a test.
@SpringBootTest
public class Wiremock_Example {
@Rule
public WireMockRule wireMockRule = new WireMockRule(options().port(8089));
public void setupMockService() {
wireMockRule.stubFor(get(urlPathEqualTo("/students"))
.willReturn(aResponse().withHeader("Content-Type", "application/json")
.withStatus(200).withStatusMessage("OK")));
}
@Test
public void positiveTest() {
setupMockService();
given()
.when()
.get("http://localhost:8089/students")
.then()
.assertThat().statusCode(200);
}
}
In the below example, it is shown that the response content of a wiremock service can also be verified.
@SpringBootTest
public class Wiremock_Example {
@Rule
public WireMockRule wireMockRule = new WireMockRule(options().port(8090));
public void setupMockService() {
wireMockRule.stubFor(get(urlPathEqualTo("/students"))
.willReturn(aResponse().withHeader("Content-Type", "application/json")
.withStatus(200).withStatusMessage("OK").withBody("Tom")));
}
@Test
public void responseMessageTest() {
setupMockService();
given()
.when()
.get("http://localhost:8090/students")
.then()
.assertThat().body(containsString("Tom"));
}
}
How to read response body from a file?
To read the body content from a file, place the file under the __files directory under src/test/resources when running from the JUnit rule.To make your stub use the file, use withBodyFile() on the response builder with the file’s path relative to __files.
public class Wiremock_Example {
@Rule
public WireMockRule wireMockRule = new WireMockRule(options().port(8085));
public void setupMockService() {
wireMockRule.stubFor(get(urlPathEqualTo("/students"))
.willReturn(aResponse().withHeader("Content-Type", "application/json") .withStatus(200).withStatusMessage("OK").withBodyFile("Response.json")));
}
@Test
public void responseFileTest() {
setupMockService();
given()
.when()
.get("http://localhost:8085/students").then()
.assertThat()
.body(containsString("John"));
}
}
To read more about SpringBoot Wiremock, you can refer SpringBoot Wiremock website.
In the previous tutorial, I explained about the Testing of SpringBoot PUT Method. In this tutorial, I will discuss about the Testing of DELETE method to delete a Resource in SpringBoot application.
The structure of project and various Java classes present in SpringBoot application and the dependencies need in POM.xml to test springboot framework are mentioned here.
Below is the code of Student Controller which exposes the services of DELETE method of Student resource.
@DeleteMapping("/students/{id}")
@DeleteMapping annotation maps HTTP DELETE requests onto specific handler methods. It is a composed annotation that acts as a shortcut for @RequestMapping(method=RequestMethod.DELETE) .
Code of StudentController.java for DELETE method is below
@DeleteMapping("/students/{id}")
public void deleteStudent(@PathVariable long id) {
studentRepository.deleteById(id);
}
Here, we are deleting the student resource by Id.
Scenario 1- Below picture shows how we can execute a sucessful DELETE Request Method using Postman
Before Deleting a Student Resource with Id 1001
In the below image, it shows all the details of student with Id 1001 and with status code of 201 is returned.
Now, we delete a Student with Id 1001 and the status code returned is 200.
After deleting the resource, again send a request to get the details of student of id 1001 which returns 404 – Not Found status.
Above scenario can be tested in the below way.
Feature: Delete Student Request
@DeleteStudent
Scenario: Send a valid Request to delete a student
Given I send a request to the URL "/students/1001" to get the detail of user with Id 1001
When I send a request to the URL "/students/1001" to delete user
Then the response will return status of 200
And I resend the request to the URL "/students/1001" to get status of 404
Test Code to test above scenario (StepDefinition file)
@SpringBootTest(classes = SpringBoot2RestServiceApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class DeleteStudentsDefinition {
private final static String BASE_URI = "http://localhost";
@LocalServerPort
private int port;
private ValidatableResponse validatableResponse1, validatableResponse2, validatableResponse3;
private void configureRestAssured() {
RestAssured.baseURI = BASE_URI;
RestAssured.port = port;
}
protected RequestSpecification getAnonymousRequest() throws NoSuchAlgorithmException {
configureRestAssured();
return given();
}
@Given("^I send a request to the URL \"([^\"]*)\" to get the detail of user with Id 1001$")
public void getRequest(String endpoint) throws Throwable {
validatableResponse1 = getAnonymousRequest().contentType(ContentType.JSON).body(toString()).when().get(endpoint)
.then();
}
@When("^I send a request to the URL \"([^\"]*)\" to delete user$")
public void iSendARequest(String endpoint) throws Throwable {
validatableResponse2 = getAnonymousRequest().contentType(ContentType.JSON).when().delete(endpoint).then();
}
@Then("^the response will return status of (\\d+)$")
public void extractResponseOfValidStudent(int status) throws NoSuchAlgorithmException {
validatableResponse2.assertThat().statusCode(equalTo(status));
}
@And("^I resend the request to the URL \"([^\"]*)\" to get status of (\\d+)$")
public void reverifyStudent(String endpoint, int status) throws NoSuchAlgorithmException {
validatableResponse3 = getAnonymousRequest().contentType(ContentType.JSON).body(toString()).when().get(endpoint)
.then();
validatableResponse3.assertThat().statusCode(equalTo(status));
}
}
We can test the negative scenario similarly. Send a request with invalid student id, then we will get 500 Internal Server Error.