SpringBoot Tutorials

 

HOME

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”. We take an opinionated view of the Spring platform and third-party libraries, so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration.

Chapter 1 Create your first SpringBoot application in IntelliJ
Chapter 2 SpringBoot Integration Test
Chapter 3 Integration Testing of Springboot with RestAssured
Chapter 4 Testing of SpringBoot REST Application using Serenity BDD and Rest Assured for GET Method
Chapter 5 Testing of SpringBoot REST Application using Serenity BDD and Rest Assured for POST Method to create a Resource
Chapter 6 Testing of SpringBoot REST Application using Serenity BDD and Rest Assured for PUT Method to update a Resource
Chapter 7 Testing of SpringBoot REST Application using Serenity BDD and Rest Assured for DELETE Method to delete a Resource
Chapter 8 Testing of SpringBoot Validation for RESTful Services
Chapter 9 Testing of SpringBoot Exception Handling
Chapter 10 SpringBoot WireMock
Chapter 11 SpringBoot Dependency Injection using Autowired

SpringBoot Testing with JUnit

Chapter 1 Integration Testing of Springboot with Cucumber and JUnit4
Chapter 2 Integration Testing of SpringBoot Application with Serenity BDD, Cucumber and JUnit4
Chapter 3 Testing of SpringBoot Application with Serenity and JUnit5
Chapter 4 Testing of SpringBoot Application with Serenity BDD, Cucumber and JUnit5
Chapter 5 Testing of SpringBoot Application with JUnit5

SpringBoot with TestNG

Chapter 1 Testing of SpringBoot Application with TestNG
Chapter 2 Integration Testing of Springboot with Cucumber and TestNG

SpringBoot with Gradle

Chapter 1 Testing of Gradle SpringBoot Application with Serenity, Cucumber and JUnit4
Chapter 2 Testing of Gradle SpringBoot Application with Serenity and JUnit5

CI/CD

Chapter 1 How to run SpringBoot tests with GitHub Actions
Chapter 2 How to run SpringBoot project in GitLab CI/CD

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

HOME

In this tutorial, I will explain the creation of JSON Object Payload with the help of POJO (Plain Old Java Object).

What is POJO?

POJO stands for Plain Old Java Object. It is a very simple object, and it has no bounds or we can say that it has no restrictions other than the Java language specification. Also, it does not require any classpath.

A big advantage of POJO is it increases the readability and reusability of our project code and developers find it easy when understanding the code. Also, POJO is easy to write and anyone can understand them easily.

Now let’s deep dive into some technical terms about the POJO. Below are a few points about the POJO are:

  1. A POJO should not have to extend prespecified classes.
  2. Secondly, a POJO should not have implemented any prespecified interface.
  3. Lastly, POJO should not contain prespecified annotations

A POJO class can follow some rules for better usability. These rules are:-

  1. Each variable should be declared as private just to restrict direct access.
  2. Each variable that needs to be accessed outside class may have a getter, a setter, or both methods. If the value of a field is stored after some calculations, then we must not have any setter method for that.
  3. It Should have a default public constructor.
  4. Can override toString(), hashcode, and equals() methods.

POJO classes are extensively used for creating JSON and XML payloads for API.

In the below example, let me create a simple JSON with some nodes which is actually a 1:1 mapping i.e. each key has a single value, and the type of values is mixed.

{
  "firstName" : "Vibha",
  "lastName" : "Singh",
  "age" : 30,
  "salary" : 75000.0,
  "designation" : "Manager",
  "contactNumber" : "+91999996712",
  "emailId" : "abc123@test.com"
}

Let us create variables in the POJO class now for the above JSON. Now, a class name Employee will be created with the private data members as mentioned in the above JSON. Since we have created all variables as private, then there should be a way to manipulate or retrieve these data. So we create the corresponding getter and setter methods for these data members.

It is very tedious to create getter and setter methods for all the data members for big JSON strings.  Every IDE gives you a shortcut to generate getter and setter methods.  Here, I am using Eclipse and creating these getter and setter methods.

Select all the data members and Right-click on the page. Then select Source and then select Generate Getter and Setter methods.

This opens a new screen as shown below.

You can select the data member for which you want to create the getter and setter method. I want to create the getter and setter methods for all the data members, so click on Select All and then click on the Generate Button. This will generate the getter and setter methods for all the data members.

