How to create Nested JSON Object using POJO – Jackson API

Last Updated On

HOME

In the previous tutorial, I explained the creation of JSON Array using POJO. In this tutorial, I will explain the creation of a nested JSON Object (JSON with multiple nodes) using POJO.

It is recommended to go through these tutorials to understand POJO, JSON Object, and JSON Array.

How to create JSON Object Payload using POJO – Jackson API

How to create JSON Array Payload using POJO – Jackson API

We are using Jackson API for Serialization and Deserialization. So, add the Jackson dependency to the project. We need to add the below-mentioned dependencies to run this example.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>RestAssured_JUnit4_Demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <junit.version>4.13.2</junit.version>
        <jackson.version>2.17.1</jackson.version>
        <hamcrest.version>1.3</hamcrest.version>
    </properties>

    <dependencies>

        <!-- JUnit4 Dependency -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- Jackson Dependency -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!-- Hamcrest Dependency -->
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-all</artifactId>
            <version>${hamcrest.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

We will use a nested JSON Object (a combination of JSON Arrays and objects).

{
  "companyName": "QAAutomation",
  "companyEmailId": "qaautomation@org.com",
  "companyNumber": "+353891234121", 
  "companyAddress": "12, HeneryStreet, Dublin, D12PW20", 
  "supportedSalaryBanks": [
    "AIB",
    "BOI",
    "PSB"
  ],
  "employee": [
    { 
	  "firstName" : "Vibha",
      "lastName" : "Singh",
      "age" : 30,
      "salary" : 75000.0,
      "designation" : "Manager",
      "contactNumber" : "+919999988822",
      "emailId" : "abc@test.com"
    },
    {
      "firstName" : "Neha",
      "lastName" : "Verma",
      "age" : 25,
      "salary" : 60000.0,
      "designation" : "Lead",
      "contactNumber" : "+914442266221",
      "emailId" : "xyz@test.com"
    },
    {
      "firstName" : "Rajesh",
      "lastName" : "Gupta",
      "age" : 20,
      "salary" : 40000.0,
      "designation" : "Intern",
      "contactNumber" : "+919933384422",
      "emailId" : "pqr@test.com"
    }
  ],
  "contractors": [
    {
      "firstName": "John",
      "lastName": "Mathew",
      "contractFrom": "Jan-2018",
      "contractTo": "Aug-2022",
	  "contactNumber" : "+919631384422"
    },
    {
      "firstName": "Seema",
      "lastName": "Prasad",
      "contractFrom": "Jun-2019",
      "contractTo": "Jun-2023"
	  "contactNumber" : "+919688881422"
    }
  ],
  "companyPFDeails": {
    "pfName": "XYZ",
    "pfYear": 2020,
    "noOfEmployees": 100
  }
}

It is very overwhelming to handle this type of nested JSON Object at a glance. So, we will split this into small parts or objects. So basically, we can split the above JSON into 4 parts – Employee, Contractors, CompanyPFDetails, and NestedPOJODemo.

companyName, companyEmailId, companyNumber, and companyAddress are 1:1 mapping in the payload. supportedSalaryBanks is an array of String values.

	private String companyName;
	private String companyEmailId;
	private String companyNumber;
	private String companyAddress;
	private List<String> supportedSalaryBanks;

Employee has value as an array of employees. There is no ready-made data type to represent elements of this array as a whole. So here we need to create a POJO class that can contain all details of an employee.

To represent an array of Employees and Contractors

	List<Employee> employee;
	List<Contractors> contractors;

Create a POJO class for CompanyPFDetails and add it to the main payload.

Now, let us see various POJO classes.

Employee POJO Class

public class Employee {

	// private variables or data members of pojo class
	private String firstName;
	private String lastName;
	private int age;
	private double salary;
	private String designation;
	private String contactNumber;
	private String emailId;

	// Getter and setter methods
	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	public String getDesignation() {
		return designation;
	}

	public void setDesignation(String designation) {
		this.designation = designation;
	}

	public String getContactNumber() {
		return contactNumber;
	}

	public void setContactNumber(String contactNumber) {
		this.contactNumber = contactNumber;
	}

	public String getEmailId() {
		return emailId;
	}

	public void setEmailId(String emailId) {
		this.emailId = emailId;
	}

}

Contractors POJO Class

public class Contractors {

	// private variables or data members of pojo class
	private String firstName;
	private String lastName;
	private String contractFrom;
	private String contractTo;
	private String contactNumber;

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getContractFrom() {
		return contractFrom;
	}

	public void setContractFrom(String contractFrom) {
		this.contractFrom = contractFrom;
	}

	public String getContractTo() {
		return contractTo;
	}

	public void setContractTo(String contractTo) {
		this.contractTo = contractTo;
	}

	public String getContactNumber() {
		return contactNumber;
	}

	public void setContactNumber(String contactNumber) {
		this.contactNumber = contactNumber;
	}

}

CompanyPFDetails POJO Class

public class CompanyPFDetails {

	private String pfName;
	private int pfYear;
	private int noOfEmployees;

	public String getPfName() {
		return pfName;
	}

	public void setPfName(String pfName) {
		this.pfName = pfName;
	}

	public int getPfYear() {
		return pfYear;
	}

	public void setPfYear(int pfYear) {
		this.pfYear = pfYear;
	}

	public int getNoOfEmployees() {
		return noOfEmployees;
	}

	public void setNoOfEmployees(int noOfEmployees) {
		this.noOfEmployees = noOfEmployees;
	}

}

NestedPOJODemo class

public class NestedPOJODemo {

	// private variables or data members of pojo class
	private String companyName;
	private String companyEmailId;
	private String companyNumber;
	private String companyAddress;
	private List<String> supportedSalaryBanks;
	List<Employee> employee;
	List<Contractors> contractors;
	CompanyPFDetails companyPFDetails;

	public String getCompanyName() {
		return companyName;
	}

	public void setCompanyName(String companyName) {
		this.companyName = companyName;
	}

	public String getCompanyEmailId() {
		return companyEmailId;
	}

	public void setCompanyEmailId(String companyEmailId) {
		this.companyEmailId = companyEmailId;
	}

	public String getCompanyNumber() {
		return companyNumber;
	}

	public void setCompanyNumber(String companyNumber) {
		this.companyNumber = companyNumber;
	}

	public String getCompanyAddress() {
		return companyAddress;
	}

	public void setCompanyAddress(String companyAddress) {
		this.companyAddress = companyAddress;
	}

	public List<String> getSupportedSalaryBanks() {
		return supportedSalaryBanks;
	}

	public void setSupportedSalaryBanks(List<String> supportedSalaryBanks) {
		this.supportedSalaryBanks = supportedSalaryBanks;
	}

	public List<Employee> getEmployee() {
		return employee;
	}

	public void setEmployee(List<Employee> employee) {
		this.employee = employee;
	}

	public List<Contractors> getContractors() {
		return contractors;
	}

	public void setContractors(List<Contractors> contractors) {
		this.contractors = contractors;
	}

	public CompanyPFDetails getCompanyPFDetails() {
		return companyPFDetails;
	}

	public void setCompanyPFDetails(CompanyPFDetails companyPFDetails) {
		this.companyPFDetails = companyPFDetails;
	}

}

Let’s create a JSON Payload using the above POJO classes.

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class NestedPOJOTest {

    @Test
    public void createNestedPOJO() throws IOException {

        NestedPOJODemo demo = new NestedPOJODemo();
        demo.setCompanyName("QAAutomation");
        demo.setCompanyEmailId("qaautomation@org.com");
        demo.setCompanyNumber("+353891234121");
        demo.setCompanyAddress("12, HeneryStreet, Dublin, D12PW20");

        List<String> supportedSalaryBanks = new ArrayList<String>();
        supportedSalaryBanks.add("AIB");
        supportedSalaryBanks.add("BOI");
        supportedSalaryBanks.add("PSB");
        demo.setSupportedSalaryBanks(supportedSalaryBanks);

        // First Employee
        Employee emp1 = new Employee();
        emp1.setFirstName("Vibha");
        emp1.setLastName("Singh");
        emp1.setAge(30);
        emp1.setSalary(75000);
        emp1.setDesignation("Manager");
        emp1.setContactNumber("+919999988822");
        emp1.setEmailId("abc@test.com");

        // Second Employee
        Employee emp2 = new Employee();
        emp2.setFirstName("Neha");
        emp2.setLastName("Verms");
        emp2.setAge(35);
        emp2.setSalary(60000);
        emp2.setDesignation("Lead");
        emp2.setContactNumber("+914442266221");
        emp2.setEmailId("xyz@test.com");

        // Third Employee
        Employee emp3 = new Employee();
        emp3.setFirstName("Rajesh");
        emp3.setLastName("Gupta");
        emp3.setAge(20);
        emp3.setSalary(40000);
        emp3.setDesignation("Intern");
        emp3.setContactNumber("+919933384422");
        emp3.setEmailId("pqr@test.com");

        // Creating a List of Employees
        List<Employee> employeeList = new ArrayList<Employee>();
        employeeList.add(emp1);
        employeeList.add(emp2);
        employeeList.add(emp3);
        demo.setEmployee(employeeList);

        // First Contractor
        Contractors contractor1 = new Contractors();
        contractor1.setFirstName("John");
        contractor1.setLastName("Mathew");
        contractor1.setContractFrom("Jan-2018");
        contractor1.setContractTo("Aug-2022");
        contractor1.setContactNumber("+919631384422");

        // Second Contractor
        Contractors contractor2 = new Contractors();
        contractor2.setFirstName("Seema");
        contractor2.setLastName("Mathew");
        contractor2.setContractFrom("Jun-2019");
        contractor2.setContractTo("Jun-2023");
        contractor2.setContactNumber("+919688881422");

        // Creating a List of Contractors
        List<Contractors> contractorList = new ArrayList<Contractors>();
        contractorList.add(contractor1);
        contractorList.add(contractor2);
        demo.setContractors(contractorList);

        CompanyPFDetails pf = new CompanyPFDetails();
        pf.setPfName("XYZ");
        pf.setPfYear(2020);
        pf.setNoOfEmployees(100);
        demo.setCompanyPFDetails(pf);

        ObjectMapper mapper = new ObjectMapper();
        String nestedJsonPayload = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(demo);
        System.out.println(nestedJsonPayload);

    }

}

Here, I have used ObjectMapper for reading and writing JSON, either to and from basic POJOs (Plain Old Java Objects), or to and from a general-purpose JSON Tree Model (JsonNode), as well as related functionality for performing conversions.

We can save this JSON payload in a file in the project or any location of your choice. Here, I’m saving this Nested JSON Payload in a file within src/test/resources.

String userDir = System.getProperty("user.dir");
mapper.writerWithDefaultPrettyPrinter().writeValue(new File(userDir + "\\src\\test\\resources\\NestedEmployeePayload.json"), demo);
	}

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

