Show List
Test Rest API Using JUnit Cucumber Gherkins Rest Assured and Gradle
In this tutorial, we are going to test rest API from the previous chapter using Cucumber framework, Gherkins and Rest Assured.
Cucumber is a testing framework that follows BDD (Behavior driven development). It allows software behavior to be written in English text called Gherkins. It is defined as scenarios of in a Given condition When action happens Then expected outcome.
REST Assured is a Java library used for API testing. It helps reduce lot of boilerplate code required for making connection, sending and receiving requests. It also supports Given, When, Then notations.
In this example we will see how to write the tests in Cucumber framework, run them using JUnit, Gradle task and to prepare Cucumber test report.
Here is the project structure. Files under src/main/java are to create the REST API as discussed in the previous chapter. You can download the source code from there to code along. We are only going to add the files under test folder and dependencies to test the API.
Here is a quick view of different endpoints the developed REST API supports. For this demo we are only going to write the scripts to test the GET operation on /student/{id} end point.
Add Gradle Dependencies
Add below dependencies in build.gradle file to add support for Cucumber and REST Assured. See the build.gradle full code below for reference.
testImplementation 'io.cucumber:cucumber-java:6.8.1'
testImplementation 'io.cucumber:cucumber-junit:6.8.1'
testImplementation 'io.rest-assured:rest-assured:4.3.3'
Feature File
As we saw earlier Cucumber supports writing the test scenarios in English like language (Gherkins). Here is the feature file that documents the scenario for the test.
It is quite self explanatory that as part of this test a valid request will be sent to get the student details then expectation is that correct values are returned in the response. Example section is used to dynamically supply the values to the Given/When/Then steps.
APITest.feature
Feature: Validation of get method
@GetStudentDetails
Scenario Outline: Send a valid Request to get student details
Given I send a request to the URL to get student details
Then the response will return status "<rt_status>" and id <id> and name "<st_name>" and grade "<st_grade>"
Examples:
|id |st_name |st_grade |rt_status|
|1 |Ana |One |200|
Step Definition
The code for each of the Given, When, Then step is provided in the step definition file. Here we are using REST assured to make the call to the API and evaluate the response.
API_StepDefinitions.java
package com.example.demo.stepdefinitions;
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
public class API_StepDefinitions {
private ValidatableResponse apiResponse;
private String endpoint = "http://localhost:8080/student/1";
@Given("I send a request to the URL to get student details")
public void sendRequest(){
apiResponse = given().contentType(ContentType.JSON)
.when().get(endpoint).then();
System.out.println("Response :"+apiResponse.extract().asPrettyString());
}
@Then("the response will return status {string} and id {int} and name {string} and grade {string}")
public void verify(String statusCode, Integer st_id, String st_name, String st_grade) {
apiResponse.assertThat().statusCode(Integer.parseInt(statusCode));
apiResponse.assertThat().body("student_id",equalTo(st_id));
apiResponse.assertThat().body("name",equalTo(st_name));
apiResponse.assertThat().body("grade",equalTo(st_grade));
}
}
Test Runner
Test runner is provided by JUnit. Cucumber annotations are used to bind the feature and step definition scripts to run with the test runner.
CucumberTestRunner.java
package com.example.demo.runner;
import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
@RunWith(Cucumber.class)
@CucumberOptions(plugin ="pretty",features= {"src/test/resources/features/APITest.feature"}, glue= {"com.example.demo.stepdefinitions"})
public class CucumberTestRunner {
}
Cucumber Report
Properties file is used to set the flag to automatically generate the Cucumber test report
cucumber.properties
cucumber.publish.enabled=true
Build.gradle
Here is the build.gradle file. We have added the configuration for CucumberRunTime, Gradle test task to use JUnit. Cucumber task has also been added which can be called through the command line to run the Cucumber tests.
/*
* This file was generated by the Gradle 'init' task.
*
* This project uses @Incubating APIs which are subject to change.
*/
plugins {
id 'java'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.7.3'
implementation 'org.springdoc:springdoc-openapi-ui:1.6.4'
implementation 'org.springframework.boot:spring-boot-starter-web:2.7.0'
runtimeOnly 'com.h2database:h2:2.1.214'
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.7.0'
testImplementation 'io.cucumber:cucumber-java:6.8.1'
testImplementation 'io.cucumber:cucumber-junit:6.8.1'
testImplementation 'io.rest-assured:rest-assured:4.3.3'
}
test {
useJUnitPlatform()
}
configurations {
cucumberRuntime {
extendsFrom testImplementation
}
}
//Using gradle task to run Cucumber Tests
task cucumber() {
dependsOn assemble, testClasses
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty', '--glue', 'com.example.demo.stepdefinitions', 'src/test/resources']
}
}
}
Here are meaning of args in cucumber task:
--plugin/-p: Specifies the plugins that we want to add in project
pretty: It generates pretty report.
html: It will generate the html report.
--glue/-g: It specifies project package name, and location of feature file(s)
Running the tests
Start the REST API application and then run the test. In the IntelliJ Idea IDE, the test can be run from the feature file or test runner.
As the custom task was added in the build.gradle file, we can also run the test from command line using "gradle cucumber" command from project home:gradle cucumber
PS C:\Users\mail2\Downloads\Rest Service Testing with JUnit RestAssured Cucumber Gherkins> gradle cucumber Starting a Gradle Daemon, 1 busy and 1 incompatible Daemons could not be reused, use --status for details > Task :cucumber @GetStudentDetails Scenario Outline: Send a valid Request to get student details # src/test/resources/features/APITest.feature:11 21:04:14.667 [main] DEBUG org.apache.http.impl.conn.BasicClientConnectionManager - Get connection for route {}->http://localhost:8080 21:04:14.681 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnectionOperator - Connecting to localhost:8080 21:04:14.696 [main] DEBUG org.apache.http.client.protocol.RequestAddCookies - CookieSpec selected: ignoreCookies 21:04:14.697 [main] DEBUG org.apache.http.client.protocol.RequestAuthCache - Auth cache not set in the context 21:04:14.699 [main] DEBUG org.apache.http.client.protocol.RequestTargetAuthentication - Target auth state: UNCHALLENGED 21:04:14.699 [main] DEBUG org.apache.http.client.protocol.RequestProxyAuthentication - Proxy auth state: UNCHALLENGED 21:04:14.699 [main] DEBUG org.apache.http.impl.client.DefaultHttpClient - Attempt 1 to execute request 21:04:14.699 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnection - Sending request: GET /student/1 HTTP/1.1 21:04:14.699 [main] DEBUG org.apache.http.wire - >> "GET /student/1 HTTP/1.1[\r][\n]" 21:04:14.701 [main] DEBUG org.apache.http.wire - >> "Content-Type: application/json; charset=UTF-8[\r][\n]" 21:04:14.701 [main] DEBUG org.apache.http.wire - >> "Accept: */*[\r][\n]" 21:04:14.701 [main] DEBUG org.apache.http.wire - >> "Host: localhost:8080[\r][\n]" 21:04:14.701 [main] DEBUG org.apache.http.wire - >> "Connection: Keep-Alive[\r][\n]" 21:04:14.701 [main] DEBUG org.apache.http.wire - >> "User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_275)[\r][\n]" 21:04:14.701 [main] DEBUG org.apache.http.wire - >> "Accept-Encoding: gzip,deflate[\r][\n]" 21:04:14.701 [main] DEBUG org.apache.http.wire - >> "[\r][\n]" 21:04:14.701 [main] DEBUG org.apache.http.headers - >> GET /student/1 HTTP/1.1 21:04:14.701 [main] DEBUG org.apache.http.headers - >> Content-Type: application/json; charset=UTF-8 21:04:14.701 [main] DEBUG org.apache.http.headers - >> Accept: */* 21:04:14.701 [main] DEBUG org.apache.http.headers - >> Host: localhost:8080 21:04:14.701 [main] DEBUG org.apache.http.headers - >> Connection: Keep-Alive 21:04:14.701 [main] DEBUG org.apache.http.headers - >> User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_275) 21:04:14.701 [main] DEBUG org.apache.http.headers - >> Accept-Encoding: gzip,deflate 21:04:14.706 [main] DEBUG org.apache.http.wire - << "HTTP/1.1 200 [\r][\n]" 21:04:14.708 [main] DEBUG org.apache.http.wire - << "Content-Type: application/json[\r][\n]" 21:04:14.708 [main] DEBUG org.apache.http.wire - << "Transfer-Encoding: chunked[\r][\n]" 21:04:14.708 [main] DEBUG org.apache.http.wire - << "Date: Wed, 21 Dec 2022 02:04:14 GMT[\r][\n]" 21:04:14.708 [main] DEBUG org.apache.http.wire - << "Keep-Alive: timeout=60[\r][\n]" 21:04:14.708 [main] DEBUG org.apache.http.wire - << "Connection: keep-alive[\r][\n]" 21:04:14.708 [main] DEBUG org.apache.http.wire - << "[\r][\n]" 21:04:14.708 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnection - Receiving response: HTTP/1.1 200 21:04:14.708 [main] DEBUG org.apache.http.headers - << HTTP/1.1 200 21:04:14.708 [main] DEBUG org.apache.http.headers - << Content-Type: application/json 21:04:14.708 [main] DEBUG org.apache.http.headers - << Transfer-Encoding: chunked 21:04:14.708 [main] DEBUG org.apache.http.headers - << Date: Wed, 21 Dec 2022 02:04:14 GMT 21:04:14.708 [main] DEBUG org.apache.http.headers - << Keep-Alive: timeout=60 21:04:14.708 [main] DEBUG org.apache.http.headers - << Connection: keep-alive 21:04:14.713 [main] DEBUG org.apache.http.impl.client.DefaultHttpClient - Connection can be kept alive for 60000 MILLISECONDS 21:04:14.734 [main] DEBUG io.restassured.internal.RequestSpecificationImpl$RestAssuredHttpBuilder - Parsing response as: application/json 21:04:14.734 [main] DEBUG io.restassured.internal.RequestSpecificationImpl$RestAssuredHttpBuilder - Parsed data to instance of: class org.apache.http.conn.EofSensorInputStream 21:04:14.779 [main] DEBUG org.apache.http.wire - << "2b[\r][\n]" 21:04:14.779 [main] DEBUG org.apache.http.wire - << "{"student_id":1,"name":"Ana","grade":"One"}" 21:04:14.780 [main] DEBUG org.apache.http.wire - << "[\r][\n]" 21:04:14.780 [main] DEBUG org.apache.http.wire - << "0[\r][\n]" 21:04:14.780 [main] DEBUG org.apache.http.wire - << "[\r][\n]" 21:04:14.780 [main] DEBUG org.apache.http.impl.conn.BasicClientConnectionManager - Releasing connection org.apache.http.impl.conn.ManagedClientConnectionImpl@7cf7aee 21:04:14.780 [main] DEBUG org.apache.http.impl.conn.BasicClientConnectionManager - Connection can be kept alive for 60000 MILLISECONDS Response :{ "student_id": 1, "name": "Ana", "grade": "One" } Given I send a request to the URL to get student details # com.example.demo.stepdefinitions.API_StepDefinitions.sendRequest() Then the response will return status "200" and id 1 and name "Ana" and grade "One" # com.example.demo.stepdefinitions.API_StepDefinitions.verify(java.lang.String,java.lang.Integer,ja va.lang.String,java.lang.String) 1 Scenarios (1 passed) 2 Steps (2 passed) 0m2.341s 21:04:15.744 [Finalizer] DEBUG org.apache.http.impl.conn.DefaultClientConnection - Connection 0.0.0.0:62594<->127.0.0.1:8080 closed ???????????????????????????????????????????????????????????????????????????? ? View your Cucumber Report at: ? ? https://reports.cucumber.io/reports/2509f6e8-7451-4ef1-85ab-96b699a75571 ? ? ? ? This report will self-destruct in 24h. ? ? Keep reports forever: https://reports.cucumber.io/profile ? ???????????????????????????????????????????????????????????????????????????? Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0. You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins. See https://docs.gradle.org/7.5.1/userguide/command_line_interface.html#sec:command_line_warnings
As Cucumber report link is also provided in the log above.
Source Code:
https://github.com/it-code-lab/Rest-Service-Testing-with-JUnit-RestAssured-Cucumber-Gherkins
Leave a Comment