Below is the sample code of the Employee table, which contains the data members needed for Employee JSON and their corresponding 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;
	}

}

Using the above POJO class, you can create any number of custom Employee objects and each object can be converted into a JSON Object and Each JSON object can be parsed into Employee POJO.

We will create a JSON object from POJO and vice versa now, which is generally called serialization and deserialization using Jackson APIs.

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 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 Object 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>

What is ObjectMapper ?

ObjectMapper provides functionality 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. It is also highly customizable to work both with different styles of JSON content and to support more advanced object concepts such as polymorphism and object identity.

Now, let us create a Test Class to show Serialization.

public class EmployeeTest {

	@Test
	public void serializationTest()  {

		Employee employee = new Employee();
		employee.setFirstName("Vibha");
		employee.setLastName("Singh");
		employee.setAge(30);
		employee.setSalary(75000);
		employee.setDesignation("Manager");

		// Converting a Java class object to a JSON payload as string
		ObjectMapper mapper = new ObjectMapper();
		String employeeJson = mapper.writeValueAsString(employee);
		String employeePrettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(employee);
		System.out.println(employeeJson);
		System.out.println(employeePrettyJson);
   }
}try {
			String employeeJson = mapper.writeValueAsString(employee);
			System.out.println(employeeJson);
			String employeePrettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(employee);
			System.out.println(employeePrettyJson);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}	
	}

The output of the above program is

Here, ObjectMapper from fasterxml.jackson.databind is used for Serialization.

writeValueAsString() is a method that can be used to serialize any Java value as a String.

writerWithDefaultPrettyPrinter() is used to pretty-print the JSON output. It is a Factory method for constructing ObjectWriter that will serialize objects using the default pretty printer for indentation.

I hope this has helped to clear your doubts regarding POJO and how to create JSON objects using POJO.

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

Filtering by tags in PyTest-BDD

HOME

@Login
Feature: Login Page

    @ValidCredentials
    Scenario: Successful Application Login
        Given User is on login page
        When User enter username "Admin" and password "admin123"
        Then User should be able to login successfully and new page open "Dashboard"

@ScenarioOutline
Feature: Scenario Outline Example

    @InvalidCredentials
    Scenario Outline: Invalid credentials generate error message
        Given User is on OrangeHome page
        When User enters username as "<username>" and password as "<password>"
        Then User should be able to see error message as "Invalid credentials"

      Examples:
       | username | password    |
       | admin    | admin12345  |
       | Admin123 | admin       |
       | 123      | admin       |


    @MissingUsername
    Scenario: Missing Username generates error
        Given User is on OrangeHome page
        When User enters username as " " and password as "admin123"
        Then User should be able to see error message as "Required" below the username field

pytest

[pytest]
markers =
    Login: mark all tests as login
    ValidCredentials: mark a test as a valid credentials (deselect with '-m "not ValidCredentials"')
	ScenarioOutline: mark all tests as ScenarioOutline
	InvalidCredentials: mark test as invalid credentials (deselect with '-m "not InvalidCredentials"')
	MissingUsername: mark test as missing username (deselect with '-m "not MissingUsername"')

 pytest -k "MissingUsername"

pytest -k  "Login"

pytest -m "ScenarioOutline and MissingUsername"

pytest -m "ValidCredentials or MissingUsername"

 pytest -m "not InvalidCredentials"

Scenario Outline in PyTest – BDD

HOME

pip install pytest
pip install pytest-bdd
pip install pytest-selenium (If using Selenium)

Feature: Scenario Outline Example

    @InvalidCredentials
    Scenario Outline: Unsuccessful Application Login
        Given User is on OrangeHome page
        When User enters username as "<username>" and password as "<password>"
        Then User should be able to see error message as "Invalid credentials"

      Examples:
       | username | password    |
       | admin    | admin12345  |
       | Admin123 | admin       |
       | 123      | admin       |

import pytest

from pytest_bdd import scenarios, given, when, then, parsers
from selenium import webdriver
from selenium.webdriver.common.by import By


# Constants
TIMEOUT = 5
URL = 'https://opensource-demo.orangehrmlive.com/'


# Scenarios
scenarios('../features/ScenarioOutline.feature')


# Fixtures
@pytest.fixture
def driver():
    driver = webdriver.Firefox()
    driver.implicitly_wait(TIMEOUT)
    driver.maximize_window()
    yield driver
    driver.quit()