How to create JSON Array Payload using POJO – Jackson API

HOME

In the previous tutorial, I explained the creation of JSON Object using POJO. In this tutorial, will create a JSON Array Payload using POJO.

To learn about POJO, please refer to this tutorial.

You can refer to these tutorials to understand various ways of using Jackson API

Serialization – How to create JSON Payload from Java Object – Jackson API

How to create JSON Array Payload using POJO – Jackson API

How to create Nested JSON Object using POJO – Jackson API

Serialization – How to convert Map to JSON string using Jackson API

JSON Array is a collection of JSON Objects. In the below example, will create a list of employees.

The sample JSON Array structure looks like the image as shown below:-

{
  "firstName" : "Vibha",
  "lastName" : "Singh",
  "age" : 30,
  "salary" : 75000.0,
  "designation" : "Manager",
  "contactNumber" : "+919999988822",
  "emailId" : "abc@test.com"
  
  "firstName" : "Neha",
  "lastName" : "Verma",
  "age" : 25,
  "salary" : 60000.0,
  "designation" : "Lead",
  "contactNumber" : "+914442266221",
  "emailId" : "xyz@test.com"
  
  "firstName" : "Rajesh",
  "lastName" : "Gupta",
  "age" : 20,
  "salary" : 40000.0,
  "designation" : "Intern",
  "contactNumber" : "+919933384422",
  "emailId" : "pqr@test.com"
}

We need to create an Employee class that contains private data members and the corresponding getter and setter methods of these data members.

Below is an Employee Class with private data members, as well as the corresponding getter and setter methods of these data members. Every IDE provides a shortcut to create these getter and setter methods.

public class Employee {

	// private variables or data members of POJO class
	private String firstName;
	private String lastName;
	private int age;
	private double salary;
	private String designation;
	private String contactNumber;
	private String emailId;

	// Getter and setter methods
	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	public String getDesignation() {
		return designation;
	}

	public void setDesignation(String designation) {
		this.designation = designation;
	}

	public String getContactNumber() {
		return contactNumber;
	}

	public void setContactNumber(String contactNumber) {
		this.contactNumber = contactNumber;
	}

	public String getEmailId() {
		return emailId;
	}

	public void setEmailId(String emailId) {
		this.emailId = emailId;
	}

}

Serialization – Serialization is a process where you convert an Instance of a Class (Object of a class) into a Byte Stream. Here, we are converting Employee class object to JSON Array representation or Object.

Deserialization – It is the reverse of serializing. In this process, we will read the Serialized byte stream from the file and convert it back into the Class instance representation. Here, we are converting a JSON Array to an Employee class object.

We are using Jackson API for Serialization and Deserialization. So, add the Jackson dependency to the project.

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.0</version>
</dependency>

Below is the class where we will assign values to the data members by using getter methods (Serialization).

public class EmployeeArrayTest {

