In this tutorial, I will test a SOAP Service using Rest Assured. I will verify the status code, line of Status, and content of the Response. To set up a basic Rest Assured Maven Project, click here and Gradle project, click here.
Add the below-mentioned dependencies to the pom.xml.
SOAP is an XML-based protocol for accessing web services over HTTP. It has some specifications that could be used across all applications.
SOAP messages are XML documents that are comprised of the following three basic building blocks:
The SOAP Envelope encapsulates all the data in a message and identifies the XML document as a SOAP message.
The Header element contains additional information about the SOAP message. This information could be authentication credentials, for example, which are used by the calling application.
The Body element includes the details of the actual message that needs to be sent from the web service to the calling application. This data includes call and response information.
Implementation Steps:
Step 1 – I have created an XML file for the soap request body in the project resource folder. “Number.xml” is the name of the file.
Step 2 – Specify the base URL to the RESTful web service using the RestAssuredclass.
RestAssured.baseURI = "http://www.dneonline.com";
Step 3 – The response to a request made by REST Assured.
Response response = given()
Response is imported from package:
import io.restassured.response.Response;
Step 4 – Set the content type to specify the format in which the request payload will be sent to the server. Here, the Content-Type is “text/xml; charset=utf-8”.
A Request Body is created by using the below snippet:
requestBody = new File(getClass().getClassLoader().getResource("Number.xml").getFile());
Step 6 – Send the POST request to the server and receive the response of the request made by REST Assured. This response contains every detail returned by hitting request i.e. response body, response headers, status code, status lines, cookies, etc. The response is imported from package:
import io.restassured.response.Response;
Step 7 – To validate a response like status code or value, we have used the below code
An assertion is a way to verify that the expected result and the actual result match or not in the test case. A test is considered successful ONLY if it is completed without throwing any exceptions. If the current value and the expected value match then the assertion passes and when the assertion passes nothing happens. But when an assertion fails, it will fail the test case.
There are various ways to perform assertions in API Testing. For API Testing, we are using Rest Assured, which uses either Hamcrest or JUnit assertions. We are going to discuss Hamcrest Assertions here.
What is Hamcrest?
Hamcrest is a framework for writing matcher objects, allowing ‘match’ rules to be defined declaratively. We do not need to add Hamcrest dependency explicitly as the Rest-Assured 4.3.3 version includes itself. To learn more about Hamcrest, please refer to this link.
We need to add the below dependency to use Hamcrest in the project. Please use the latest version from here
equalTo – It checks whether the extracted string from JSON is equal to the expected string.
equalToIgnoringCase – It checks if the extracted string from JSON matches the expected string. The comparison does not consider case (small or capital).
equalToIgnoringWhiteSpace – It checks if the extracted string from JSON matches the expected string. It takes into account the white spaces.
containsString– It checks whether the extracted string from JSON contains the expected string as a substring.
startsWith– It checks whether the extracted string from JSON is starting with a given string or character.
endsWith – It checks whether the extracted string from JSON is ending with a given string or character.
Below assertions are imported from the package shown below:-
Below are examples to show the use of collection-related assertions.
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
public class HamcrestNullAssertion {
public String endpoint = "https://restful-booker.herokuapp.com/booking/1";
@Test
public void nullAssertion() {
RestAssured.given().contentType(ContentType.JSON)
.when().get(endpoint)
.then().body("totalprice1", is(nullValue()));
}
}
The output of the above program is
hasKey
It checks whether the extracted map has an expected key.
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.hasKey;
public class HamcrestHasKeyAssertion {
public String endpoint = "https://restful-booker.herokuapp.com/booking/1";
@Test
public void collectionAssertions() {
RestAssured.given().contentType(ContentType.JSON)
.when().get(endpoint)
.then().body("bookingdates",hasKey("checkin"));
}
}
The output of the above program is
Not Assertion
The not assertion inverts the meaning of the other assertions. For example, if you want to perform negative assertions, then we can use any assertions with NOT.
The below assertion is imported from the package shown below:-
Below are examples to show the use of negative assertions.
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.testng.annotations.Test;
public class HamcrestNotAssertion {
public String endpoint = "https://restful-booker.herokuapp.com/booking/1";
@Test
public void negativeAssertions() {
RestAssured.given().contentType(ContentType.JSON)
.when().get(endpoint)
.then().body("totalprice",not(equalTo(874)));
}
}
The output of the above program is
Multiple Assert Statements
In the below example, all 3 assertions will fail. It will only execute the first assertion. If the first assertion fails, then other assertions will not be executed.
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.equalTo;
public class HamcrestMultipleAssertions {
public String endpoint = "https://restful-booker.herokuapp.com/booking/1";
@Test
public void test1() {
RestAssured.given().contentType(ContentType.JSON)
.when().get(endpoint).then()
.body("firstname", equalTo("Jim"), // will fail
"lastname", equalTo("Smith"), // will fail
"totalprice", equalTo(314)); // will fail
}
}
The output of the above program is
To execute all the assertions in the test case, combine them into a single body. This should be done just like it is shown below. You can see that all the assertions failed, and they are shown in the response.
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.testng.annotations.Test;
import static org.hamcrest.Matchers.equalTo;
public class HamcrestMultipleAssertions {
public String endpoint = "https://restful-booker.herokuapp.com/booking/1";
@Test
public void test1() {
RestAssured.given().contentType(ContentType.JSON)
.when().get(endpoint).then()
.body("firstname", equalTo("Jim"), // will fail
"lastname", equalTo("Smith"), // will fail
"totalprice", equalTo(314)); // will fail
}
}
The output of the above program is
I have tried to show the use of a few of the most commonly used assertion methods. There are many more methods available in Hamcrest package. To know about other methods, write import static org.hamcrest.Matchers and add (.) at the end, it will show the list of all the methods available in Hamcrest.
To know more details related to Hamcrest assertion, you can refer the official website – Hamcrest
Points to Remember:
Hamcrest is commonly used in JUnit, RestAssured, and Mockito for API and unit testing
It offers a variety of matchers for different data types, such as: Numbers: greaterThan(), lessThan(), Strings: containsString(), startsWith(), endsWith()
You can combine multiple matchers using logical operators: assertThat(score, allOf(greaterThan(50), lessThan(100)))
Encourages fluent and expressive test writing with assertThat() instead of assertEquals()
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
JUnit 4 has a feature called parameterized tests. Parameterized test means to execute the test multiple times with different sets of test data. This eliminates the redundancy of the code. This helps the developers to save time by eliminating the need to copy the code multiple times. Parameterizing tests can increase code coverage and provide confidence that the code is working as expected. These are the steps that need to be followed to create a parameterized test.
Annotate test class with @RunWith(Parameterized.class).
Create an instance variable for each “column” of test data.
It has a single constructor that contains the test data.
Create a public static method annotated with @Parameters that returns a Collection of Objects (as Array) as test data set.
Create your test case(s) using the instance variables as the source of the test data.
The test case will be invoked once for each row of data.
There are multiple ways to parameterize a test. They are the following:
Parameterized Test with Constructor
Parameterized Test with Parameter Annotation
Parameterized Test using CSV File
Let us see parameterized tests in action.
1. Parameterized Test with Constructor
Steps to create a Parameterized JUnit test
1. Create a parameterized test class
Annotate your test class using @runWith(Parameterized.class).
Declaring the variable ‘num1’, ‘num2’, ‘num3’ as private and type as int.
@RunWith(value = Parameterized.class)
public class ParameterizedTest {
private int num1;
private int num2;
private int num3;
2. Create a constructor
Create a constructor that stores the test data. It stores 3 variables.
public ParameterizedTest(int num1, int num2, int num3) {
this.num1 = num1;
this.num2 = num2;
this.num3 = num3;
}
3. Create a static method that generates and returns test data.
Creating a two-dimensional array (providing input parameters for multiplication). Using the asList method, we convert the data into a List type. Since the return type of method input is the collection.
Using @Parameters annotation to create a set of input data to run our test.
The static method identified by @Parameters annotation returns a Collection, where each entry in the Collection will be the input data for one iteration of the test.
The complete code is shown below:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
import static org.junit.Assert.assertEquals;
@RunWith(value = Parameterized.class)
public class ParameterizedTest {
private int num1;
private int num2;
private int num3;
public ParameterizedTest(int num1, int num2, int num3) {
this.num1 = num1;
this.num2 = num2;
this.num3 = num3;
}
@Parameterized.Parameters(name = "{index}: multiply({0}*{1}) = {2}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{1, 1, 1},
{2, 2, 4},
{8, 2, 16},
{4, 5, 20},
{5, 5, 25}
});
}
@Test
public void multiplication() {
System.out.println("The product of "+num1+" and "+num2+" is "+num3);
assertEquals((num1*num2), num3);
}
}
The output of the above program is
2. Parameterized Test with Parameter Annotation
It is also possible to inject data values directly into fields without needing a constructor using the@Parameter annotation.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
import static org.junit.Assert.assertEquals;
@RunWith(value = Parameterized.class)
public class ParameterizedTest1 {
@Parameterized.Parameter(value = 0)
public int num1;
@Parameterized.Parameter(value = 1)
public int num2;
@Parameterized.Parameter(value = 2)
public int num3;
@Parameterized.Parameters(name = "{index}: multiply({0}*{1}) = {2}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{1, 1, 1},
{2, 2, 4},
{8, 2, 16},
{4, 5, 20},
{5, 5, 24}
});
}
@Test
public void multiplication() {
System.out.println("The product of "+num1+" and "+num2+" is "+num3);
assertEquals((num1*num2), num3);
}
}
The output of the above program is
3. Parameterized Test using CSV File
We can use an external CSV file to load the test data. This helps if the number of possible test cases is quite significant, or if test cases are frequently changed. The changes can be done without affecting the test code.
To start with, add a JUnitParams dependency to POM.xml
Let’s say that we have a CSV file with test parameters as JunitParamsTestParameters.csv:
Now let’s look at how this file can be used to load test parametersin the test method:
import junitparams.JUnitParamsRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import junitparams.FileParameters;
import static org.junit.Assert.assertEquals;
@RunWith(JUnitParamsRunner.class)
public class ParameterizedTest2 {
@Test
@FileParameters("src/test/resources/JunitParamsTestParameters.csv")
public void multiplication(int num1, int num2, int num3) {
System.out.println("The product of "+num1+" and "+num2+" is "+num3);
assertEquals((num1*num2), num3);
}
}
The output of the above program is
The parameterized test enables us to execute the same test over and over again using different values.
Important annotations to be used during parameterization
@RunWith
@Parameters
Congratulations. We are done. I hope this tutorial is helpful to you. Happy Learning!!
In the last tutorial, I explained How to test GET Request using Rest Assured. In this tutorial, I will automate a POST Request using Rest Assured. I will verify the status code, line of Status, and content of the Response. To set up a basic Rest Assured Maven Project, click here and Gradle project, click here.
Add the below-mentioned dependencies to the pom.xml. The latest dependency can be downloaded from here.
An HTTP POST method is used to create a new resource in the collection of resources. The request body is passed as a JSON/XML or in a different format. If a resource is created successfully by the endpoint or server, it returns a status code 201(Created). It also provides a Location header with a link to the newly-created resource with the 201 HTTP status. It may return 200 (OK) and 204 (No Content) status code as well, based on how the API is developed.
POST is neither safe nor idempotent. It is therefore recommended for non-idempotent resource requests. Making two identical POST requests will most result in two resources containing the same information.
Below is an example of a Request performing POST operation in Postman.
Below are the steps to test a POST Request using Rest Assured:
Step 1 – Specify the base URL to the RESTful web service using the RestAssuredclass.
Step 2 – Every Request in the Rest-Assured library is represented by an interface called RequestSpecification. This interface allows modifying the request, like adding headers or adding authentication details. Use the RestAssuredclass to generate a RequestSpecification.
contentTypeis imported from restassured.http package:
import io.restassured.http.ContentType;
Step 4 – Pass Request Body as String.
requestSpecification.body(jsonString);
Step 5 – Send the POST request to the server. Then receive the response of the request made by REST Assured. This response contains every detail returned by hitting request i.e. response body, response headers, status code, status lines, cookies, etc. The response is imported from package:
import io.restassured.response.Response;
Step 6 – To validate a response like status code or value, we need to get the reference of type ValidatableResponse.
ValidatableResponse is an interface. A validatable response to a request made by, REST Assured. ValidatableResponse is imported from package:
PrettyPrint() – It prints the response body if possible and returns it as a string. Pretty printing is possible for content-types JSON, XML, and HTML.
Below is the example of testing a POST request in Non-BDD format. I have used ValidatableResponse for the assertion of status. It is also used for the status line and body of the Response.
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
public class POST_NonBDDDemo {
RequestSpecification requestSpecification;
Response response;
ValidatableResponse validatableResponse;
@Test
public void verifyStatusCode() {
String jsonString = "{\"name\":\"newapitest\",\"salary\":\"4000\",\"age\":\"29\"}";
RestAssured.baseURI = "https://dummy.restapiexample.com/api/v1/create";
// Create a request specification
requestSpecification = RestAssured.given();
// Setting content type to specify format in which request payload will be sent.
requestSpecification.contentType(ContentType.JSON);
// Adding body as string
requestSpecification.body(jsonString);
// Calling POST method
response = requestSpecification.post();
// Let's print response body.
String responseString = response.prettyPrint();
/*
* To perform validation on response, we need to get ValidatableResponse type of
* response
*/
validatableResponse = response.then();
// Check status code
validatableResponse.statusCode(200);
// It will check if status line is as expected
validatableResponse.statusLine("HTTP/1.1 200 OK");
// Check response body - name attribute
validatableResponse.body("data.name", equalTo("newapitest"));
// Check response body - message attribute
validatableResponse.body("message", equalTo("Successfully! Record has been added."));
}
}
The below image shows the test result of the above test.
Test implemented in BDD Format
1. equalTois used for assertion, and is imported from a static hamcrest package:
import static org.hamcrest.Matchers.equalTo;
2. givenis a static import from package:
import static io.restassured.RestAssured.given;
Below is an example of a BDD Test.
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import org.junit.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.equalTo;
public class POST_BDDDemo {
ValidatableResponse validatableResponse;
@Test
public void createUser() {
String json = "{\"name\":\"apitest\",\"salary\":\"5000\",\"age\":\"30\"}";
// GIVEN
validatableResponse = given()
.baseUri("https://dummy.restapiexample.com/api")
.contentType(ContentType.JSON)
.body(json)
// WHEN
.when()
.post("/v1/create")
// THEN
.then()
.assertThat().statusCode(200).body("data.name", equalTo("apitest"))
.body("message", equalTo("Successfully! Record has been added."));
System.out.println("Response :" + validatableResponse.extract().asPrettyString());
}
}
The below image shows the test result of the above test.
Explanation:
1. This string represents the JSON payload that will be sent in the body of the POST request. It includes fields like name, salary, and age.
In the last tutorial, I explained the Setup of the REST Assured Maven Project In Eclipse IDE. In this tutorial, I will automate a GET Request. I will verify the status code, line of Status, and content of the Response.
RestAssured is a class that consists of many static fields and methods. It supports POST, GET, PUT, DELETE, HEAD, PATCH, and OPTIONS requests and verifies the response to these requests.
Add the below-mentioned dependencies to the pom.xml.
Step 2 – Every Request in the Rest-Assured library is represented by an interface called RequestSpecification. This interface allows modification of the request, like adding headers or adding authentication details.
requestSpecification = RestAssured.given();
RequestSpecification is imported from the package:
Step 3 – Send the request to the server and receive the response to the request made by REST Assured. This response contains every detail returned by hitting request i.e. response body, response headers, status code, status lines, cookies, etc.
response = requestSpecification.get();
The response is imported from package:
import io.restassured.response.Response;
Step 4 – To validate a response like status code or value, we need to acquire a reference. This reference should be of type ValidatableResponse. ValidatableResponse is an interface. A validatable response to a request made by, REST Assured. ValidatableResponse is imported from the package:
PrettyPrint() – It prints the response body if possible and returns it as a string. Pretty printing is possible for content-types JSON, XML, and HTML.
Below is an example of creating a test in Non-BDD format. I have used ValidatableResponse for the assertion of the status. It is also used for the status line of the Response.
import io.restassured.RestAssured;
import io.restassured.response.Response;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
import org.junit.Test;
public class Get_NonBDDDemo {
RequestSpecification requestSpecification;
Response response;
ValidatableResponse validatableResponse;
@Test
public void verifyStatusCode() {
RestAssured.baseURI = "http://dummy.restapiexample.com/api/v1/employees";
// Create a request specification
requestSpecification = RestAssured.given();
// Calling GET method
response = requestSpecification.get();
// Let's print response body.
String resString = response.prettyPrint();
System.out.println("Response Details : " + resString);
/*
* To perform validation on response, we need to get ValidatableResponse type of
* response
*/
validatableResponse = response.then();
// Get status code
validatableResponse.statusCode(200);
// Check status line is as expected
validatableResponse.statusLine("HTTP/1.1 200 OK");
}
}
The output of the above program is
If you don’t want to use ValidatableResponse for the assertion, you can use Response from io.restassured .response to get the status code and status line, which are asserted using JUnit.Assert.
import io.restassured.RestAssured;
import io.restassured.response.Response;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
import org.junit.Assert;
import org.junit.Test;
public class Get_NonBDDResponseDemo {
RequestSpecification requestSpecification;
Response response;
@Test
public void verifyStatusCode() {
RestAssured.baseURI = "http://dummy.restapiexample.com/api/v1/employees";
// Create a request specification
requestSpecification = RestAssured.given();
// Calling GET method
response = requestSpecification.get();
// Let's print response body.
String resString = response.prettyPrint();
System.out.println("Response Details : " + resString);
// Get status line
String statusLine = response.getStatusLine();
Assert.assertEquals(statusLine, "HTTP/1.1 200 OK");
// Get status code
int statusCode = response.getStatusCode();
Assert.assertEquals(statusCode, 200);
}
}
The output of the above program is
Below is an example of a specific user in GET Request in Postman.
Below is the test implemented in BDD Format. In this test, I am asserting the data of Employee of Id 2. I have validated the name of the employee as well as the response message.
1. equalTo is used for assertion, andis imported from a static hamcrest package:
import static org.hamcrest.Matchers.equalTo;
2. givenis a static import from package:
import static io.restassured.RestAssured.given;
Let us see this with an example.
import org.junit.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.equalTo;
public class Get_BDDDemo {
@Test
public void verifyUser() {
// Given
given()
// When
.when()
.get("http://dummy.restapiexample.com/api/v1/employee/2")
// Then
.then()
.statusCode(200).statusLine("HTTP/1.1 200 OK")
// To verify booking id at index 3
.body("data.employee_name", equalTo("Garrett Winters"))
.body("message", equalTo("Successfully! Record has been fetched."));
}
}
The output of the above program is
Explanation
1.This is the preconditionblock. It is used to specify headers, parameters, authentication, etc. In this example, it’s empty, meaning no special setup is needed for this GET request.
given
2. It performs a GET request to the given URL. It is trying to get the employee with id = 2.
Rest–Assuredis a Java-based library that is used to test RESTful Web Services. REST-assured was designed to simplify the testing and validation of REST APIs. It takes influence from testing techniques used in dynamic languages such as Ruby and Groovy.
Rest Assured enables you to test REST APIs using Java libraries and integrates well with Maven/Gradle. REST Assured is a Java library that provides a domain-specific language (DSL) for writing powerful, maintainable tests for RESTful APIs.
What is JUnit5?
JUnit 5 is the next generation of JUnit. JUnit 5 is composed of several different modules from three different sub-projects.
Java needs to be present on the system to run the tests. Click here to know How to install Java. To know if Java is installed or not on your machine, type this command in the command line. This command will show the version of Java installed on your machine.
java -version
Step 2 – Download and setup Eclipse IDE on the system
The Eclipse IDE (integrated development environment) provides strong support for Java developers, which is needed to write Java code. Click here to learn How to install Eclipse.
Step 3 – Setup Maven
To build a test framework, we need to add a number of dependencies to the project. It is a very tedious and cumbersome process to add each dependency manually. So, to overcome this problem, we use a build management tool. Maven is a build management tool that is used to define project structure, dependencies, build, and test management. Click here to learn How to install Maven.
To know if Maven is already installed or not on your machine, type this command in the command line. This command will show the version of Maven installed on your machine.
The tests should be written in src/test/java directory. To learn how to create a JSON Request body using JSONObject, please refer to this tutorial.
import io.restassured.http.ContentType;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
public class APITests {
String BaseURL = "https://reqres.in/api";
@Test
public void createUser() {
JSONObject data = new JSONObject();
data.put("name", "NewUser1");
data.put("job", "Testing");
// GIVEN
given()
.contentType(ContentType.JSON)
.body(data.toString())
// WHEN
.when()
.post(BaseURL + "/users")
// THEN
.then()
.statusCode(201)
.body("name", equalTo("NewUser1"))
.body("job", equalTo("Testing"))
.log().all();
}
@Test
public void getUser() { //Failed Test
// GIVEN
given()
.contentType(ContentType.JSON)
// WHEN
.when()
.get(BaseURL + "/users/2")
// THEN
.then()
.statusCode(200)
.body("data.first_name", equalTo("Janet1"))
.log().all();
}
}
Step 7 – Test Execution through JUnit Test
Go to the Runner class and right-click Run As JUnit Test. The tests will run as JUnit tests.
Below is the image to run the tests in IntelliJ.
This is how the execution console will look like.
Step 8 – Run the tests from the command line
Maven Site Plugin creates a folder – site under the target directory, and the Maven Surefire Report plugin generates the JUnit Reports in the site folder. We need to run the tests through the command line to generate the JUnit Report.
mvn clean test site
The output of the above program is
Step 9 – Report Generation
After the test execution, refresh the project, and a new folder with the name site in the target folder will be generated. This folder contains the reports generated by JUnit. The structure of the folder site looks as shown below.
View the Report
Right-click on the Junit5 Report.html and select Open In -> Browser ->Chrome.
Summary Report
Below is the summary Report.
Surefire Report
Below is an example of a Surefire Report. This report contains a summary of the test execution.
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
Logging plays an important role in understanding the behaviour of the test. When we are testing an API, it is good to know how the APIs are behaving. We should understand how the request is made and how we received the response from the API. It’s important to check what the headers look like. We also need to see what the body looks like. Additionally, verify what parameters we are providing to the request. All of this helps us debug the test code. It helps us identify the reason for the failure of the test.
REST Assured, provide support to a different type of logging as shown below:-
To log all request specification details including parameters, headers, and body of the request, log().all() needs to be added to post given() section.
import org.testng.annotations.Test;
import io.restassured.http.ContentType;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.equalTo;
public class RestTests {
@Test
public void requestLoggingDemo() {
String json = "{\"name\":\"apitest\",\"salary\":\"5000\",\"age\":\"30\"}";
// GIVEN
given()
.log().all()
.baseUri("https://dummy.restapiexample.com/api")
.contentType(ContentType.JSON)
.body(json)
// WHEN
.when()
.post("/v1/create")
// THEN
.then()
.assertThat()
.statusCode(200)
.body("data.name", equalTo("apitest"))
.body("message", equalTo("Successfully! Record has been added."));
}
}
The output of the above program is
Other different request logging options are:-
given().log().params(). .. // Log only the parameters of the request
given().log().body(). .. // Log only the request body
given().log().headers(). .. // Log only the request headers
given().log().cookies(). .. // Log only the request cookies
given().log().method(). .. // Log only the request method
given().log().path(). .. // Log only the request path
Response Logging
If you want to print the response body regardless of the status code, you can do
get("/x").then().log().body()..
This will print the response body regardless of an error occurring.
import org.testng.annotations.Test;
import io.restassured.http.ContentType;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.equalTo;
public class RestTests {
@Test
public void responseLoggingDemo() {
String json = "{\"name\":\"apitest\",\"salary\":\"5000\",\"age\":\"30\"}";
// GIVEN
given()
.baseUri("https://dummy.restapiexample.com/api")
.contentType(ContentType.JSON)
.body(json)
// WHEN
.when()
.post("/v1/create")
// THEN
.then()
.log().all()
.statusCode(200)
.body("data.name", equalTo("apitest"))
.body("message", equalTo("Successfully! Record has been added."));
}
}
The output of the above program is
Conditional Logging
What if you want to perform logging conditionally? For example, log in if validation fails and the status code is equal to 200. Also, log in if the server returns a status code >=400.
.then().log().ifStatusCodeIsEqualTo(302). .. // Only log if the status code is equal to 302
.then().log().ifStatusCodeMatches(matcher). .. // Only log if the status code matches the supplied Hamcrest matcher
Let us create an example of conditional logging.
import org.testng.annotations.Test;
import io.restassured.http.ContentType;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.equalTo;
public class RestTests {
@Test
public void conditionalResponseLoggingDemo() {
String json = "{\"name\":\"apitest\",\"salary\":\"5000\",\"age\":\"30\"}";
// GIVEN
given()
.baseUri("https://dummy.restapiexample.com/api")
.contentType(ContentType.JSON)
.body(json)
// WHEN
.when()
.post("/v1/create")
// THEN
.then()
.log().ifStatusCodeIsEqualTo(200)
.assertThat().statusCode(200)
.body("data.name", equalTo("apitest"))
.body("message", equalTo("Successfully! Record has been added."));
}
}
The output of the above program is
Logging to a text file with Rest Assured
We will see how we can log all the request and response data to a txt file using Rest Assured.
Create a PrintStream object. You have to provide an object of FileOutputStream() to the PrintStream() constructor. Provide the path to the logging.txt file in FileOutputStream().
REST Assured gives us a filter() method, this filter method accepts RequestLoggingFilter and ResponseLoggingFilter. They have two methods, logRequestTo() and logResponseTo() methods respectively. These methods expect a Stream.
Pass the log stream we created to these methods.
import org.testng.annotations.Test;
import io.restassured.filter.log.RequestLoggingFilter;
import io.restassured.filter.log.ResponseLoggingFilter;
import io.restassured.http.ContentType;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.equalTo;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class RestTests {
@Test
public void responsetoFileDemo() throws FileNotFoundException {
PrintStream log = new PrintStream(new FileOutputStream("logging.txt"));
String json = "{\"name\":\"apitest\",\"salary\":\"5000\",\"age\":\"30\"}";
// GIVEN
given()
.baseUri("https://dummy.restapiexample.com/api")
.contentType(ContentType.JSON)
.body(json)
.filter(RequestLoggingFilter.logRequestTo(log))
.filter(ResponseLoggingFilter.logResponseTo(log))
// WHEN
.when()
.post("/v1/create")
// THEN
.then()
.log().ifStatusCodeIsEqualTo(200)
.assertThat().statusCode(200)
.body("data.name", equalTo("apitest"))
.body("message", equalTo("Successfully! Record has been added."));
}
}
The output of the above program is
Mostly we have more than 1 test, and we want to save the log of all the tests in the text file. We can create a @BeforeClass method, and this class contains the code to create the file and append the data to that file.
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import io.restassured.filter.log.RequestLoggingFilter;
import io.restassured.filter.log.ResponseLoggingFilter;
import io.restassured.http.ContentType;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.equalTo;
public class LogTest {
public PrintStream log ;
RequestLoggingFilter requestLoggingFilter;
ResponseLoggingFilter responseLoggingFilter;
@BeforeClass
public void init() throws FileNotFoundException {
log = new PrintStream(new FileOutputStream("test_logging.txt"),true);
requestLoggingFilter = new RequestLoggingFilter(log);
responseLoggingFilter = new ResponseLoggingFilter(log);
}
@Test
public void test1() {
// Given
given()
.contentType(ContentType.JSON)
. filters(requestLoggingFilter,responseLoggingFilter)
.when()
.get("https://dummy.restapiexample.com/api/v1/employee/2")
.then()
.log().ifStatusCodeIsEqualTo(200)
.assertThat().statusCode(200).statusLine("HTTP/1.1 200 OK")
// To verify booking id at index 2
.body("data.employee_name", equalTo("Garrett Winters"))
.body("message", equalTo("Successfully! Record has been fetched."));
}
@Test
public void test2() {
// Given
given()
.contentType(ContentType.JSON)
. filters(requestLoggingFilter,responseLoggingFilter)
.when()
.get("https://dummy.restapiexample.com/api/v1/employee/1")
.then()
.log().ifStatusCodeIsEqualTo(200)
.assertThat().statusCode(200).statusLine("HTTP/1.1 200 OK")
// To verify booking id at index 1
.body("data.employee_name", equalTo("Tiger Nixon"))
.body("message", equalTo("Successfully! Record has been fetched."));
}
@Test
public void test3() throws FileNotFoundException {
String json = "{\"name\":\"apitest\",\"salary\":\"5000\",\"age\":\"30\"}";
// GIVEN
given()
.baseUri("https://dummy.restapiexample.com/api")
.contentType(ContentType.JSON)
.body(json)
.filters(requestLoggingFilter,responseLoggingFilter)
// WHEN
.when()
.post("/v1/create")
// THEN
.then()
.log().ifStatusCodeIsEqualTo(200)
.assertThat().statusCode(200)
.body("data.name", equalTo("apitest"))
.body("message", equalTo("Successfully! Record has been added."));
}
}
The below file shows that the log for multiple requests is saved here.
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
Selenium is an open-source tool. It is not a single automation tool like QTP. Instead, it is a suite of software or a set of JAR files. These tools automate web browsers across various browsers. Selenium is used by many companies, but a few to mention are Netflix, Google, HubSpot, Fitbit, Accenture, IBM, and more.
Selenium – Introduction, Installation, Test Script
In the previous tutorial, I explained the Page Object Model with Selenium, Cucumber and JUnit. In this tutorial, I’ll create a BDD Framework for web application testing. I will use the Page Object Model with Selenium, Cucumber, and TestNG.
The Page Object model is an object design pattern in Selenium, where web pages are represented as classes, the various elements on the page are defined as variables in the class and all possible user interactions can then be implemented as methods in the class.
What is Cucumber?
Cucumber is one such open-source tool, which supports Behavior Driven Development(BDD). In simple words, Cucumber can be defined as a testing framework, driven by plain English. It serves as documentation, automated tests, and development aid – all in one.
Dependency List
Cucumber Java – 7.18.1
Cucumber TestNG – 7.18.1
Java 17
Maven – 3.9.6
Selenium – 4.23.0
TestNG – 7.10.2
Maven Compiler – 3.13.0
Maven Surefire – 3.3.1
Project Structure
Implementation Steps
Step 1- Download and Install Java
Cucumber and Selenium need Java to be installed on the system to run the tests. Click here to learn How to install Java.
Step 2 – Setup Maven
To build a test framework, we need to add a number of dependencies to the project. Click here to learn How to install Maven.
Step 3 – Install Cucumber Eclipse Plugin(Only for Eclipse)
The cucumber plugin is an Eclipse plugin that allows eclipse to understand the Gherkin syntax. When we are working with cucumber we will write the feature files that contain Feature, Scenario, Given, When, Then, And, But, Tags, Scenario Outline, and Examples. By default, eclipse doesn’t understand these keywords so it doesn’t show any syntax highlighter. Cucumber Eclipse Plugin highlights the keywords present in Feature File. Refer to this tutorial to get more detail – How to setup Cucumber with Eclipse.
Step 4 – Create a new Maven Project
To create a new Maven project, go to the File -> New Project-> Maven-> Maven project-> Next -> Enter Group ID & Artifact ID -> Finish.
Step 5 – Create source folder src/test/resources to create test scenarios in the Feature file
A new Maven Project is created with 2 folders – src/main/javaand src/test/java. To create test scenarios, we need a new source folder called – src/test/resources. To create this folder, right-click on test directory ->select New ->Directory, and then it shows Maven Source Directories as resources as shown below.
Double-click on the resources directory and a new source directory under your new Maven project is created as shown in the below image.
Step 6 – Add Selenium, TestNG, and Cucumber dependencies to the project
Add below mentioned Selenium, TestNG, and Cucumber dependencies to the project.
Step 7 – Add Maven Compiler Plugin and Surefire Plugin
The compiler plugin is used to compile the source code of a Maven project. This plugin has two goals, which are already bound to specific phases of the default lifecycle:
Step 8 – Create a feature file in the src/test/resources
Create a folder with name features. Now, create the feature file in this folder. The feature file should be saved with the extension .feature. This feature file contains the test scenarios created to test the application. The Test Scenarios are written in Gherkins language in the format of Given, When, Then, And, But.
Below is an example of Test Scenarios in the feature file. I have failed one test scenario intentionally – @MissingUsername.
Feature: Login to HRM Application
Background:
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
@ValidCredentials
Scenario: Login with valid credentials
When User enters username as "Admin" and password as "admin123"
Then User should be able to login successfully and new page open
@InvalidCredentials
Scenario Outline: Login with invalid credentials
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| Admin | admin12$$ | Invalid credentials |
| admin$$ | admin123 | Invalid credentials |
| abc123 | xyz$$ | Invalid credentials |
@MissingUsername @FailedTest
Scenario: Login with blank username
When User enters username as " " and password as "admin123"
Then User should be able to see a message "Required1" below Username
Step 9 – Create the classes for locators, actions, and utilities in src/main/java
Create folders – actions, locators, and utils in src/main/java.
Create a Java Class for each page where define WebElements as variables using Annotation @FindBy. Create another Java class that contains methods for actions performed on WebElements. Here, I’m going to create 2 classes for locators – LoginPageLocatorsand HomePageLocators as well as 2 classes for actions – LoginPageActionsand HomePageActions
TheLocator class contains WebElements which are identified by @FindByannotation as shown below:-
Action class contains methods for the action to be performed on the web elements identified in the locator class.
The initElements is a static method of PageFactory class that is used to initialize all the web elements located by @FindBy annotation. Only after the WebElements are initialized, they can be used in the methods to perform actions.
public Login(WebDriver driver) {
this.driver = driver;
// This initElements method will create all WebElements
PageFactory.initElements(driver, this);
}
Below is the sample code of the LoginPageLocators.
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPageLocators {
@FindBy(name = "username")
public WebElement userName;
@FindBy(name = "password")
public WebElement password;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[1]/div/span")
public WebElement missingUsernameErrorMessage;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[3]/button")
public WebElement login;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/div/div[1]/div[1]/p")
public WebElement errorMessage;
}
Below is the sample code for the HomePageLocators.
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePageLocators {
@FindBy(xpath = "//span[@class='oxd-topbar-header-breadcrumb']/h6")
public WebElement homePageUserName;
}
Create the action classesfor each web page. These action classes contain all the methods needed by the step definitions. In this case, I have created 2 action classes – LoginPageActions, HomePageActions
LoginPageActions
import org.example.locators.LoginPageLocators;
import org.example.utils.HelperClass;
import org.openqa.selenium.support.PageFactory;
public class LoginPageActions {
LoginPageLocators loginPageLocators = null;
public LoginPageActions() {
this.loginPageLocators = new LoginPageLocators();
PageFactory.initElements(HelperClass.getDriver(),loginPageLocators);
}
// Get the error message when username is blank
public String getMissingUsernameText() {
return loginPageLocators.missingUsernameErrorMessage.getText();
}
// Get the Error Message
public String getErrorMessage() {
return loginPageLocators.errorMessage.getText();
}
public void login(String strUserName, String strPassword) {
// Fill user name
loginPageLocators.userName.sendKeys(strUserName);
// Fill password
loginPageLocators.password.sendKeys(strPassword);
// Click Login button
loginPageLocators.login.click();
}
HomePageActions
import org.example.locators.HomePageLocators;
import org.example.utils.HelperClass;
import org.openqa.selenium.support.PageFactory;
public class HomePageActions {
HomePageLocators homePageLocators = null;
public HomePageActions() {
this.homePageLocators = new HomePageLocators();
PageFactory.initElements(HelperClass.getDriver(),homePageLocators);
}
// Get the User name from Home Page
public String getHomePageText() {
return homePageLocators.homePageUserName.getText();
}
}
Create a Helper class where we are initializing the web driver, initializing the web driver wait, defining the timeouts, and creating a private constructor of the class, it will declare the web driver, so whenever we create an object of this class, a new web browser is invoked.
package com.example.utils;
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.chrome.ChromeOptions;
public class HelperClass {
private static HelperClass helperClass;
private static WebDriver driver;
public final static int TIMEOUT = 5;
private HelperClass() {
WebDriverManager.chromedriver().setup();
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized");
driver = new ChromeDriver(options);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
}
public static void openPage(String url) {
driver.get(url);
}
public static WebDriver getDriver() {
return driver;
}
public static void setUpDriver() {
if (helperClass==null) {
helperClass = new HelperClass();
}
}
public static void tearDown() {
if(driver!=null) {
driver.quit();
}
helperClass = null;
}
}
Step 10 – Create a StepDefinition class in src/test/java
Create a Java Class called Definition where we will create the Test Code related to the Given, When, Then of Feature file in src/test/java.
Now, we need to create the Step Definition of the Feature File – LoginPageDefinitions.java.
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import org.example.actions.HomePageActions;
import org.example.actions.LoginPageActions;
import org.example.utils.HelperClass;
import org.testng.Assert;
public class LoginPageDefinitions {
LoginPageActions objLogin = new LoginPageActions();
HomePageActions objHomePage = new HomePageActions();
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
HelperClass.openPage(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
// login to application
objLogin.login(userName, passWord);
}
@Then("User should be able to login successfully and new page open")
public void verifyLogin() {
// Verify home page
Assert.assertTrue(objHomePage.getHomePageText().contains("Dashboard"));
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessage(String expectedErrorMessage) {
// Verify error message
Assert.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
}
@Then("User should be able to see a message {string} below Username")
public void verifyMissingUsernameMessage(String message) {
Assert.assertEquals(objLogin.getMissingUsernameText(),message);
}
}
Step 11 – Create a Hook class in src/test/java
Create thehook class that contains the Before and After hook to initialize the web browser and close the web browser. I have added the code to take the screenshot of the failed scenario in @After Hook.
Below is the code for the Hooks class.
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import com.example.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public class Hooks {
@Before
public static void setUp() {
HelperClass.setUpDriver();
}
@After
public static void tearDown(Scenario scenario) {
//validate if scenario has failed
if(scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) HelperClass.getDriver()).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", scenario.getName());
}
HelperClass.tearDown();
}
}
Step 12 – Create a TestNG Cucumber Runner classin the src/test/java
Cucumber needs a TestRunner class to run the feature files. It is suggested to create a folder with the name of the runner in the src/test/java directory and create the Cucumber TestRunner class in this folder. Below is the code of the Cucumber TestRunner class.
Below is the code for CucumberRunnerTests class.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = "src/test/resources/features/LoginPage.feature", glue = "com.example.definitions",
plugin = {})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
Note:- The name of the Runner class should end with Test otherwise we can’t run the tests using Command Line.
Step 13 – Run the tests from TestNG
You can execute the test script by right-clicking on TestRunner class -> Run As TestNG. (Eclipse)
In the case of the IntelliJ project, right-click on the runner class and select Run ‘CucumberRunnerTests’.
The output of the above program is
Step 14 – Run the tests from testng.xml
Create a testng.xml as shown below and run the tests as TestNG.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="Cucumber with TestNG Test">
<classes>
<class name="com.example.runner.CucumberRunnerTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
The testng.xml is highlighted below:
Step 15 – Run the tests from Command Line
Run the below command in the command prompt to run the tests and to get the test execution report.
mvn clean test
The output of the above program is
Step 16 – Cucumber Report Generation
To get Cucumber Test Reports, add cucumber.properties under src/test/resources and add the below instruction in the file.
cucumber.publish.enabled=true
Below is the image of the Cucumber Report generated using the Cucumber Service.
In the above example, as we can see, one of the tests has failed. So, when a test fails, we have written the code to take a screenshot of the failed step. The Attached Image shows the image of the failed test. You can click on that to see the screenshot.
Step 17 – TestNG Report Generation
TestNG generates various types of reports under the target->surefire-reports 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.
emailable-report.html
Index.html
TestNG also produces an “index.html” report. The below image shows the index.html report.