# Given Steps
@given('User is on OrangeHome page')
def open_browser(driver):
    driver.get(URL)


# When Steps
@when(parsers.parse('User enters username as "{username}" and password as "{password}"'))
def search_phrase(driver, username, password):
    driver.find_element(By.NAME, "username").send_keys(username)
    driver.find_element(By.NAME, "password").send_keys(password)
    driver.find_element(By.XPATH, "//*[@class='oxd-form']/div[3]/button").click()


# Then Steps
@then(parsers.parse('User should be able to see error message as "{expected_error_message}"'))
def search_results(driver, expected_error_message):
    actual_error_message = driver.find_element(By.XPATH, "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/div/div[1]/div[1]/p").text
    assert actual_error_message == expected_error_message

scenarios('../features/ScenarioOutline.feature')

pytest stepdefinitions/test_scenario_outline_definitions.py -s

 pytest stepdefinitions/test_scenario_outline_definitions.py --html=Report.html 

PyTest – BDD (Behavioural Driven Development) with Selenium

HOME

pip install pytest
pip install pytest-bdd

pip install pytest-selenium

Feature: Login Page

    Scenario: Successful Application Login
        Given User is on login page
        When User enter username "Admin" and password "admin123"
        Then User should be able to login successfully and new page open "Dashboard"

import pytest

from pytest_bdd import scenarios, given, when, then, parsers
from selenium import webdriver
from selenium.webdriver.common.by import By

# Constants
TIMEOUT = 4
URL = 'https://opensource-demo.orangehrmlive.com/'


# Scenarios
scenarios('../features/Login.feature')


# Fixtures
@pytest.fixture
def driver():
    driver = webdriver.Firefox()
    driver.implicitly_wait(TIMEOUT)
    driver.maximize_window()
    yield driver
    driver.quit()


# Given Steps
@given('User is on login page')
def open_browser(driver):
    driver.get(URL)


# When Steps
@when(parsers.parse('User enter username "{username}" and password "{password}"'))
def search_phrase(driver, username, password):
    driver.find_element(By.NAME, "username").send_keys(username)
    driver.find_element(By.NAME, "password").send_keys(password)
    driver.find_element(By.XPATH, "//*[@class='oxd-form']/div[3]/button").click()


# Then Steps
@then(parsers.parse('User should be able to login successfully and new page open "{heading}"'))
def search_results(driver, heading):
    homepage_title = driver.find_element(By.XPATH, "//*[@class='oxd-topbar-header-breadcrumb']/h6").text
    assert homepage_title == heading

scenarios('../features/Login.feature')

pytest tests

pytest tests --html=Report.html -s

How to run Python Selenium tests with Jenkins

HOME

In the previous tutorial, we have seen the How to generate HTML Reports in Jenkins. In this tutorial, we show you how to integrate PyTest tests with Jenkins

Prerequisite:

Jenkin’s installed and started on the computer. The current Jenkins version is – 2.361.2

Implementation Steps

To generate HTML Report in Jenkins, we need to download HTML Publisher Plugin. Please refer to this tutorial to install the plugin – How to install Plugins in Jenkins.

Step 1: Create a new FreeStyle project

  1. Give the Name of the projectPyTestFramework_Demo
  2. Click on the Freestyle project. 
  3. Click on the OK button.

In the General section, enter the project description in the Description box – This is demo integration of Python with Selenium tests to run in Jenkins.

Step 2: Select a custom workspace

Mention the full path of the project in the Use custom workspace.

Select Source Code Management as None if the project is locally present on the machine.

Step 3: Build Management

Go to the Build section of the new job. Select “Execute Windows batch command”.

cd tests
pytest  --html=$WORKSPACE/Reports/report.html -s

Step 4: Select “Publish HTML reports” from “Post Build Actions”

Scroll down to “Post Build Actions” and click on the “Add Post Build Actions” drop-down list. Select “Publish HTML reports“. 

If you want to see where the report will be saved in Jenkins, go to the Dashboard -> PyTestFramework_Demo -> Workspace -> Reports ->report.html.

Enter the HTML directory to archive – Reports, Index page[s] – report.html, and Report title – HTML Report.

Click on the Apply and Save buttons.

We have created a new FreeStyle project “PyTestFramework_Demo” with the configuration to run the Python tests in windows batch.

Step 5: Execute the tests