	@Test
	public void createEmployeeArray() {

		Employee emp1 = new Employee();
		emp1.setFirstName("Vibha");
		emp1.setLastName("Singh");
		emp1.setAge(30);
		emp1.setSalary(75000);
		emp1.setDesignation("Manager");
		emp1.setContactNumber("+919999988822");
		emp1.setEmailId("abc@test.com");

		Employee emp2 = new Employee();
		emp2.setFirstName("Neha");
		emp2.setLastName("Verms");
		emp2.setAge(35);
		emp2.setSalary(60000);
		emp2.setDesignation("Lead");
		emp2.setContactNumber("+914442266221");
		emp2.setEmailId("xyz@test.com");

		Employee emp3 = new Employee();
		emp3.setFirstName("Rajesh");
		emp3.setLastName("Gupta");
		emp3.setAge(20);
		emp3.setSalary(40000);
		emp3.setDesignation("Intern");
		emp3.setContactNumber("+919933384422");
		emp3.setEmailId("pqr@test.com");

		// Creating a List of Employees
		List<Employee> employeeList = new ArrayList<Employee>();
		employeeList.add(emp1);
		employeeList.add(emp2);
		employeeList.add(emp3);

		// Converting a Java class object to a JSON Array Payload as string
		ObjectMapper mapper = new ObjectMapper();
		try {
			String allEmployeeJson = mapper.writeValueAsString(employeeList);
			String employeeListPrettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(employeeList);
			System.out.println(allEmployeeJson);
			System.out.println(employeeListPrettyJson);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
	}

ObjectMapper is imported from:-

import com.fasterxml.jackson.databind.ObjectMapper;

In the below example, we are deserializing the JSON Array Payload to Java objects.

    @Test
	public void getEmployeeArray() {

		Employee emp1 = new Employee();
		emp1.setFirstName("Vibha");
		emp1.setLastName("Singh");
		emp1.setAge(30);
		emp1.setSalary(75000);
		emp1.setDesignation("Manager");
		emp1.setContactNumber("+919999988822");
		emp1.setEmailId("abc@test.com");

		Employee emp2 = new Employee();
		emp2.setFirstName("Neha");
		emp2.setLastName("Verms");
		emp2.setAge(35);
		emp2.setSalary(60000);
		emp2.setDesignation("Lead");
		emp2.setContactNumber("+914442266221");
		emp2.setEmailId("xyz@test.com");

		Employee emp3 = new Employee();
		emp3.setFirstName("Rajesh");
		emp3.setLastName("Gupta");
		emp3.setAge(20);
		emp3.setSalary(40000);
		emp3.setDesignation("Intern");
		emp3.setContactNumber("+919933384422");
		emp3.setEmailId("pqr@test.com");

		// Creating a List of Employees
		List<Employee> employeeList = new ArrayList<Employee>();
		employeeList.add(emp1);
		employeeList.add(emp2);
		employeeList.add(emp3);

		// Converting a Java class object to a JSON Array Payload as string

		ObjectMapper mapper = new ObjectMapper();
		String allEmployeeJson = null;

		try {
			allEmployeeJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(employeeList);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

		List<Employee> allEmployeeDetails = null;
		try {
			allEmployeeDetails = mapper.readValue(allEmployeeJson, new TypeReference<List<Employee>>() {
			});
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

		for (Employee emp : allEmployeeDetails) {

			System.out.println("===================================");

			System.out.println("First Name of employee : " + emp.getFirstName());
			System.out.println("Last Name of employee : " + emp.getLastName());
			System.out.println("Age of employee : " + emp.getAge());
			System.out.println("Salary of employee : " + emp.getSalary());
			System.out.println("Designation of employee : " + emp.getDesignation());
			System.out.println("Contact Number of employee : " + emp.getContactNumber());
			System.out.println("EmailId of employee : " + emp.getEmailId());
		}
	}

If you want to read the data from a file placed on Desktop, below is the sample code for the same.

  @Test
	public void readArrayJsonFromFile() throws IOException {

		ObjectMapper mapper = new ObjectMapper();

		// Converting Employee json string to Employee class object
		List<Employee> allEmployeeDetails = null;
		try {
			allEmployeeDetails = mapper.readValue(new File(
					"C:\\Users\\Vibha\\Desktop\\EmployeeList.json"),
					new TypeReference<List<Employee>>() {
					});
		} catch (StreamReadException e) {
			e.printStackTrace();
		} catch (DatabindException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}


		for (Employee emp : allEmployeeDetails) {

			System.out.println("######################################");

			System.out.println("First Name of employee : " + emp.getFirstName());
			System.out.println("Last Name of employee : " + emp.getLastName());
			System.out.println("Age of employee : " + emp.getAge());
			System.out.println("Salary of employee : " + emp.getSalary());
			System.out.println("Designation of employee : " + emp.getDesignation());
			System.out.println("Contact Number of employee : " + emp.getContactNumber());
			System.out.println("EmailId of employee : " + emp.getEmailId());
		}
	}

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Throw in Java

HOME

In Java we have already defined exception classes such as ArithmeticException, NullPointerException, ArrayIndexOutOfBounds exception etc. These exceptions are set to trigger on different conditions. For example when we divide a number by zero, this triggers ArithmeticException, when we try to access the array element out of its bounds then we get ArrayIndexOutOfBoundsException.


The Java throw keyword is used to throw an exception explicitly. We specify the exception object which is to be thrown. The Exception has some message with it that provides the error description. These exceptions may be related to user inputs, server, etc.

We can throw either checked or unchecked exceptions in Java by throw keyword. It is mainly used to throw a custom exception.

throw Instance
Example:
throw new ArithmeticException("/ by zero");

Instance must be of type Throwable or a subclass of Throwable. For example Exception is a sub-class of Throwable and user defined exceptions typically extend Exception class.Data types such as int, char, floats or non-throwable classes cannot be used as exceptions.

The flow of execution of the program stops immediately after the throw statement is executed and the nearest enclosing try block is checked to see if it has a catch statement that matches the type of exception. If it finds a match, controlled is transferred to that statement otherwise next enclosing try block is checked and so on. If no matching catch is found then the default exception handler will halt the program.

public class Example1 {

	public static void test() {
		try {
			throw new ArithmeticException(" Hello ");
		} catch (ArithmeticException e) {
			System.out.println("Caught throw exception in method.");
			throw e; // rethrowing the exception
		}
	}

	public static void main(String args[]) {
		try {
			test();
		} catch (ArithmeticException e) {
			System.out.println("Caught exception in main.");
		}
	}
}

Output
Caught throw exception in method.
Caught exception in main.

Let us take another example where we have created the validate method that takes integer value as a parameter. If the age is less than 18, we are throwing the ArithmeticException otherwise print a message “Person is eligible to drive!!” .

public class Example1 {

	public static void validate(int age) {
		if (age < 18) {

			// throw Arithmetic exception if not eligible to drive
			throw new ArithmeticException("Person is not eligible to drive");
		} else {
			System.out.println("Person is eligible to drive!!");
		}
	}

	public static void main(String args[]) {
		validate(13);

	}
}

Exception in thread "main" java.lang.ArithmeticException: Person is not eligible to drive
	at ExceptionsExamples.Example1.validate(Example1.java:9)
	at ExceptionsExamples.Example1.main(Example1.java:16)

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Difference between == and equals() method in Java

HOME

What is == operator ?

To compare primitives and objects we use “==” equality operator in java which is a binary operator given in the java programming language

What is equals() method in java?

The Java String class equals() method compares the two given strings based on the content of the string. If any character is not matched, it returns false. If all characters are matched, it returns true.

Difference between == and equals method in java

1. == is an operator whereas equals() is a method of Object class.

2. == operator compares reference or memory location of objects in the heap, whether they point to the same location or not. Whenever we create an object using the operator new it will create a new memory location for that object. So we use the == operator to check memory location or address of two objects are the same or not.
equals() method  is used to compare the state of two objects or the contents of the objects.

3. Equals method can be overridden but you can’t override behavior of “==” operator.

Below is an example which shows the use of == and equals.

public class EqualsDemo1 {

	public static void main(String[] args) {
		String str1 = new String("JAVATUTORIAL");
		String str2 = new String("JAVATUTORIAL");

		// Reference comparison
		System.out.println("Check str1 == str2 : " + (str1 == str2));

		// Content comparison
		System.out.println("Check str1.equals(str2) : " + str1.equals(str2));

		// integer-type
		System.out.println("Check Integer Type :" + (10 == 10));

		// char-type
		System.out.println("Check Char Type :" + ('a' == 'a'));
	}
}

Output
Check str1 == str2 : false
Check str1.equals(str2) : true
Check Integer Type :true
Check Char Type :true

Let us take an example where both strings have same characters and same length but in lower case.

public class EqualsDemo2 {

	public static void main(String[] args) {
		String str1 = new String("JAVATUTORIAL");
		String str2 = new String("jAVATUTORIAL");

		// Reference comparison
		System.out.println("Check str1 == str2 : " + (str1 == str2));

		// Content comparison
		System.out.println("Check str1.equals(str2) : " + str1.equals(str2));
	}
}

Output
Check str1 == str2 : false
Check str1.equals(str2) : false

Let us take an example where both Integer refer to the same object.

public class EqualsDemo3 {

	public static void main(String[] args) {

		Integer i = new Integer(10);
		Integer j = i;

		System.out.println("Check i == j : " + (i == j));
}

Output
Check i == j : true

Let us take an example where both Integer1 and Integer2 refer to the different objects.

public class EqualsDemo4 {

	public static void main(String[] args) {

		Integer i = new Integer(10);
		Integer j = new Integer(10);

		System.out.println("Check i == j : " + (i == j));
	}
}

Output
Check i == j : false

That’s it for today. Have a wonderful learning.

Throws in Java

HOME

The throws keyword in Java is used in the signature of method to indicate that this method might throw one of the listed type of exceptions. The caller to these methods has to handle the exception using a try-catch block.

Syntax of throws

return_type method_name() throws ExceptionType1, ExceptionType2 …{  
//method code  
} 

As you can see from the above syntax, we can use throws to declare multiple exceptions.

Below is an example of throws exception. Here, as you can see IOException is listed as an exception. This exception is handled by a try catch block when findFile() method is called.

public class ThrowsExample {

	public static void findFile() throws IOException {

		File file= new File("C:\\test.txt");
		FileInputStream stream = new FileInputStream(file);

	}

	public static void main(String[] args) {
		try {
			findFile();
		} catch (IOException e) {
			System.out.println(e);
		}
	}
}

Output
java.io.FileNotFoundException: C:\test.txt (The system cannot find the file specified)

When we run this program, if the file test.txt does not exist, FileInputStream throws a FileNotFoundException which extends the IOException class.

If a method does not handle exceptions, the type of exceptions that may occur within it must be specified in the throws clause so that methods further up in the call stack can handle them or specify them using throws keyword themselves.

The findFile() method specifies that an IOException can be thrown. The main() method calls this method and handles the exception if it is thrown.

In a program, if there is a chance of raising an exception then compiler always warn us about it and compulsorily we should handle that checked exception, Otherwise we will get compile time error saying unreported exception XXX must be caught or declared to be thrown. To prevent this compile time error we can handle the exception in two ways: 

  1. We have caught the exception i.e. we have handled the exception using try/catch block.
  2. We have declared the exception i.e. specified throws keyword with the method.

Case 1 : Handle Exception Using try-catch block

In case we handle the exception, the code will be executed fine whether exception occurs during the program or not.

class Demo {
	void method() throws IOException {
		throw new IOException("IOException Occurred");
	}
}

public class TestThrowsExample2 {

	public static void main(String[] args) {
		try {
			Demo demo = new Demo();
			demo.method();
		} catch (Exception e) {
			System.out.println("Exception handled");
		}

		System.out.println("Continue the program...");
	}
}

Output
Exception handled
Continue the program...

Case 2: We declare the exception, if exception does not occur, the code will be executed fine.

class Test {
	void method() throws IOException {
		System.out.println("No Exception");
	}
}

public class TestThrowsExample3 {

	public static void main(String[] args) {
		try {
			Test test = new Test();
			test.method();
		} catch (Exception e) {
			System.out.println("Exception handled");
		}

		System.out.println("Continue the program...");
	}
}

Output
No Exception
Continue the program...

Case 3 :  We declare the exception and the exception occurs, it will be thrown at runtime because throws does not handle the exception.

class TestDemo {
	void method() throws IOException {
		throw new IOException("IOException Occurred");
	}
}

public class TestThrowsExample4 {

	public static void main(String[] args) throws IOException {
		TestDemo test = new TestDemo();
		test.method();
		System.out.println("Continue the program...");

	}

}

Output
Exception in thread "main" java.io.IOException: IOException Occurred

DataProvider in TestNG using Excel

HOME

In the previous tutorial, I explained the DataProvider in TestNG without using Excel. In this tutorial, I will explain the use of Excel in DataProvider for TestNG.

I have created an Excel – SearchInBing.xlsx and placed it on the Desktop. You can create a Test Data folder in your project and place the Excel file within it. So, my datasheet looks like the below:

Next, we will create a DataProvider method that will use another method to read the Excel file & create a 2D object from the row & column values of the Excel and return the same value, so that our test script can use it. The code for it would look like the below:

import org.apache.poi.ss.usermodel.Cell;
import org.testng.annotations.DataProvider;

import java.io.FileInputStream;
import java.io.IOException;

import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ExcelDataProvider {

    @DataProvider(name = "excelData")
    public Object[][] excelDataProvider() throws IOException {

        // We are creating an object from the excel sheet data by calling a method that
        // reads data from the excel stored locally in our system
        Object[][] arrObj = getExcelData(
                C:\\Users\\Vibha\\Desktop\\SearchInBing.xlsx","Details");
        return arrObj;
}

    // This method handles the excel - opens it and reads the data from the
    // respective cells using a for-loop & returns it in the form of a string array
    public String[][] getExcelData(String fileName, String sheetName) throws IOException {
        String[][] data = null;
        try {

            FileInputStream fis = new FileInputStream(fileName);
            XSSFWorkbook workbook = new XSSFWorkbook(fis);
            XSSFSheet sheet = workbook.getSheet(sheetName);
            XSSFRow row = sheet.getRow(0);
            int noOfRows = sheet.getPhysicalNumberOfRows();
            int noOfCols = row.getLastCellNum();
            Cell cell;
            data = new String[noOfRows - 1][noOfCols];

            for (int i = 1; i < noOfRows; i++) {
                for (int j = 0; j < noOfCols; j++) {
                    row = sheet.getRow(i);
                    cell = row.getCell(j);
                    data[i - 1][j] = cell.getStringCellValue();
                }
            }
        } catch (Exception e) {
            System.out.println("The exception is: " + e.getMessage());
        }
        return data;
    }
}

Now, create a class that contains the test code. By default, the data provider will be looked for in the current test class or one of its base classes. If you want to put your data provider in a different class, it needs to be a static method or a class with a no-arg constructor, and you specify the class where it can be found in the data provider class attribute.

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class DataProviderWithExcelDemo {
    
  WebDriver driver;

   @BeforeMethod
    public void setUp() {
        System.out.println("Start test");
        WebDriverManager.chromedriver().setup();
        driver = new ChromeDriver();
        driver.get("https://www.bing.com");
        driver.manage().window().maximize();

    }

    @Test(dataProvider = "excelData", dataProviderClass = ExcelDataProvider.class)
    public void search(String keyWord1, String keyWord2) {

        WebElement txtBox = driver.findElement(By.id("sb_form_q"));
        txtBox.sendKeys(keyWord1, " ", keyWord2);
        System.out.println("Keyword entered is : " + keyWord1 + " " + keyWord2);
        txtBox.sendKeys(Keys.ENTER);
        System.out.println("Search results are displayed.");
        System.out.println("RESULT: "+ driver.getTitle());
        Assert.assertTrue(driver.getPageSource().contains(keyWord1));
    }

    @AfterMethod
    public void burnDown() {
        driver.quit();
    }

}

To run the code, right-click and Select – TestNG Test.

The Execution status will look like this, as shown below:

This test execution generates reports under the test-output folder.

We are concerned about two reports – index.html and emailable-report.html.

Below is the image of emailable-report.html.

Below is the image of index.html.

See how easy it is to read data from Excel and use it in the Test Code using DataProvider.

I hope you have enjoyed this tutorial. Happy Learning!!

Serenity BDD with Gradle and Cucumber for Web Application

HOME

In the previous tutorial, I have explained about Integration Testing of SpringBoot Application with Serenity BDD and Cucumber in Maven project. This tutorial describes the creation of the Gradle Java Project to test a web application using Cucumber6 and JUnit4.

In this tutorial, I will explain creating a framework for the testing of Web Applications in Cucumber BDD.

Pre-Requisite

  1. Java 11 installed
  2. Gradle installed
  3. Eclipse or IntelliJ installed

This framework consists of:

  1. Serenity – 2.6.0
  2. Serenity Cucumber – 2.6.0
  3. Java 11
  4. JUnit – 4.13.2
  5. Gradle – 7.2

Steps to setup Gradle Java Project for Web Application using Serenity, Cucumber6 and JUnit4

  1. Download and Install Java on the system
  2. Download and setup Eclipse IDE on the system
  3. Setup Gradle on System and create a new Gradle Project
  4. Update repositories, plugins, and dependencies to the Gradle project
  5. Create a feature file under src/test/resources
  6. Create the Step Definition class or Glue Code for the Test Scenario
  7. Create a Serenity Cucumber Runner class
  8. Create serenity.conf file under src/test/resources
  9. Create serenity.properties file at the root of the project
  10. Run the tests through commandline which generates Serenity Report

Step 1- Download and Install Java

Cucumber and Rest-Assured 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 system

The Eclipse IDE (integrated development environment) provides strong support for Java developers. Click here to know How to install Eclipse.

Step 3 – Setup Gradle

To build a test framework, we need to add several dependencies to the project. This can be achieved by any build Tool. I have used Gradle Build Tool. Click here to know How to install Gradle. Click here to know How to create a Gradle Java project. Below is the structure of the Gradle project.

Step 4 – Update repositories, plugin, and dependencies to the Gradle project

defaultTasks 'clean', 'test', 'aggregate'

repositories {
    mavenLocal()
    jcenter()
}

buildscript {
    repositories {
        mavenLocal()
        jcenter()
    }
    dependencies {
        classpath("net.serenity-bdd:serenity-gradle-plugin:2.4.24")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'net.serenity-bdd.aggregator'

sourceCompatibility = 11
targetCompatibility = 11

dependencies {
   
    testImplementation 'net.serenity-bdd:serenity-core:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-cucumber6:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-screenplay:2.6.0'
    testImplementation 'net.serenity-bdd:serenity-screenplay-webdriver:2.6.0'
    testImplementation 'junit:junit:4.13.1'
}

test {
    testLogging.showStandardStreams = true
    systemProperties System.getProperties()
}

gradle.startParameter.continueOnFailure = true

test.finalizedBy(aggregate)

Step 5 – Create a feature file under src/test/resources

A Feature File is an entry point to the Cucumber tests. This is a file where you will describe your tests in Descriptive language (Like English). A feature file can contain a scenario or can contain many scenarios in a single feature file. Below is an example of Feature file.

Feature: Login to HRM  

   @ValidCredentials
   Scenario: Login with valid credentials
   
    Given User is on Home page
    When User enters username as "Admin"
    And User enters password as "admin123"
    Then User should be able to login successfully

Step 6 – Create the Step Definition class or Glue Code for the Test Scenario

The steps definition file stores the mapping between each step of the test scenario defined in the feature file with a code of the function to be executed. So, now when Cucumber executes a step of the scenario mentioned in the feature file, it scans the step definition file and figures out which function is to be called.

Create a StepDefinition class for LoginPage.feature

public class LoginPageDefinitions {
 
    @Steps
    StepLoginPage loginPage;
 
    @Steps
    StepDashboardPage dashPage;
 
    @Steps
    StepForgetPasswordPage forgetpasswordPage;
 
    @Given("User is on Home page")
    public void openApplication() {
        loginPage.open();
        System.out.println("Page is opened");
    }
 
    @When("User enters username as {string}")
    public void enterUsername(String userName) {
        System.out.println("Enter Username");
        loginPage.inputUserName(userName);
    }
 
    @When("User enters password as {string}")
    public void enterPassword(String passWord) {
        loginPage.inputPassword(passWord);
 
        loginPage.clickLogin();
    }
 
    @Then("User should be able to login successfully")
    public void clickOnLoginButton() {
        dashPage.loginVerify();
    }
   
}

Serenity Step Libraries integrate smoothly into Cucumber Step Definition files; all you need to do is to annotate a step library variable with the @Steps annotation.  Methods that represent a business task or action (inputUserName()), and that will appear in the reports as a separate step, are annotated with the @Step annotation. Here, I have created two StepClasses – StepLoginPage and StepDashboardPage

public class StepLoginPage extends PageObject {
 
    @Step("Enter Username")
    public void inputUserName(String userName) {
        $(By.name("txtUsername")).sendKeys((userName));
    }
 
    @Step("Enter Password")
    public void inputPassword(String passWord) {
        $(By.name("txtPassword")).sendKeys((passWord));
    }
 
    @Step("Click Submit Button")
    public void clickLogin() {
        $(By.name("Submit")).click();
    } 
 
}

StepDashboardPage

public class StepDashboardPage extends PageObject {
 
    @Step("Successful login")
    public void loginVerify() {
        String dashboardTitle = $(By.id("welcome")).getText();
        assertThat(dashboardTitle, containsString("Welcome"));
    }
}

Step 7 – Create a Serenity Cucumber Runner class

import org.junit.runner.RunWith;

import io.cucumber.junit.CucumberOptions;
import net.serenitybdd.cucumber.CucumberWithSerenity;

@RunWith(CucumberWithSerenity.class)
@CucumberOptions(plugin = {}, features = "lib/src/test/resources/features", glue = "serenitygradleautomation.definitions")

public class CucumberTestSuite {

}

Step 8 – Create serenity.conf file under src/test/resources

Serenity.conf file is used to specify various features like the type of webdriver used, various test environments, run tests in headless mode, and many more options.

webdriver {
    driver = firefox
}
 
 
environments {
  default {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/"
  }
  dev {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/dev"
  }
  staging {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/staging"
  }
  prod {
    webdriver.base.url = "https://opensource-demo.orangehrmlive.com/prod"
  }
}

Step 9 – Create serenity.properties file at the root of the project

serenity.project.name = Serenity and Cucumber Gradle Demo

Step 10 – Run the tests through commandline which generates Serenity Report

Open the command line and go to the location where gradle.build of the project is present and type the below command.

gradle test

The Serenity report is generated under /lib/target/site/serenity.

Serenity Report

Below is the image of Overall Test Result with steps and screenshots.

That’s it! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

Integration of Allure Report with Selenium and TestNG

HOME

In this tutorial, I will explain how to integrate Allure Report (one of the very famous Reports) with Selenium and TestNG.

What is Allure Framework?

Allure is an open-source framework designed to create interactive and comprehensive test reports by Yandex QA Team.

The below example covers the implementation of Allure Reports in Selenium using TestNG, Java, and Maven.

Prerequisite:

  1. Java 11 installed
  2. Maven installed
  3. Eclipse or IntelliJ installed
  4. Environment variables JAVA_HOME, MAVEN_HOME and ALLURE_HOME are correctly configured

Dependency List

  1. Selenium – 3.141.59
  2. Java 11
  3. TestNG – 7.4.0
  4. Maven – 3.8.1
  5. Allure Report – 2.14.0
  6. Allure TestNG – 2.14.0

Implementation Steps

  1. Update the Properties section in Maven pom.xml
  2. Add Selenium, TestNG, and Allure TestNG dependencies in POM.xml
  3. Update Build Section of pom.xml in Allure Report Project.
  4. Create Pages and Test Code for the pages
  5. Create testng.xml for the project
  6. Run the Test and Generate Allure Report

Step 1 – Update the Properties section

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <selenium.version>3.141.59</selenium.version>
    <testng.version>7.4.0</testng.version>
    <allure.testng.version>2.14.0</allure.testng.version>
    <maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <aspectj.version>1.9.6</aspectj.version>
    <maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
  </properties>

Step 2 – Add Selenium, TestNG, and Allure TestNG dependencies in POM.xml

<dependencies>
    
     <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>${selenium.version}</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.testng/testng -->
    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>${testng.version}</version>
      <scope>test</scope>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/io.qameta.allure/allure-testng -->
    <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-testng</artifactId>
        <version>${allure.testng.version}</version>
        <scope>test</scope>
    </dependency>
  </dependencies>

Step 3 – Update the Build Section of pom.xml in the Allure Report Project

<build>
       
       <plugins>
   <!-- Compiler plug-in -->
  
           <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${maven.compiler.source}</source> <!--For JAVA 8 use 1.8-->
                    <target>${maven.compiler.target}</target> <!--For JAVA 8 use 1.8-->
                </configuration>
            </plugin>
            
     <!-- Added Surefire Plugin configuration to execute tests -->       
          <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>${maven.surefire.plugin.version}</version>
              <configuration>
                    <suiteXmlFiles>
                        <suiteXmlFile>TestNG.xml</suiteXmlFile>
                    </suiteXmlFiles>
                 <argLine>
                    -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                 </argLine>
             </configuration>          
             <dependencies>
            
            <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjweaver</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
            </dependencies>
        </plugin>
      </plugins>
  </build>

Step 4 – Create Pages and Test Code for the pages

Below is the sample project which uses Selenium and TestNG which is used to generate an Allure Report.

We have 2 pages. Below is the code for Login Page which contains all the web elements and methods related to that web elements.

LoginPage.java

public class LoginPage {

	WebDriver driver;

	By userName = By.name("txtUsername");

	By password = By.name("txtPassword");

	By titleText = By.id("logInPanelHeading");

	By login = By.id("btnLogin");

	By errorMessage = By.id("spanMessage");

	public LoginPage(WebDriver driver) {
		this.driver = driver;
	}

	// Set user name in textbox
	public void setUserName(String strUserName) {
		driver.findElement(userName).sendKeys(strUserName);
	}

	// Set password in password textbox
	public void setPassword(String strPassword) {
		driver.findElement(password).sendKeys(strPassword);
	}

	// Click on login button
	public void clickLogin() {
		driver.findElement(login).click();
	}

	@Step("Verify title of Login Page")
	public void verifyPageTitle() {
		String loginPageTitle = driver.findElement(titleText).getText();
		Assert.assertTrue(loginPageTitle.contains("LOGIN Panel"));
	}

    /* Failed Test */
	@Step("Verify error message when invalid credentail is provided")
	public void verifyErrorMessage() {
		String invalidCredentialErrorMessage = driver.findElement(errorMessage).getText();
		Assert.assertTrue(invalidCredentialErrorMessage.contains("Incorrect Credentials"));
	}

	@Step("Enter username and password")
	public void login(String strUserName, String strPasword) {

		// Fill user name
		this.setUserName(strUserName);

		// Fill password
		this.setPassword(strPasword);

		// Click Login button
		this.clickLogin();

	}
}

Dashboard.java

public class DashboardPage {

	WebDriver driver;

	By dashboardPageTitle = By.id("welcome");

	By options = By.cssSelector(
			"#dashboard-quick-launch-panel-menu_holder > table > tbody > tr > td:nth-child(1) > div > a > span");

	public DashboardPage(WebDriver driver) {
		this.driver = driver;

	}

	@Step("Verify title of Dashboard page")
	public void verifyDashboardPageTitle() {
		String DashboardPageTitle = driver.findElement(dashboardPageTitle).getText();
		Assert.assertTrue(DashboardPageTitle.contains("Welcome"));
	}

	@Step("Verify Quick Launch Options on Dashboard page")
	public void verifyQuickLaunchOptions() {
		String QuickLaunchOptions = driver.findElement(options).getText();
		Assert.assertTrue(QuickLaunchOptions.contains("Assign Leave"));
	}

}

Below are the Test classes for Login Page and Dashboard Page. Here, we have BaseTest Class also which contains the common methods needed by other test pages.

BaseTest.java

public class BaseTest {

	public static WebDriver driver;
	LoginPage objLogin;
	DashboardPage objDashboardPage;

	@Step("Start the application")
	@BeforeMethod
	public void setup() {
		System.setProperty("webdriver.gecko.driver",
				"C:\\Users\\Vibha\\Software\\geckodriver-v0.26.0-win64\\geckodriver.exe");
		driver = new FirefoxDriver();
		driver.manage().window().maximize();
		driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
		driver.get("https://opensource-demo.orangehrmlive.com/");
	}

	@Step("Stop the application")
	@AfterMethod
	public void close() {
		driver.close();
	}
}

LoginTests.java

@Epic("Web Application Regression Testing")
@Feature("Login Page Tests")
@Listeners(TestExecutionListener.class)
public class LoginTests extends BaseTest {

	LoginPage objLogin;
	DashboardPage objDashboardPage;

	@Severity(SeverityLevel.NORMAL)
	@Test(priority = 0, description = "Verify Login Page")
	@Description("Test Description : Verify the title of Login Page")
	@Story("Title of Login Page")
	public void verifyLoginPage() {

		// Create Login Page object
		objLogin = new LoginPage(driver);

		// Verify login page text
		objLogin.verifyPageTitle();
	}

   /* Failed Test */
	@Severity(SeverityLevel.BLOCKER)
	@Test(priority = 1, description = "Login with invalid username and password")
	@Description("Test Description : Login Test with invalid credentials")
	@Story("Unsuccessful Login to Application")
	public void invalidCredentialTest() {

		// Create Login Page object
		objLogin = new LoginPage(driver);
		objLogin.login("test", "test123");

		// Verify login page text
		objLogin.verifyErrorMessage();

	}

}

We can order tests by severity by using @Severity annotation. Click here to know more about other Allure annotations.

DashboardTests.java

@Epic("Web Application Regression Testing")
@Feature("Dashboard Page Tests")
@Listeners(TestExecutionListener.class)
public class DashboardTests extends BaseTest {

	LoginPage objLogin;
	DashboardPage objDashboardPage;

	@Severity(SeverityLevel.BLOCKER)
	@Test(priority = 0, description = "Verify Dashboard Page")
	@Description("Test Description : After successful login to application opens Dashboard page")
	@Story("Successful login of application opens Dashboard Page")

	public void DasboardTest() {

		objLogin = new LoginPage(driver);

		// login to application
		objLogin.login("Admin", "admin123");

		// go the dashboard page
		objDashboardPage = new DashboardPage(driver);

		// Verify dashboard page
		objDashboardPage.verifyQuickLaunchOptions();

	}

}

We can group tests with @Epic@Feature, and @Stories annotations. Click here to know more about other Allure annotations.

TestExecutionListener.class

We can add attachments to our reports by using @Attachment annotation. It can return String, byte [], etc.  I need to add @Listeners({ TestExecutionListener.class }) declaration at the top of the test classes. Click here to know more about other Allure annotations.

public class TestExecutionListener implements ITestListener {

	@Attachment(value = "Screenshot of {0}", type = "image/png")
	public byte[] saveScreenshot(String name, WebDriver driver) {
		return (byte[]) ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
	}

	@Override
	public void onTestFailure(ITestResult result) {
		saveScreenshot(result.getName(), BaseTest.driver);
	}

}

Step 5 – Create testng.xml for the project

TestNG.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name = "Allure Reports">
  <test name = "Login Page Tests">
    <classes>
          <class name = "com.example.TestNGAllureReportDemo.tests.LoginTests"/>
         
          </classes>
          </test> 
    <test name =" Dashboard Tests">   
    <classes> 
          <class name = "com.example.TestNGAllureReportDemo.tests.DashboardTests"/>
          </classes>
    </test>

</suite>

Step 6 – Run the Test and Generate Allure Report

To run the tests, use the below command

mvn clean test

In the below image, we can see that one test failed and two passed out of three tests.

To create an Allure Report, use the below command

allure serve

This will generate the beautiful Allure Test Report as shown below.

Allure Report Dashboard

The overview page hosts several default widgets representing the basic characteristics of your project and test environment.

  1. Statistics – overall report statistics.
  2. Launches – if this report represents several test launches, statistics per launch will be shown here.
  3. Behaviors – information on results aggregated according to stories and features.
  4. Executors – information on test executors that were used to run the tests.
  5. History Trend – if tests accumulated some historical data, it’s a trend that will be calculated and shown on the graph.
  6. Environment – information on the test environment.

Categories in Allure Report

The categories tab gives you a way to create custom defects classifications to apply for test results. There are two categories of defects – Product Defects (failed tests) and Test Defects (broken tests).

Suites in Allure Report

On the Suites tab a standard structural representation of executed tests, grouped by suites and classes can be found.

Graphs in Allure Report

Graphs allow you to see different statistics collected from the test data: status breakdown or severity and duration diagrams.

Timeline in Allure Report

The timeline tab visualizes retrospective of tests execution, allure adaptors collect precise timings of tests, and here on this tab, they are arranged accordingly to their sequential or parallel timing structure.

Behaviors of Allure Report

This tab groups test results according to Epic, Feature, and Story tags.

Packages in Allure Report

The packages tab represents a tree-like layout of test results, grouped by different packages.

If you click on the (highlighted tab), it will show the test execution report in the below format.

Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!

Additional tutorials on Allure Reports:

Integration Testing of SpringBoot using RestAssured

HOME

In this tutorial, I am going to build an automation framework to test Springboot application with Rest Assured and JUnit4 only.

  1. What is Rest Assured?
  2. Dependency List
  3. Sample SpringBoot Application
  4. Implementation Steps
    1. Add SpringbootTest and Rest-Assured dependencies to the project
    2. Create a test file under src/test/java and write the test code
    3. Run the tests from JUnit
    4. Run the tests from Command Line

What is Rest Assured?

REST Assured is a Java DSL for simplifying the testing of REST-based services built on top of HTTP Builder. It supports POST, GET, PUT, DELETE, OPTIONS, PATCH, and HEAD requests and can be used to validate and verify the response to these requests.

The rest-Assured library also provides the ability to validate the HTTP Responses received from the server. For e.g. we can verify the Status code, Status message, Headers, and even the Body of the response. This makes Rest-Assured a very flexible library that can be used for testing.

Dependency List:

  1. Springboot – 3.2.3
  2. Java 17
  3. JUnit – 4.13.2
  4. Maven – 3.9.6
  5. RestAssured – 5.3.2
  6. Junit Vintage

Below is the sample SpringBoot application used for the testing.

The Spring Boot Application class is generated with Spring Initializer. This class acts as the launching point for the application.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootDemoApplication.class, args);
	}

}

The JPA Entity is any Java POJO, which can represent the underlying table structure. As our service is based on the Student table, we will create a Student Entity object.

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;


@Entity
public class Student {

    @Id
    @GeneratedValue
    private Long id;

    @NotNull
    @Size(min = 4, message = "Name should have atleast 4 characters")
    private String name;

    @NotBlank(message = "passportNumber is mandatory")
    private String passportNumber;

    public Student() {
        super();
    }

    public Student(Long id, String name, String passportNumber) {
        super();
        this.id = id;
        this.name = name;
        this.passportNumber = passportNumber;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassportNumber() {
        return passportNumber;
    }

    public void setPassportNumber(String passportNumber) {
        this.passportNumber = passportNumber;
    }
}

The Repository represents the DAO layer, which typically does all the database operations. Thanks to Spring Data, who provides the implementations for these methods. Let’s have a look at our StudentRepository, which extends the JpaRepository. There are no method declarations here in the StudentRepository. That is because Spring Data’s JpaRepository has already declared basic CRUD methods.

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository<Student, Long>{

}

Spring Rest Controller exposes all services on the student resource. RestController used for the below example is shown below.

import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
import java.util.List;
import java.util.Optional;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

@RestController
public class StudentController {

    @Autowired
    private StudentRepository studentRepository;

    @GetMapping("/students")
    public List<Student> retrieveAllStudents() {
        return studentRepository.findAll();
    }

    @GetMapping("/students/{id}")
    public EntityModel<Student> retrieveStudent(@PathVariable long id) {
        Optional<Student> student = studentRepository.findById(id);

        if (!student.isPresent())
            throw new StudentNotFoundException("id-" + id);

        EntityModel<Student> resource = EntityModel.of(student.get());

        WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllStudents());

        resource.add(linkTo.withRel("all-students"));

        return resource;
    }

    @PostMapping("/students")
    public ResponseEntity<Object> createStudent(@Valid @RequestBody Student student) {
        Student savedStudent = studentRepository.save(student);

        URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
                .buildAndExpand(savedStudent.getId()).toUri();

        return ResponseEntity.created(location).build();

    }

    @DeleteMapping("/students/{id}")
    public void deleteStudent(@PathVariable long id) {
        studentRepository.deleteById(id);
    }

    @PutMapping("/students/{id}")
    public ResponseEntity<Object> updateStudent(@Valid @RequestBody Student student, @PathVariable long id) {

        Optional<Student> studentOptional = studentRepository.findById(id);

        if (!studentOptional.isPresent())
            return ResponseEntity.notFound().build();

        student.setId(id);

        studentRepository.save(student);

        return ResponseEntity.noContent().build();
    }
}

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class StudentNotFoundException extends RuntimeException {

    public StudentNotFoundException(String exception) {
        super(exception);
    }

}

spring.jpa.defer-datasource-initialization=true
insert into student values(10001,'Annie', 'E1234567');
insert into student values(20001,'John', 'A1234568');
insert into student values(30001,'David','C1232268');
insert into student values(40001,'Amy','D213458');

Implementation Steps

Step 1 – Add SpringbootTest and Rest-Assured dependencies to the project

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.3</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>SpringBoot_Demo</name>
  <description>Demo project for Spring Boot</description>

  <properties>
    <java.version>17</java.version>
    <junit.version>4.13.2</junit.version>
    <rest-assured.version>5.3.2</rest-assured.version>
    <maven.compiler.plugin.version>3.12.1</maven.compiler.plugin.version>
    <maven.surefire.plugin.version>3.2.3</maven.surefire.plugin.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-hateoas</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <version>${rest-assured.version}</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.junit.vintage</groupId>
      <artifactId>junit-vintage-engine</artifactId>
      <scope>test</scope>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven.compiler.plugin.version}</version>
        <configuration>
          <source>${maven.compiler.source}</source>
          <target>${maven.compiler.target}</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${maven.surefire.plugin.version}</version>
        <configuration>
          <testFailureIgnore>true</testFailureIgnore>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Step 2 – Create a test file under src/test/java and write the test code

package org.example;

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.ValidatableResponse;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringbootDemoTests {

    private final static String BASE_URI = "http://localhost";

    @LocalServerPort
    private int port;

    private ValidatableResponse validatableResponse;

    private ValidatableResponse validatableResponse1;

    @Before
    public void configureRestAssured() {
        RestAssured.baseURI = BASE_URI;
        RestAssured.port = port;
    }

    /* Get operation - Get the details of a Student */
    @Test
    public void listUsers() {

        validatableResponse = given()
                .contentType(ContentType.JSON)
                .when()
                .get("/students")
                .then()
                .assertThat().statusCode(200);

    }

    /* Get operation - Get the details of a Student */
    @Test
    public void listAUser() {

        validatableResponse = given()
                .contentType(ContentType.JSON)
                .when()
                .get("/students/30001")
                .then()
                .assertThat().log().all().statusCode(200)
                .body("id",equalTo(30001))
                .body("name",equalTo("David"))
                .body("passportNumber",equalTo("C1232268"));;
    }

    /* Create operation - Create a new Student */
    @Test
    public void createAUser() throws JSONException {

        JSONObject newStudent = new JSONObject();

        newStudent.put("name", "Timmy");
        newStudent.put("passportNumber", "ZZZ12345");

        validatableResponse = given()
                .contentType(ContentType.JSON).body(newStudent.toString())
                .when()
                .post("/students")
                .then()
                .log().all().assertThat().statusCode(201);

        /* Verify that a new Student is created */
        validatableResponse1 = given()
                .contentType(ContentType.JSON)
                .when()
                .get("/students/1")
                .then()
                .log().all().assertThat().statusCode(200)
                .body("id",equalTo(1))
                .body("name",equalTo("Timmy"))
                .body("passportNumber",equalTo("ZZZ12345"));

    }

    /* Update operation - Update PassportNumber of a Student */
    @Test
    public void updateAUser() throws JSONException {

        JSONObject newStudent = new JSONObject();

        newStudent.put("name", "John");
        newStudent.put("passportNumber", "YYYY1234");

        validatableResponse = given()
                .contentType(ContentType.JSON).body(newStudent.toString())
                .when()
                .put("/students/20001")
                .then()
                .log().all().assertThat().statusCode(204);

        /* Verify that the updated Student has updated PassportNumber */
        validatableResponse1 = given()
                .contentType(ContentType.JSON)
                .when()
                .get("/students/20001")
                .then()
                .log().all().assertThat().statusCode(200)
                .body("id",equalTo(20001))
                .body("name",equalTo("John"))
                .body("passportNumber",equalTo("YYYY1234"));

    }

    /* Delete operation - Delete a Student */
    @Test
    public void deleteAUser() throws JSONException {

        validatableResponse = given()
                .contentType(ContentType.JSON)
                .when()
                .delete("/students/10003")
                .then()
                .log().all().assertThat().statusCode(200);


        /* Verify that the deleted Student Request returns STATUS 404 */
        validatableResponse1 = given()
                .contentType(ContentType.JSON)
                .when()
                .get("/students/10003")
                .then()
                .log().all().assertThat().statusCode(404);

    }
}

When a class is annotated with @RunWith or extends a class annotated with @RunWith, JUnit will invoke the class it references to run the tests in that class instead of the runner built into JUnit.

SpringRunner is an alias for the SpringJUnit4ClassRunner. Here, we have simply annotated a JUnit 4-based test class with @RunWith(SpringRunner.class). The Spring TestContext Framework provides generic, annotation-driven unit and integration testing support that is agnostic of the testing framework in use (JUnit, TestNG).

We build the test class with @SpringBootTest annotation which starts up an Application Context used throughout our test. In the classes property of @SpringBootTest annotation, we can specify which configuration classes build our Application Context. By default, @SpringBootTest annotation does not provide any web environment.
In order to set up a test web server we need to use @SpringBootTest’s webEnvironment annotation.
There are a few modes in which the web server can be started.

  • RANDOM_PORT – this is a recommended option where a real, embedded web server starts on a random port
  • DEFINED_PORT – web server will start on an 8080 or a port defined in application.properties
  • MOCK – loads a mock web environment where embedded servers are not started up.

Step 3 – Run the tests from JUnit

Right-click Run as JUnit Tests (Eclipse)

Right Click and select Run SpringBootDemoTests (IntelliJ)

Step 4 – Run the tests from Command Line

Open a command prompt and use the below command to run the tests.

mvn clean test

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

How to Export Eclipse projects to GitLab

HOME

What is GitLab?

GitLab is the open DevOps platform, delivered as a single application that spans the entire software development lifecycle. If you’re not using GitLab, your DevOps lifecycle is likely spread across any number of applications. These silos take overhead to integrate, manage, configure, and maintain, slowing down your team and your deployments. Moving to a single application will speed up your workflow and help you deliver better software, faster. To know more about GitLab, click here.

In this article, we will see how to push an existing project to GitLab using Eclipse IDE.

Implementation Steps

Step 1 – Go to GitLab and select the project which you want to clone. Click on the blue color “Clone” button and then copy the hyperlink as shown in the image. You can either Clone with SSH or Clone with HTTPS.

Step 2 – Open Eclipse IDE and right-click on the project you want to push and go to the Team ->Share project.

Step 3 – It will add the project to the given repository as shown below image. Select the Finish button.

As you can see, the given project is Git Repository. If the project is not GIT Repository, refer to this tutorial – How to create a new Git Repository  to convert the project in GIT Repository.

Step 4 – Again, Right-Click on the project and go to the Team ->commit.

Step 5 – Select the files you want to commit and click green color + sign or Drag and Drop the files from “Unstaged Changes to Staged Changes“.

This is how the stage files looks like as shown below.

Step 6 – Write the commit message in “Commit Message” and click “Commit and Push“.

Step 7 – Fill in the below details in this window and click the “Preview” button.

URI – This is the URL that we have cloned from GitLab in Step 1.
Host – gitlab.com
Repository path – the path of the project in GitLab (This is auto-populated after entering URI)

Authentication
User – Username of GitLab
Password – password of GitLab

Step 8 – A new window will open which provides the detail of the Destination location of the project. Click the “Preview” button.

Step 9 – Push to the new branch of GitLab Repository and click the Push button.

Step 10 – As this is a new project with a master branch, you can see the whole project migrated in GitLab. If we are not using the master branch, but the local branch, then we need to create a merge request to merge the latest changes in the already existing project in GitLab.

Cheers!!Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!