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


  • captcha text