Let’s execute it now by clicking on the “Build Now” button. 

Right-click on Build Number (here in my case it is #1).

Click on Console Output to see the result.

Step 6: View the HTML Report

Once the execution is completed, click on go “Back to Project“, and we could see a link to view the “HTML Report“.

We can see here that the HTML Report link is displayed in the Console.

Below is the HTML Report generated in Jenkins.

There are chances that the report won’t look very pretty. The reason is that CSS is stripped out because of the Content Security Policy in Jenkins.

The default rule set in Jenkins is:

sandbox; default-src 'none'; img-src 'self'; style-src 'self';

To know more about this, please refer to this tutorial – https://www.jenkins.io/doc/book/security/configuring-content-security-policy/.

We can customize Content Security Policy in Jenkins. But keep in mind that it should be done after checking with the Security team in your organization. This is a workaround solution. I can’t emphasize enough that this is not a standard practice.

Go to Manage Jenkins -> Manage Nodes and Clouds.

Click on the Script Console option.

Type in the following command and Press Run. If you see the output as ‘Result:’ then the protection is disabled. Re-Run your build and you can see that the new HTML files archived will have the CSS enabled.

System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","")

Re-run the HTMLReport_Demo project. Now you can see a properly rendered HTML Report.

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

Additional Tutorials

How to Install Python on Windows 11
How to install and setup Robot Framework for Python
How to rerun failed tests in Robot Framework
How to implement tagging in Robot Framework
 How to set variable values from Runtime command in Robot Framework
How to load data from CSV files in the Robot Framework?

Page Object Model Implementation of Python with Selenium – PyTest

HOME

pip install -U pytest

from selenium.webdriver import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec


class BasePage:

    def __init__(self, driver):
        self._driver = driver
        self._wait = WebDriverWait(self._driver, 10)
        self._action = ActionChains(self._driver)

    def find(self, locator):
        return self._driver.find_element(*locator)

from selenium.webdriver.common.by import By

from pages.base_page import BasePage


class LoginPage(BasePage):
    username = (By.NAME, "username")
    password = (By.NAME, "password")
    submit_button = (By.XPATH, "//*[@class='oxd-form']/div[3]/button")
    error_message = (By.XPATH, "//*[@class='orangehrm-login-error']/div[1]/div[1]/p")

    def execute_login(self, username: str, password: str):
        self.find(self.username).send_keys(username)
        self.find(self.password).send_keys(password)
        self.find(self.submit_button).click()

    def actual_error(self):
        return self.find(self.error_message).text

from selenium.webdriver.common.by import By

from pages.base_page import BasePage


class DashboardPage(BasePage):
    heading = (By.XPATH, "//*[@class='oxd-topbar-header-breadcrumb']/h6")
    assign_leave_option = (By.XPATH, "//*[@title='Assign Leave']")
    time_at_work_option = (By.XPATH, "//*[@title='Time at Work']")

    def actual_heading(self):
        return self.find(self.heading).text

    def assign_leave_displayed(self):
        return self.find(self.assign_leave_option).is_displayed()

    def time_at_work_displayed(self):
        return self.find(self.assign_leave_option).is_displayed()

import pytest
from selenium import webdriver


@pytest.fixture(scope="class")
def driver():

    driver = webdriver.Chrome()
    driver.get("https://opensource-demo.orangehrmlive.com/")
    driver.maximize_window()
    driver.implicitly_wait(2)
    yield driver
    driver.close()

import pytest

from pages.dashboard_page import DashboardPage
from pages.login_page import LoginPage


def test_login_negative(driver, get_data):

    login_page = LoginPage(driver)
    login_page.execute_login(get_data["username"], get_data["password"])
    assert login_page.actual_error() == "Invalid credentials", "Error message does not match expected value"


def test_login_positive(driver):
    login_page = LoginPage(driver)
    login_page.execute_login("Admin", "admin123")
    dashboard_page = DashboardPage(driver)
    assert dashboard_page.actual_heading() == "Dashboard", "Heading is different"

@pytest.fixture(params=[{"username":"admin", "password":"admin$$"},
                        {"username":"Admin123", "password":"123"}])
def get_data(request):
    return request.param

from pages.dashboard_page import DashboardPage
from pages.login_page import LoginPage


def test_successful_login(driver):

    login_page = LoginPage(driver)
    login_page.execute_login("Admin", "admin123")
    dashboard_page = DashboardPage(driver)
    assert dashboard_page.actual_heading() == "Dashboard"


def test_time_at_work(driver):
    login_page = LoginPage(driver)
    login_page.execute_login("Admin", "admin123")
    dashboard_page = DashboardPage(driver)
    assert dashboard_page.time_at_work_displayed()

pytest 

 pytest --html=report.html -s

Pytest Logging to print to a File

HOME

import logging


def test_logging_file():

    # Create a named logger
    logger = logging.getLogger(__name__)

    # Create a file handler
    filehandler = logging.FileHandler('logfile.log')

    # Set the formatter for the file handler
    formatter = logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
    filehandler.setFormatter(formatter)

    # Add the file handler to the logger
    logger.addHandler(filehandler)  

    logger.setLevel(logging.INFO)
    logger.debug("Debug statement is executed")
    logger.info("Information statement is executed")
    logger.warning("Warning mode, but test continues")
    logger.error("Error has happened and failed the test")
    logger.critical("Critical issue")

logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")

pytest test_logging_file.py -s

import inspect
import logging


class BaseClass:

    def get_logger(self):

        loggerName = inspect.stack()[1][3]
        logger = logging.getLogger(loggerName)

        filehandler = logging.FileHandler('logfile.log')

        formatter = logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
        filehandler.setFormatter(formatter)

        logger.addHandler(filehandler)  

        logger.setLevel(logging.INFO)
        return logger

import pytest
from demo.BaseClass import BaseClass


class TestExample3(BaseClass):

    @pytest.fixture()
    def dataload(self):
        print("User Profile data")
        return ["Vibha", "Singh", "qaautiomation.expert"]

    @pytest.mark.usefixtures("dataload")
    def test_user_profile(self, dataload):
        log = self.get_logger()
        log.info(dataload)
        log.info(f'First Name : {dataload[0]}')
        log.info(f'Surname : {dataload[1]}')
        log.info(f'Website : {dataload[2]}')

pytest -s

Pytest Logging to print to Console

HOME

import logging


def test_logging_console():
    logger = logging.getLogger(__name__)

    # Create a console handler
    console_handler = logging.StreamHandler()

    # Set the formatter for the console handler
    formatter = logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
    console_handler.setFormatter(formatter)

    # Add the console handler to the logger
    logger.addHandler(console_handler)
    logger.setLevel(logging.INFO)

    logger.debug("Debug statement is executed")
    logger.info("Information statement is executed")
    logger.warning("Warning mode, but test continues")
    logger.error("Error has happened and failed the test")
    logger.critical("Critical issue")

logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")

pytest test_logging_console.py -s

Parameterizing test with multiple data sets using Fixtures in PyTest

HOME

@pytest.fixture()
def dataload():
    print("User Profile data")
    return ["Vibha","Singh","qaautiomation.expert"]
import pytest


@pytest.mark.usefixtures("dataload")
class TestExample:

    def test_edit_profile(self, dataload):
        print(dataload)
        print("First Name :", dataload[0])
        print("Surname :", dataload[1])
        print("Website :", dataload[2])

import pytest


@pytest.fixture(params=["chrome", "firefox", "edge"])
def cross_browser(request):
    if request.param == "chrome":
        print ("Browser selected is chrome")
    elif request.param == "firefox":
        print("Browser selected is firefox")
    elif request.param == "edge":
        print("Browser selected is edge")

    return request.param
def test_cross_browser(cross_browser):
    print(cross_browser)

import pytest


@pytest.fixture(params=[("chrome","Vibha","Singh"), ("firefox","qaautomation.expert"), ("edge","QA")])
def multiple_parameters(request):
    return request.param

def test_multiple_parameters(multiple_parameters):
    print(multiple_parameters)
    print("First parameter :", multiple_parameters[0])
    print("Second parameter :", multiple_parameters[1])

import pytest


@pytest.fixture
def arithmetic_fixture(request):
    factor = request.param
    yield factor


@pytest.mark.parametrize("arithmetic_fixture", [6, 7, 9], indirect=True)
def test_arithmetic(arithmetic_fixture):
    result1 = arithmetic_fixture * 5
    result2 = arithmetic_fixture + 11
    result3 = arithmetic_fixture - 3
    print(arithmetic_fixture)
    print("Multiplication :", result1)
    print("Addition :", result2)
    print("Subtraction :", result3)