In the previous tutorial, we got an introduction to Log4j 2. In this tutorial, we will create a Selenium project and implement logging using the Log4j2 Properties configuration.
We’ve defined the level of the root logger as DEBUG. This means that we’ll get all the log events with level DEBUG and above. We’ve also defined a name for the appender as STDOUT and LogToFile.
We want to direct the logs to the console as well as File, we assigned the Appender typeas Console and File.
We specify the pattern in which we want to print the log messages.
Step 4 – Import log4j in the Selenium script
We need to import the log4j library in the Selenium script so that we can use it in your code. Here is an example of how to import log4j in the Selenium script:
We need to create a Logger instance in the Selenium script so that we can use it to log information. Here is an example of how to create a Logger instance:
Apache Log4j is a Java-based logging utility. Log4j2 is the updated version of the popular and influential log4j library. It is an open-source logging API for Java.
In the previous tutorials, we discussed BDD(Behaviour Driven Development) and Gherkin. Cucumberis one such open-source tool, which supports Behaviour 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.
In this tutorial, we will set up Cucumber with Eclipse.
Java is a robust programming language. Java is a general-purpose programming language that is concurrent; class-based and object-oriented language. Java follows the concept of “write once and run anywhere (WORA)” which means that compiled Java code can be run on all different platforms that support Java without the need for recompilation. Cucumber supports the Java platform for execution. Click here to know How to install Java.
2. Download and Start Eclipse
Eclipse is an Integrated Development Environment (IDE). It contains a base workspace and an extensible plug-in system for customizing the environment. To download Eclipse, please refer to this tutorial – How to install Eclipse.
3. Maven – How to install Maven on Windows
Apache Maven is a software project management and comprehension tool. It uses the concept of a project object model (POM), Maven can manage a project’s build, reporting, and documentation from a central piece of information. MAVEN helps us in creating the project structure and managing and downloading the dependencies. We need to define the required dependencies in pom.xml. To install Maven on Windows, please refer to this tutorial –How to install Maven.
4. Install Cucumber Eclipse Plugin
The Cucumber plugin is an Eclipse plugin that allows eclipse to understand the Gherkin syntax. Cucumber Eclipse Plugin highlights the keywords present in Feature File. To install Cucumber Eclipse Plugin, please refer to this tutorial – How to install Cucumber Eclipse Plugin
This will indicate Maven, which Cucumber files are to be downloaded from the central repository to the local repository. Create one more dependency tag.
This will indicate Maven, which Cucumber JUnit files are to download from the central repository to the local repository. Create one more dependency tag.
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?
A 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.
Dependency List
Springboot – 2.5.2
Cucumber – 6.10.4
Java 11
JUnit – 4.13.2
Maven – 3.8.1
RestAssured – 4.3.3
JUnit Vintage Engine (To run the tests through command line)
Below is the structure of a SpringBoot application project.
Below are various Java classes present in a SpringBoot REST Application.
SpringBootRestServiceApplication.java – The Spring Boot Application class is generated with Spring Initializer. This class acts as the launching point for the application.
pom.xml – This contains all the dependencies needed to build this project.
Student.java – This is JPA Entity for Student class
StudentRepository.java – This is JPA Repository for Student. This is created using Spring Data JpaRepository.
StudentController.java – Spring Rest Controller exposing all services on the student resource.
CustomizedExceptionHandler.java – This implements global exception handling and customizes the responses based on the exception type.
ErrorDetails.java – Response Bean to use when exceptions are thrown from API.
StudentNotFoundException.java – Exception thrown from resources when the student is not found.
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.
application.properties – 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.
We need the below files to create a SpringBoot Application.
Student.java
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;
}
}
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);
}
}
application.properties
spring.jpa.defer-datasource-initialization=true
data.sql
insert into student values(10001,'Annie', 'E1234567');
insert into student values(10002,'John', 'A1234568');
insert into student values(10003,'David','C1232268');
Test Automation Framework Implementation
Step 1 – Add SpringbootTest, Rest-Assured, and Cucumber dependencies to the project
To Test a SpringBoot Application, we are using SpringBoot Test, JUnit, Cucumber, and Rest Assured. Below mentioned dependencies are added in POM.xml
Step 2 – Create a directory src/test/resources and create a feature file under src/test/resources
By default, the Maven project has src/test/java directory only. Create a new directory under src/test with the name of resources. Create a folder name as Features within 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
@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 id <studentID> and names "<studentNames>" and passport_no "<studentPassportNo>"
Examples:
|studentID |studentNames |studentPassportNo|
|10001 |Annie |E1234567 |
|10002 |John |A1234568 |
|10003 |David |C1232268 |
Step 3 – Create the Step Definition class or Glue Code for the Test Scenariounder src/test/java
The corresponding step definition file of the above feature file is shown below.
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;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;
@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringbootCucumberTestDefinitions {
private final static String BASE_URI = "http://localhost";
@LocalServerPort
private int port;
private ValidatableResponse validatableResponse;
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 iSendARequest(String endpoint) throws Throwable {
validatableResponse = requestSpecification().contentType(ContentType.JSON)
.when().get(endpoint).then();
System.out.println("RESPONSE :"+validatableResponse.extract().asString());
}
@Then("the response will return status {int} and id {int} and names {string} and passport_no {string}")
public void extractResponse(int status, int id, String studentName,String passportNo) {
validatableResponse.assertThat().statusCode(equalTo(status))
.body("id",hasItem(id)).body(containsString(studentName))
.body(containsString(passportNo));
}
}
The @CucumberContextConfiguration annotation tells Cucumber to use this class as the test context configuration for Spring. It is imported from:-
With the @SpringBootTest annotation, Spring Boot provides a convenient way to start up an application context to be used in a test. It is imported from package:-
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 package:-
The assertions are imported from the Hamcrest package:-
import static org.hamcrest.Matchers.*;
Step 4 – Create a Cucumber Runner classunder src/test/java
A runner will help us to run the feature file and act as an interlink between the feature file and StepDefinition Class. To know more about Runner, refer to this link. The TestRunner should be created within the directory src/test/java.
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"}, glue = { "com.example.demo.definitions"})
public class CucumberRunnerTests {
}
The @CucumberOptionsannotation 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 configurations and classes that are shared between tests.
Step 5 – Run the tests from JUnit
You can execute the test script by right-clicking onTestRunner class -> Run As JUnit 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.
The output of the above program is
Step 6 – Run the tests from the Command Line
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. If we still want to write tests using JUnit 4, we need to add the following Maven dependency:
To get Cucumber Test Reports, add cucumber.properties under src/test/resources and add the below instruction in the file. To know more about Cucumber Report Service, refer to this tutorial.
cucumber.publish.enabled=true
Below is the image of the report generated after the completion of the execution. This report can be saved on GitHub for future use.
That’s it! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
It’s necessary for us to establish connections to many databases during test automation. This article will teach us how to use an example code snippet to establish a Java connection to Microsoft SQL Server. However, before we do so, let’s first examine the various automation situations that call for database connections.
Why do we need to connect to databases in automation?
To get test data – If the database is automated, test data may be immediately retrieved and processed using a test automation script. To verify the result – To confirm the outcome, we may use automation to cross-check the front-end result to the database’s back-end record. To delete test data created – It is best practice in automation to remove test data that has been created. In order to do this, we use database automation and immediately execute the delete query. To update certain data – Update queries can be used to update test data in accordance with the requirements of the test script.
Connecting to SQL Server in Java
Get a connection to the Database
Create a statement object
Execute the SQL query
Process the Result Set
Get a connection to the Database
Need a connection string in the form of a JDBC URL.
You can connect to MySQL, MS MySql Server, or Oracle using the below syntax:
MS MySql Server - jdbc:odbc:DemoDSN
MySQL - jdbc:mysql://localhost:3306/demodb
Oracle - jdbc:orac;e:thin@myserver:1521:demodb
Failure to connect to the database will throw an exception:-
java.sql.SQLException : bad url or credentials
java.lang.ClassNotFoundException: JDBC driver not in classpath
Create a statement object
The statement object is based on connection. It will be used later to execute SQL query.
The DriverManager class acts as an interface between users and drivers. DriverManager helps to connect an application based on the database connection string.
We use the statement object to execute the SQL Query. Pass in the SQL query.
executeQuery method returns a result table in a ResultSet object. After you obtain the result table, you need to use ResultSet methods to move through the result table and obtain the individual column values from each row.
ResultSet result = stmt.executeQuery("select * from employee");
Process the result Set
Result Set is initially placed before the first row. Result.next() – It moves forward one row and returns true if there are more rows to process Looping through the result set.
import java.sql.*;
public class JdbcTest {
public static void main(String[] args) throws SQLException {
Connection conn;
Statement stmt = null;
ResultSet result = null;
String dbUrl = "jdbc:mysql://localhost:3306/demo";
String username = "student";
String password = "student1$";
try {
//Get a connection to database
conn = DriverManager.getConnection(dbUrl, username, password);
System.out.println("Database connection is successful\n");
//Create a statement
stmt = conn.createStatement();
//Execute the SQL Query
result = stmt.executeQuery("Select * from employees");
//Process the result set
while (result.next()) {
System.out.println("First_Name :" + result.getString("first_name") + " , " + ("Last_Name :" + result.getString("last_name")));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
The output of the above program is
Let us go to MySQL Server and run the command. Before that, if you want to create an “employees” table in the demo database, please use the below script:
create database if not exists demo;
use demo;
drop table if exists employees;
CREATE TABLE `employees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`last_name` varchar(64) DEFAULT NULL,
`first_name` varchar(64) DEFAULT NULL,
`email` varchar(64) DEFAULT NULL,
`department` varchar(64) DEFAULT NULL,
`salary` DECIMAL(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (1,'Doe','John','john.doe@foo.com', 'HR', 55000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (2,'Public','Mary','mary.public@foo.com', 'Engineering', 75000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (3,'Queue','Susan','susan.queue@foo.com', 'Legal', 130000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (4,'Williams','David','david.williams@foo.com', 'HR', 120000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (5,'Johnson','Lisa','lisa.johnson@foo.com', 'Engineering', 50000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (6,'Smith','Paul','paul.smith@foo.com', 'Legal', 100000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (7,'Adams','Carl','carl.adams@foo.com', 'HR', 50000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (8,'Brown','Bill','bill.brown@foo.com', 'Engineering', 50000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (9,'Thomas','Susan','susan.thomas@foo.com', 'Legal', 80000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (10,'Davis','John','john.davis@foo.com', 'HR', 45000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (11,'Fowler','Mary','mary.fowler@foo.com', 'Engineering', 65000.00);
INSERT INTO `employees` (`id`,`last_name`,`first_name`,`email`, `department`, `salary`) VALUES (12,'Waters','David','david.waters@foo.com', 'Legal', 90000.00);
Now execute the below SQL command:-
select * from demo.employees;
The output of the above program is
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
Serenity BDD is an open-source library that aims to make the idea of living documentation a reality.
What is Rest Assured?
Rest Assured is one of the most powerful libraries for testing RESTful API using Java language. Rest-Assured is a Java-based library that is used to test RESTful Web Services. This library behaves like a headless Client to access REST web services. The rest-Assured library also provides the ability to validate the HTTP Responses received from the server.
Prerequisite
Java 17 installed
Maven installed
Eclipse or IntelliJ installed
Dependency List
Java 17
Maven – 3.9.5
Serenity – 4.0.18
Serenity Rest Assured – 4.0.18
Rest Assured – 5.3.2
JUnit – 4.13.2
Maven Surefire Plugin – 3.1.2
Maven Failsafe Plugin – 3.1.2
Maven Compiler Plugin – 3.11.0
Project Structure
Implementation Steps
Step 1 – Update the Properties section in Maven pom.xml
Step 4 – Create the Test Code in src/java/testdirectory
There are 2 ways to create the same test. One approach is to have a Definition file that contains all the test code as shown below.
package org.example.tests;
import io.restassured.response.Response;
import net.serenitybdd.rest.SerenityRest;
import org.json.JSONObject;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
public class Employee {
private static final String URL = "https://reqres.in/api";
public Response response;
int id = 2;
@Test
public void verifyValidUser() {
response = SerenityRest
.given()
.contentType("application/json")
.header("Content-Type", "application/json")
.when()
.get(URL + "/users/" + id);
SerenityRest.restAssuredThat(response -> response.statusCode(200)
.body("data.id", equalTo(2))
.body("data.email", equalTo("janet.weaver@reqres.in"))
.body("data.first_name", equalTo("Janet"))
.body("data.last_name", equalTo("Weaver")));
}
@Test
public void verifyCreateUser() {
JSONObject data = new JSONObject();
data.put("name", "Test");
data.put("job", "Test Architect");
response = SerenityRest
.given()
.contentType("application/json")
.header("Content-Type", "application/json")
.body(data.toString())
.when()
.post(URL + "/users");
SerenityRest.restAssuredThat(response -> response.statusCode(201)
.body("name", equalTo("Test"))
.body("job", equalTo("Test Architect")));
}
}
Another approach is that all tests are split into reusable blocks called “steps“. The main principle of the BDD approach is that we are trying to keep complexity to a high-level human-readable level. First of all, let’s create a separate package to keep our steps. It is always better to keep them separate as it shows which classes contain reusable components. It is better to make steps smaller. So let’s make separate reusable steps from our tests:
package org.example.steps;
import io.restassured.response.Response;
import net.serenitybdd.annotations.Step;
import net.serenitybdd.rest.SerenityRest;
import org.json.JSONObject;
import static org.hamcrest.Matchers.equalTo;
public class EmployeeSteps {
private static final String URL = "https://reqres.in/api";
public Response response;
@Step("Search user by id {0}")
public void sendUser(int id) {
response = SerenityRest
.given()
.contentType("application/json")
.header("Content-Type", "application/json")
.when()
.get(URL + "/users/" + id);
}
@Step("Create a new user")
public void createUser() {
JSONObject data = new JSONObject();
data.put("name", "Test");
data.put("job", "Test Architect");
response = SerenityRest
.given()
.contentType("application/json")
.header("Content-Type", "application/json")
.body(data.toString())
.when()
.post(URL + "/users");
}
@Step("Verify the status code {0}")
public void verifyStatusCode(int expectedStatusCode) {
SerenityRest.restAssuredThat(response -> response.statusCode(expectedStatusCode));
}
@Step("Verify the user id {0}")
public void verifyId(int expectedId) {
SerenityRest.restAssuredThat(response -> response.body("data.id", equalTo(expectedId)));
}
@Step("Verify the user first name {0}")
public void verifyFirstName(String expectedFirstName) {
SerenityRest.restAssuredThat(response -> response.body("data.first_name", equalTo(expectedFirstName)));
}
@Step("Verify the user last name {0}")
public void verifyLastName(String expectedLastName) {
SerenityRest.restAssuredThat(response -> response.body("data.last_name", equalTo(expectedLastName)));
}
@Step("Verify the user email {0}")
public void verifyEmail(String expectedEmail) {
SerenityRest.restAssuredThat(response -> response.body("data.email", equalTo(expectedEmail)));
}
@Step("Verify the new user name {0}")
public void verifyNewUserName(String expectedName) {
SerenityRest.restAssuredThat(response -> response.body("name", equalTo(expectedName)));
}
@Step("Verify the new user job {0}")
public void verifyNewUserJob(String expectedJob) {
SerenityRest.restAssuredThat(response -> response.body("job", equalTo(expectedJob)));
}
}
Now our steps are ready. Let’s refactor the main class with our tests:
One more important thing we added is the “@RunWith(SerenityRunner.class)” annotation on top of the class. As we have now organized our structure to meet some basic Serenity principles, we are ready to run the test using Serenity. This time (after we added the mentioned annotation) these tests will be run using the “SerenityRunner”. For that we can use exactly the same command to run our tests:
mvn clean verify
The output of the above program is
In the console, you should find printed messages for tests to start. At the same time under the target directory you can find the HTML-generated report we were talking about before:
You can open the report in any browser:
If you click on any test you should see a detailed description of the test steps:
One of the most important features of the Serenity and REST Assured integration is that by using detailed reporting, you can easily validate all requests and response details even if you are not adding any logs inside tests. Like the example above, for each executed REST request you can click the button “REST Query” and get a detailed request and response description:
There is another very useful Serenity Report – Serenity Symmary.html
As you can see, Serenity and REST Assured provide you with a wonderful combination. REST Assured keeps API testing clean and easy to maintain, while Serenity gives you outstanding test reporting and flexibility in running and grouping your tests inside a test suite.
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
GitLab is a web-based Git repository that provides free open and private repositories, issue-following capabilities, and wikis. It is a complete DevOps platform that enables professionals to perform all the tasks in a project—from project planning and source code management to monitoring and security.
In this tutorial, I will explain how we can clone a project from GitLab in Eclipse.
Implementation Steps
Step 1– Go to GitLab and select the project which you want to clone. Click on the blue color “Clone” button then copy the hyperlink as shown in the image. You can either Clone with SSH or Clone with HTTPS.
Step 2 – Open Eclipse and go to “File > Import” in eclipse as shown in the image.
Step 3– A window will pop up in which select Git Folder. Under the Git folder, select the option – “Projects from Git(with smart import)”as shown in the image.
Click on the “NEXT” button.
Step 4 – A new window will pop up in which select the option – “Clone URI” as shown in the image.
Click on the “NEXT” button.
Step 5 – Another window will pop up in which you have to paste the“GitLab Repository URL” and also“GitLab UserID and Password” and click on the “Next” button.
URI – This is the URL that we have cloned from GitLab in Step 1. Host – gitlab.com Repository path – path of the project in GitLab (This is auto-populated after entering URI)
Authentication User – Username of GitLab Password – password of GitLab
Step 6 – Select master and select “When fetching a commit, also fetch its tags“.
Click on the “Next” button.
Step 7 – Select the “Folder directory” in which you want to import the repository.
Click on the “Finish” button.
Step 8 – We have successfully imported the GitLab Repository as shown in the below image.
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
I have created a lot of tutorials on creating Test Frameworks by integrating JUnit4 with Selenium, Cucumber, Serenity, Rest API, Springboot. This tutorial explain the steps to Integrate Cucumber7 with JUnit5.
JUnit 5 is composed of several different modules from three different sub-projects.
Cucumber and Selenium need Java to be installed on the system to run the tests. Click here to know How to install Java.
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 know 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 know How to install Maven.
The Cucumber Eclipse plugin is a plugin that allows eclipse to understand the Gherkin syntax. The Cucumber Eclipse Plugin highlights the keywords present in Feature File. Click here to know more – Install Cucumber Eclipse Plugin.
Step 7 – Create a feature file in src/test/resources
Below is a sample feature file. Feature file should be saved as an extension of .feature. Add the test scenarios in this feature file. I have added sample test scenarios. The test scenarios are written in Gherkins language.
LoginPage.feature
@LoginPage
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 |
@FaceBookLink
Scenario: Verify FaceBook Icon on Login Page
Then User should be able to see FaceBook Icon
@LinkedInLink
Scenario: Verify LinkedIn Icon on Login Page
Then User should be able to see LinkedIn Icon
ForgetPasswordPage.feature
@ForgetPassword
Feature: Login to ForgotPassword Page
Background:
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
@ForgetPasswordLink
Scenario: Verify ForgetPassword link on Login Page
When User clicks on Forgot your Password Link
Then User should navigate to a new page
Step 8 – Create cucumber.properties file in src/test/resources
We need to create the junit-platform.properties file in the src/test/resources folder. Using a property file for reporting is quite helpful if you want to define several different properties.
cucumber.publish.enabled=true
Step 9 – Create a Helper class in src/main/java
We have used Page Object Model with Cucumber and TestNG. 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.
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 Locator classes in src/main/java
Create a locator class for each page that contains the detail of the locators of all the web elements. Here, I’m creating 3 locator classes – LoginPageLocators, HomePageLocators,and ForgotPasswordLocators.
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(id = "logInPanelHeading")
public WebElement titleText;
@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;
@FindBy(xpath = "//*[@href='https://www.linkedin.com/company/orangehrm/mycompany/']")
public WebElement linkedInIcon;
@FindBy(xpath = "//*[@href='https://www.facebook.com/OrangeHRM/']")
public WebElement faceBookIcon;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[4]/p")
public WebElement ForgotYourPasswordLink;
}
HomePageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePageLocators {
@FindBy(xpath = "//*[@id='app']/div[1]/div[2]/div[2]/div/div[1]/div[1]/div[1]/h5")
public WebElement homePageUserName;
}
ForgotPasswordLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class ForgotPasswordLocators {
@FindBy(xpath = "//*[@id='app']/div[1]/div[1]/div/form/h6")
public WebElement ForgotPasswordHeading;
}
Step 11 – Create Action classes in src/main/java
Create the action classes for 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, and ForgotPasswordActions.
LoginPageActions
In this class, the very first thing will do is to create the object of the LoginPageLocators class so that we should be able to access all the PageFactory elements. Secondly, create a public constructor of LoginPageActions class.
import org.openqa.selenium.support.PageFactory;
import com.example.locators.LoginPageLocators;
import com.example.utils.HelperClass;
public class LoginPageActions {
LoginPageLocators loginPageLocators = null;
public LoginPageActions() {
this.loginPageLocators = new LoginPageLocators();
PageFactory.initElements(HelperClass.getDriver(),loginPageLocators);
}
// Set user name in textbox
public void setUserName(String strUserName) {
loginPageLocators.userName.sendKeys(strUserName);
}
// Set password in password textbox
public void setPassword(String strPassword) {
loginPageLocators.password.sendKeys(strPassword);
}
// Click on login button
public void clickLogin() {
loginPageLocators.login.click();
}
// Get the title of Login Page
public String getLoginTitle() {
return loginPageLocators.titleText.getText();
}
// Get the title of Login Page
public String getErrorMessage() {
return loginPageLocators.errorMessage.getText();
}
// LinkedIn Icon is displayed
public Boolean getLinkedInIcon() {
return loginPageLocators.linkedInIcon.isDisplayed();
}
// FaceBook Icon is displayed
public Boolean getFaceBookIcon() {
return loginPageLocators.faceBookIcon.isDisplayed();
}
// Click on Forget Your Password link
public void clickOnForgetYourPasswordLink() {
loginPageLocators.ForgotYourPasswordLink.click();
}
public void login(String strUserName, String strPassword) {
// Fill user name
this.setUserName(strUserName);
// Fill password
this.setPassword(strPassword);
// Click Login button
this.clickLogin();
}
}
HomePageActions
import org.openqa.selenium.support.PageFactory;
import com.example.locators.HomePageLocators;
import com.example.utils.HelperClass;
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();
}
}
ForgotPasswordActions
import org.openqa.selenium.support.PageFactory;
import com.example.locators.ForgotPasswordLocators;
import com.example.utils.HelperClass;
public class ForgotPasswordActions {
ForgotPasswordLocators forgotPasswordLocators = null;
public ForgotPasswordActions() {
this.forgotPasswordLocators = new ForgotPasswordLocators();
PageFactory.initElements(HelperClass.getDriver(),forgotPasswordLocators);
}
// Get the Heading of Forgot Password page
public String getForgotPasswordPageText() {
return forgotPasswordLocators.ForgotPasswordHeading.getText();
}
}
Step 12 – Create a Step Definition file in src/test/java
Create the corresponding Step Definition file of the feature file.
LoginPageDefinitions
import org.junit.jupiter.api.Assertions;
import com.example.actions.ForgotPasswordActions;
import com.example.actions.HomePageActions;
import com.example.actions.LoginPageActions;
import com.example.utils.HelperClass;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
public class LoginPageDefinitions{
LoginPageActions objLogin = new LoginPageActions();
HomePageActions objHomePage = new HomePageActions();
ForgotPasswordActions objForgotPasswordPage = new ForgotPasswordActions();
@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);
// go the next page
}
@When("User clicks on Forgot your Password Link")
public void goToForgotYourPasswordPage() {
objLogin.clickOnForgetYourPasswordLink();
}
@Then("User should be able to login sucessfully and new page open")
public void verifyLogin() {
// Verify home page
Assertions.assertTrue(objHomePage.getHomePageText().contains("Employee Information"));
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessage(String expectedErrorMessage) {
// Verify home page
Assertions.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
}
@Then("User should be able to see LinkedIn Icon")
public void verifyLinkedInIcon( ) {
Assertions.assertTrue(objLogin.getLinkedInIcon());
}
@Then("User should be able to see FaceBook Icon")
public void verifyFaceBookIcon( ) {
Assertions.assertTrue(objLogin.getFaceBookIcon());
}
@Then("User should navigate to a new page")
public void verfiyForgetYourPasswordPage() {
Assertions.assertEquals(objForgotPasswordPage.getForgotPasswordPageText(), "Reset Password");
}
}
Step 13 – Create Hook class in src/test/java
Create the hook class that contains the Before and After hook to initialize the web browser and close the web browser.
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 14 – Create a Cucumber Test Runner class in 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.
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;
@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("com.example")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example")
public class CucumberRunnerTests {
}
Step 15 – Run the tests from Maven or Command Line
Use the below command to run the tests.
mvn clean verify
Step 16 – Cucumber Report Generation
Below is the image of the Cucumber Report generated using the Cucumber Service.
Serenity BDD is an open-source library that aims to make the idea of living documentation a reality.
What is Rest Assured?
Rest Assured is one of the most powerful libraries for testing RESTful API using Java language. Rest-Assured is a Java-based library that is used to test RESTful Web Services. This library behaves like a headless Client to access REST web services. 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. In this post, we will learn how to write high-quality, expressive REST API tests using Rest Assured and Serenity BDD.
Prerequisite
Java 17 installed
Maven installed
Eclipse or IntelliJ installed
Dependency List:
Java 17
Maven – 3.9.5
Serenity – 4.0.18
Serenity Rest Assured – 4.0.18
Serenity Cucumber – 4.0.18
Rest Assured – 5.3.2
JUnit – 4.13.2
Maven Surefire Plugin – 3.2.1
Maven Failsafe Plugin – 3.2.1
Maven Compiler Plugin – 3.11.0
Project Structure
Implementation Steps
Step 1 – Update Properties section in Maven pom.xml
Step 4 – Create a Feature filein src/test/resources
Create a features folder within src/test/resources to create test scenarios in the Feature file. Test Scenarios are created in a Feature File which contains an overall description of a feature as well as a number of scenarios. Feature files can be placed in different locations, but you can reduce the amount of configuration you need to do with serenity if you put them in the src/test/resources/features directory. In this feature file, will send a request, and the response should be of status “200” and employee name of “Tiger Nixon”. The feature file looks something like this:
Feature: Employee Details
@GetEmployee
Scenario: Get the details of employee
Given I send a request to endpoint
Then the API should return status 200
And Response should contains employee name "Tiger Nixon"
Step 5 – Create the Step Definition class or Glue Code
To use Rest-assured, Serenity provides class SerenityRest
import net.serenitybdd.rest.SerenityRest;
It is a Java method with an expression that is used to link it to Gherkin steps. When Cucumber executes a Gherkin step, it will look for a matching step definition to execute. These use annotations like @given, @when, and @then to match lines in the scenario to java methods
package org.example.definitions;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.restassured.response.Response;
import net.serenitybdd.rest.SerenityRest;
import static org.hamcrest.Matchers.equalTo;
public class EmployeeDefinitions {
private static final String URL = "http://dummy.restapiexample.com/api/v1/employee/1";
public Response response;
@Given("I send a request to endpoint")
public void sendRequest() {
response = SerenityRest.given().contentType("application/json").header("Content-Type", "application/json")
.when().get(URL);
}
@Then("the API should return status {int}")
public void verifyResponse(int status) {
SerenityRest.restAssuredThat(response -> response.statusCode(status));
}
@And("Response should contains employee name {string}")
public void verifyResponseContent(String expectedEmployeeName) {
SerenityRest.restAssuredThat(response -> response.body("data.employee_name", equalTo(expectedEmployeeName)));
}
}
Step 6 – Create Serenity Test Runner
Cucumber runs the feature files via JUnit and needs a dedicated Test Runner class to run the feature files. When you run the tests with serenity, you use the CucumberWithSerenity test runner. If the feature files are not in the same package as the test runner class, you also need to use the@CucumberOptionsclass to provide the root directory where the feature files can be found. It is the starting point for JUnit to start executing the tests. TestRunner class is created undersrc/test/java. The test runner to run all of the feature files looks like this:
import org.junit.runner.RunWith;
import io.cucumber.junit.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;
@RunWith(CucumberWithSerenity.class)
@CucumberOptions(plugin = { "pretty" }, features = "src/test/resources/features/Employee.feature", glue = {
"org.example.definitions" })
public class SerenityAPITestRunner {
}
Step 7 – Create serenity.properties file
Serenity.properties file is created at the root level.
serenity.project.name = Rest API Testing using Serenity, Cucumber and JUnit4
Step 8 – Serenity Tests Execution
You can run the tests from SerenityAPITestRunner or from the command line by
mvn clean verify
Test Execution Page looks like this as shown below image
Step 9 – Verify the Serenity Reports
A number of reports are generated, but we are concerned about index.html and serenity-summary.html.
The report is well-formatted and contains consolidated results. Reporting is one of the major pillars of Serenity. Serenity Report not only reports on whether a test scenario passes or fails but documents what it did, in a step-by-step narrative format. The below pic illustrates the test results for our first acceptance criteria:
The test report generated by Serenity is placed under target/site/serenity/index.html.
Index.html
The first tab is called “Overall Test Results” and it provides information about test statistics. This Overall Test Result shows the Scenario Results (No Of Test Cases Pass, No Of Test Cases Failed, No of Test Cases Pending, No Of Test Cases Ignored, No Of Test Cases Skipped).
In the below pic, the report shows the test scenario steps status and time taken for each step to execute.
With the use of the REST Query button, it’s possible to display query details. Visible details:
There is also the “Requirements” tab. When we have tests as part of our code base, all test results will be organized as associated with requirements.
There is also a “Features” tab. This page lists all the features that are part of your suite. If you expand that row you’ll see the bit of narrative text that is part of the current feature file.
Serenity-Summary.html
We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
We can create a JSON Object using a Map in Java. A JSON Object is a key-value pair and can be easily created using a Java Map. A Map in Java also represents a collection of key-value pairs.
To create a request body using JSON Object using HashMap, we need to add a Maven dependency.
I have created a simple Java map and filled it with the values that represent JSON properties.
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
public class Json_Demo {
@Test
public void passBodyAsMap() {
Map<String, String> map = new HashMap<String, String>();
map.put("employee_name", "MapTest");
map.put("employee_salary", "99999");
map.put("employee_age", "30");
map.put("profile_image", "test.png");
RestAssured.given()
.contentType(ContentType.JSON)
.body(map)
.log().all()
.when()
.post("https://dummy.restapiexample.com/api/v1/create")
.then()
.assertThat().statusCode(200)
.body("data.employee_name", equalTo("MapTest"))
.body("data.employee_age", equalTo("30"))
.body("data.employee_salary", equalTo("99999"))
.body("message", equalTo("Successfully! Record has been added.")).log().all();
}
}
The request body as well as the response body will look as shown below:-
Above one is a simple JSON Request Body. Let us take an example of a Complex Request Body or nested Request Body as shown below.
Let us create a Java program to understand this:
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
public class Json_Demo {
@Test
public void passBodyAsMultipleMap() {
// First JSON Object using Hash Map
Map<String, Object> data = new HashMap<String, Object>();
data.put("employee_name", "MapTest");
data.put("profile_image", "test.png");
// Second JSON Object using Hash Map
Map<String, String> msg = new HashMap<String, String>();
msg.put("updated_message", "Details of New Resource");
msg.put("employee_age", "30");
data.put("details", msg);
data.put("employee_salary", "99999");
RestAssured.given().contentType(ContentType.JSON).body(data).log().all()
// WHEN
.when().post("https://dummy.restapiexample.com/api/v1/create")
// THEN
.then().assertThat().statusCode(200).body("data.employee_name", equalTo("MapTest"))
.body("data.details.updated_message", equalTo("Details of New Resource"))
.body("data.details.employee_age", equalTo("30")).body("data.employee_salary", equalTo("99999"))
.body("message", equalTo("Successfully! Record has been added.")).log().all();
}
}
The request body as well as the response body will look as shown below image. The first part is the body of the request and the second part is the response provided by the API.
Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!