How to Create a BDD Automation Framework using Python Behave Library and Selenium

How to Create a BDD Automation Framework using Python Behave Library and Selenium

To deliver a good quality of work creating a robust software testing framework is a very important task. Every tester has his/her own approach or method to create a testing framework but the most common and important thing is creating a framework in such a manner that the other testers with minimal knowledge of automation testing can easily utilize the framework. While creating a framework there are some key points that we should consider you will find these points mentioned below. 

A good tester is one who has the ability to create a good testing framework. In this blog, I have explained how to create an automation testing framework. Even a beginner with minimal knowledge of automation testing can use this approach to create his own testing framework. There are many more things that you can implement in this explained framework so feel free to comment on it. 

When I started my journey as an SDET creating a framework was my first task assigned in my training so I can understand how important it is to create your own framework. Together in this blog, we will see the guidelines I have described which will help us to create a testing framework. 

Before we jump into the main topic of our discussion let’s just quickly see the steps we will be following while creating our own framework.

Key Considerations When Creating an Automation Testing Framework:

  1. Understanding the Requirements
  2. Selecting a Testing Framework
  3. Designing Test Cases
  4. Implementing Test Cases
  5. Executing Tests
  6. Maintaining and Improving the Framework

Among the various frameworks present one of the most popular frameworks used for automation testing i.e. the combination of python’s behave library and selenium. In this blog, we are going to explore how to build and use this framework for our automation testing. 

As everyone is familiar with Selenium which is an open source and one of the widely used tools for web automation testing along with Playwright and Cypress. Behave is a python library that is used for the BDD (Behavior Driven Development). Let’s just quickly explore what are the different frameworks present out there for automation testing. 

A software automation testing framework is designed to make the process of testing software more efficient and easy to use. Every framework has its own advantages and disadvantages as per the given requirement it is most important for us to choose the right framework for automation. Below you will find some of the most commonly used and popular automation frameworks.

Types of Test Automation Frameworks:

  1. Linear Scription Framework. 
  2. Modular Testing Framework. 
  3. Data-Driven Framework. 
  4. Keyword Driven Framework. 
  5. Hybrid Framework
  6. Behavior Driven Development Framework. 
  7. Test Driven Development Framework. 
Types of Automation Testing Framework.

In this blog, we will be building a BDD framework using Python’s behave library and selenium. In BDD we use the natural language to describe our test scenario divided into steps using the Gherkin language. These test scenarios are present in a feature file and because of the use of natural language, the behavior of the application is easily understandable by all. So, we can say that while creating a BDD framework one of the key components we should consider to use of the feature files and the step files. 

As described earlier a feature file is written in natural language with the help of Gherkin language by following a set format. While a step file is an implementation of the steps present in the feature file. Here, a step file is a python file and we can see that it is full of a set of functions where those functions correspond to the steps described in the feature file. Now that we have seen what is feature file and step file let’s see what is the use of python’s behave library here, so basically once the steps and feature file are ready the behave will start automatically matching the steps present in the feature file with its corresponding implementation in the step file and will also check for any assertion errors present.

Prerequisites for creating a framework:

  1. Python: https://www.python.org/downloads/ visit the site to download and install python in your system if it is not there. 
  1. Install Selenium and Behave using:

pip install selenium 

Pip install behave 

For more details please visit: https://pypi.org/project/behave/  &  https://pypi.org/project/selenium/ 

3. Pycharm IDE (Professional or Community): https://www.jetbrains.com/pycharm/download/ 

4. Install allure for report generating using:

pip install allure-behave 

For more details please visit: https://pypi.org/project/allure-behave/ 

5. We can also install all the required packages using the requirement.txt file using the below command. 

pip install -r requirement.txt

Framework Structure Overview: 

Here is the overview of our python selenium behave BDD framework. 

As a beginning, we are going to start with creating a simple framework using one scenario outline. In the next blog, we are going to see how to create an API testing framework using python. To understand both of them please read the blog carefully as I am explaining all the points here in natural language, without wasting any time let’s dive into the main topic of our discussion i.e. how to create python selenium behave BDD automation testing framework. 

For this, we will follow some guidelines which I have described as steps. 

Step 1: 

Create a project in Pycharm (here I am using Pycharm professional) and as mentioned in the prerequisites install the packages. 

 It is not compulsory to use pycharm professional we can use pycharm community as well. 

Step 2:

In this step, we will be creating a Features folder in which we will be creating our feature files for different scenarios. A feature file is something that holds your test cases in the form of a scenario and scenario outline. In this framework, we are using a scenario outline. Both scenario and scenario outline contain steps that are easy to understand for non-technical persons. We can also assign tags for the feature files and for the scenarios present in that file. Note that the feature file should end with a .feature  extension. 

Feature: Create test cases using Selenium with Python to automate below BMI calculator tests


#  We are using Scenario Outline in this feature as we can add multiple input data using examples.

  Scenario Outline: Calculating BMI value by passing multiple inputs
    Given I enter the "<Age>"
    When I Click on "<Gender>"
    And  I Enter a "<Height>"
    And  I Enter the "<Weight>"
    And  I Click on Calculate btn
    And  I Verify Result with "<Expected Result>"
    Examples:

      | Age | Gender  | Height  | Weight  | Expected Result |
      | 20  | Male    |  180    |  60     | BMI = 18.5 kg/m2|
      | 35  | Female  |  160    |  55     | BMI = 21.5 kg/m2|
      | 50  | Male    |  175    |  65     | BMI = 21.2 kg/m2|
      | 45  | Female  |  150    |  52     | BMI = 23.1 kg/m2|

Step 3:

Now, we have our feature file let’s create a step file to implement the steps described in the feature file.  In order to recognize the step file we are adding step work after the name so that behavior will come to know the step file for that particular feature file. Both feature files and step files are essential parts of the BDD framework. We have to be careful while describing the steps in the feature file because we have to use the same steps in the step file so that behavior will understand and map the step implementation. 

from behave import *

# The step file contains the implementation of the steps that we have described in the feature file.

@given('I enter the "{Age}"')
def step_impl(context, Age):
    context.bmipage.age_input(Age)

@when('I Click on "{Gender}"')
def step_impl(context, Gender):
    context.bmipage.gender_radio(Gender)

@step('I Enter a "{height}"')
def step_impl(context, height):
    context.bmipage.height_input(height)

@step('I Enter the "{weight}"')
def step_impl(context, weight):
    context.bmipage.weight_input(weight)

@step("I Click on Calculate btn")
def step_impl(context):
    context.bmipage.calculatebtn_click()

@step('I Verify Result with "{expresult}"')
def step_impl(context, expresult):
    context.bmipage.result_validation(expresult)

Step 4: 

In step 4 we will be creating a page file that contains all the locators and the action methods to perform the particular action on the web element. We are going to add all the locators at the class level only and will be using them in the respective methods. The reason behind doing so is it is a good practice to declare your locators at the class level as when the locators get changed it is effortless to replace them and we don’t have to go through the whole code again. 

from selenium.webdriver.common.by import By
import time
from Features.Pages.BasePage import BasePage


# The page contains all the locators and the actions to perform on that web element.
# In this page file we have declared all the locators at the class level and we are using them in the respective methods.

class BmiPage (BasePage):
    def __init__(self, context):
        BasePage.__init__(self, context.driver)
        self.context = context
        self.age_xpath = "//input[@id='cage']"
        self.height_xpath = "//input[@id='cheightmeter']"
        self.weight_xpath = "//input[@id='ckg']"
        self.calculatebtn_xpath = "//input[@value='Calculate']"
        self.actual_result_xpath = "//body[1]/div[3]/div[1]/div[4]/div[1]/b[1]"

    def age_input(self, Age):
        AgeInput = self.driver.find_element(By.XPATH, self.age_xpath)
        AgeInput.clear()
        AgeInput.send_keys(Age)
        time.sleep(2)

    def gender_radio(self, Gender):
       SelectGender = self.driver.find_element(By.XPATH, "//label[normalize-space()='" + Gender+"']")
        SelectGender.click()
        time.sleep(2)


    def height_input(self, height):
        HeightInput = self.driver.find_element(By.XPATH, self.height_xpath)
        HeightInput.clear()
        HeightInput.send_keys(height)
        time.sleep(3)

    def weight_input(self, weight):
        WeightInput = self.driver.find_element(By.XPATH, self.weight_xpath)
        WeightInput.clear()
        WeightInput.send_keys(weight)
        time.sleep(3)

    def calculatebtn_click(self):
        Calculatebtn = self.driver.find_element(By.XPATH, "//input[@value='Calculate']")
        Calculatebtn.click()
        time.sleep(3)

    def result_validation(self, expresult):
        try:
            Result = self.driver.find_element(By.XPATH, "//body[1]/div[3]/div[1]/div[4]/div[1]/b[1]")
            Actualresult = Result.text
            Expectedresult = expresult
            assert Actualresult == Expectedresult, "Expected Result Matched"
            time.sleep(5)
        except:
            self.driver.close()
            assert False, "Expected Result mismatched"

The next one is the base page file. We are creating a base page file to make an object of the driver so that we can easily use that for our page and environment file.

from selenium.webdriver.support.wait import WebDriverWait


# In the base page we are creating an object of driver.
# We are using this driver in the other pages and environment page.


class BasePage(object):
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(self.driver, 30)
        self.implicit_wait = 25

Step 5:

This step is very important because we will be creating an environment file (i.e. Hooks file). This file contains hooks for before and after scenarios to start and close the browser. Also if you want you can add after-step hooks for capturing screenshots for reporting. We have added a method to capture screenshots after every step and will attach them to the allure report.

import json
import time

from allure_commons._allure import attach
from allure_commons.types import AttachmentType
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from Pages.BasePage import BasePage
from Pages.BmiPage import BmiPage

data = json.load(open("Resources/config.json"))


# This environment page is used as hooks page. Here we can notice that we have used before, after hooks along side with some step hooks.


def before_scenario(context, scenario):
    context.driver = webdriver.Chrome(ChromeDriverManager().install())
    time.sleep(5)
    basepage = BasePage(context.driver)
    context.bmipage = BmiPage(basepage)
    context.stepid = 1
    context.driver.get(data['BMIWEBURL'])
    context.driver.maximize_window()
    context.driver.implicitly_wait(3)


def after_step(context, step):
    attach(context.driver.get_screenshot_as_png(), name=context.stepid, attachment_type=AttachmentType.PNG)
    context.stepid = context.stepid + 1


def after_scenario(context, scenario):
    context.driver.close()

Step 6:

It is a good practice to store all our common data and files in a resource folder. So, whenever we need to make changes it will be easy to implement them for the whole framework. For now, we are adding a config.json file in the resource folder. This file contains the web URL used before the scenario to launch the web page for the specified tag in the feature file. The file is written in JSON format. 

  “BMIWEBURL”: “https://www.calculator.net/bmi-calculator.html?ctype=metric”

Congratulations, finally we have created our own Python Selenium Behave BDD framework. As I mentioned earlier we will be using Allure for reporting the test result. For this use the below command in the terminal and it will generate the result folder for you. 

behave Features/BMICalculator.feature -f allure_behave.formatter:AllureFormatter -o Report_Json

To convert the JSON file into readable HTML format use the below command. 

allure generate Report_Json -o Report_Html –clean

Allure Report
Allure Behaviours

I am adding a GitHub repository link so that if anyone has any issues while building it, you can go through the source code here: https://github.com/ydhole-spurqlabs/SeleniumPython

Conclusion: 

Creating a testing framework is very important as well as feels like a tedious task but with the right guidelines, everyone can create a testing framework. I hope in this blog I have provided all the answers related to the python selenium behavior automation testing framework. Here, we choose a BDD framework over other existing frameworks because of its better understanding, easy to adapt, and easy to understand for end users. If you still have any issues related to what we have seen earlier feel free to comment them down we will solve them together. There are many more things we can add to this existing framework but to get started I feel this framework is enough and will cover most of the requirements. 

Read more blogs here.

Web application automation using Ginger

Web application automation using Ginger

In this article, I will introduce the concept of codeless automation as an innovative and efficient solution for software testing with minimal coding. Performs to increase the importance of time-to-market in product success, codeless automation helps to reduce testing time in release cycles, making it a growing trend in software testing.

One of the most notable codeless automation frameworks is Ginger, an open-source tool created and published by Amdocs under an Apache 2.0 license.

Advantages of codeless automation over traditional code-based automation:

  • No programming skills are required
  • Test automation can start from day 1
  • Keep pace with the development cycle
  • Faster and more efficient test development

Ginger is a free and open-source automation framework with the following features:

  • AI-driven automation
  • Supports various platforms such as web, mobile, windows, APIs, etc.
  • Simple drag-and-drop approach for codeless automation
  • Dynamic Error handling with self-healing capabilities
  • Ability to reuse existing automation scripts
  • Reduced average repair time
  • Software reusability across the software lifecycle
  • Automates visual validations
  • Built-in integrations with popular open-source tools such as QC, Jira, CI/CD, Jenkins, Bambootools, and others.

Similarly, we will expand the Ginger to investigate and create a web application automation script:

Where can you find Ginger?

You need to Visit “ginger.amdocs.com” and download the latest version of the tool. Below you can see the dashboard.

How do you set up Ginger?

Once the Ginger is downloaded, now double-click on the application file, and a license agreement window will be displayed – select ‘I accept the agreement’, browse the destination location where you want it installed, and click on ‘Next’.

Now, select the default user type and terminology, and then click ‘Install’.

After successfully installing the application. Now, click on the created shortcut to access the Ginger homepage.

Your first Project in Ginger

Here, we will perform Web application automation using Ginger, let’s create a solution for the same. A solution in Ginger is a logical structure with complete automation-related files and only one solution can be loaded at a time. However, multiple target applications can be added to the same solution.

Click on ‘Create a new solution

  • ‘Add Solution’ window
  • Enter the solution name
  • Browse the location to be stored
  • Select the ‘main application platform (‘web’)
  •  Enter the ‘solution password encryption key’
  •  Click on the ‘Create’ button

The following diagram depicts the ‘Business Flow,’ which is an end-to-end (start-to-finish) logical flow of the project/business.

So, now the folder structure is automatically created. Once the project solution is created.

Here, multiple target applications can be added to the same solution.

Go to the ‘Configurations’ tab

  • Click on ‘add (+)’ button
  • Select the desired application from the window displayed

Accordingly, the target applications have been merged into the solution:

Go to the ‘Business Flows’ tab

  • Click on the ‘Edit’ tab
  •  Select the desired application in the solution

Configure the agent

Agents are the platform where the scripts will run. First, you select the driver type where you want to launch the application and click the ‘Agents’ tab.

Now, navigate to the ‘Resources’ tab and create an environment for the desired application. An environment stores application-related data such as login credentials, server connection information, and automation flow. One should use environment variables as much as possible to store any configurations that may change per environment.

Given that you have created a new environment, it will ask whether you want to share it with all or a subgroup of the target applications created under business flow.

Given that you have created a new environment, it will ask whether you want to share it with all or a subgroup of the target applications created under business flow. Therefore, you can also create a new environment by cloning one that already exists.

You can also create a new environment by cloning one that already exists.

Now, click on the environment created

  • A list of target applications assigned to this environment is displayed
  • Select the desired application and it will open displaying three environment-specific tabs:  Parameters, Databases, and Login Users.

Add a new Activity

Basically, you need to navigate to Business Flows and add a new activity. These activities are a collection of actions required to complete a function or intended task.

For example:

The user successfully logged in using valid credentials. Here logging in with valid credentials is a functional activity that can be completed using a set of actions.

Under this activity, multiple actions can be created. You can click on the ‘Automate’ tab in the top right corner of the page and add automation actions from the ‘Action Library’ in the toggle panel. Now on the top bar, the target application configured agent, and environment that you have chosen for the current business flow is displayed.

Essentially, the actions library is divided into four categories: plugin actions, legacy actions, platform actions, and general actions.

In the below image, a ‘Bowser Action’ is added, and double-click it to add a value, i.e. to add various operation settings.

This image has an empty alt attribute; its file name is image-13.png

First, go to Expression settings, navigate to the desired folder structure, and enter the URL value, which can be parameterized in the environment.

Now let’s add actions to the activity

We’ve launched the website in the driver; now we’ll locate the elements and log in with the appropriate credentials.

After that Select ‘UI Element Action’ from the Action Library in the toggle panel and provide details about the element to be located.

First of all, I am using orange HRM demo website ( https://opensource-demo.orangehrmlive.com )

So, the element ‘Username’ is located using XPath to perform send keys operation.

The following shows the configuration for locating a button and clicking on it for successful login.

Because the configuration shown in the following figures is used to verify the element that appears on the page is successful login.

Variables – To validate the text retrieved after login, go to the expression editor and add the activity variable.

A variety of variables can be included, such as –

  • ‘Variable String’ – To verify a string on the page
  • ‘Variable Random Number’ – To verify a number randomly generated every time within a given range
  • ‘Variable List’ – To verify a variable from a list specified
  • ‘Variable Number’ – To generate a random number between the given   minimum and maximum range of number
  • ‘Variable Password String’ – To encrypt the entered password
  • ‘Variable Sequence’ – To give minimum and maximum range of numbers with a specified interval
  • ‘Variable Timer’ – To use as a timer

Similarly, other actions, as shown in the figure below, are added to browse other tabs.

From the ‘Execution Report’ tab, screenshot capture and log details can be enabled.

So here, all the actions of the test scenario are completed, Now click the ‘Run Activity’ button located at the top of the page.

Following is the execution, you can check the output values under ‘Actual Value’ and ‘Expected Value,’ as shown below.

The following image shows the execution in progress

The actual below image shows the execution is completed.

Adding run set

The Run Set is a real-time dashboard with a single complete end-to-end project flow comprised of various activities. It displays the entire Business flow, including actions, activities added and executed, and steps passed, pending, failed, or blocked.

First, you need to Set up the ‘Run Sets’ by adding the desired Business Flow and to start the execution, click on the runner tab button.

Also, you can get a complete execution history:

  • Navigate to ‘Run Sets
  • Executions History’ tab

Following are the Execution Results shown in the output of the execution.

Execution Report

The following section shows a failed scenario.

  • To start, we need to create a new Run Set.
  • Then add a new runner set, go to the ‘Run Sets’ tab, and click the ‘+’ button.

Now, Click on the ‘+’ button to add a Business flow to the runner.

After adding the business flow, you can see the number of activities and actions in the run set.

So, to run the run set, click the ‘run button’ shown in the image below.

Now, the execution is seen in progress

Here, the runner display number of failed actions, and clicking on the ‘Generate Run Set Report option’, will create a detailed execution report.

The following shows the execution report of the failed test scenario.

Formation of the Execution Reports displays a detailed execution that includes. The following information is an action name, and action type, So now execution – start, duration, and end time, number of retry iterations, status (passed/failed), Error details, and screenshots.

Therefore, we have completed Web application automation using Ginger successfully. Happy Testing!!

Conclusion: –

Ginger strikes me as a simple and modern automation tool that can be used to create test cases in natural language, making it a valuable platform for continuous testing, regression testing, and progressive testing.

Read more blogs here

XPath for Selenium, Playwright & Cypress Web Automation Testing

XPath for Selenium, Playwright & Cypress Web Automation Testing

For any web automation testing, the one and most important task is to identify and use robust locators to identify web elements so that your automated tests do not fail with “Unable to locate element”. In this article, we are providing you with the techniques that every tester should learn to create those robust locators. As we already know this can be done using different locator strategies. In this blog, we are going to learn about XPath. Before we dive into the topic of our discussion let’s just get more familiar with Xpaths. Let’s start with, 

What is XPath?

XPath (XML Path Language) is an expression language that allows the processing of values conforming to the data model defined in the XQuery and XPath Data models. Basically, it is a query language that we use to locate or find an element present on the webpage. It is defined by the World Wide Consortium (W3C). Now, let’s discuss why Xpaths are necessary.

Why is XPaths necessary?

Xpaths are the most widely used locators in automation though there are other locators like id, name, class name, tag name, and so on. Also, it is used when there are no unique attributes available to locate the web element. It allows identification with the help of the visible test present on the screen with the help of Xpath function text().

Before explaining the importance of XPath let’s just go through the different types of locators available for automation testing. 

In this blog, we will learn about the different types of Xpaths and how to implement them so that we can locate our web elements quickly using the selenium web driver. Basically, there are two types of Xpaths

1. Absolute XPath:

In this type, The XPath starts from the beginning or from the root node of the HTML DOM structure. It is a direct way to locate or find the web element but the disadvantage of absolute XPath is that as we are creating it from the start of the HTML DOM structure if there are any changes introduced in the created path of the web element then it gets failed. In this type of locator, we only use tags or nodes. The main advantage of this is that we can select a web element from the root node as it starts with the single forward slash “ / ”.

Example:

Here is an example of an absolute Xpath for an input field box. 

 example of an absolute Xpath for an input field box. 

The absolute XPath is: /html[1]/body[1]/div[1]/div[1]/div[1]/div[1]/div[1]/div[2]/div[2]/form[1]/div[1]/div[1]/div[2]/input[1]

2. Relative Xpath:

Compared to an absolute XPath the relative XPath does not start from the beginning of the HTML DOM structure. It starts from where the element is present e.g. from the middle of the HTML DOM structure if the element is located there. We don’t have to travel from the start of the HTML DOM structure. The relative Xpath starts with a double forward slash “ // “ and it can locate and search the web element anywhere on the webpage. Relative XPath directly jumps to elements on DOM. The other difference between absolute and relative XPath is that in absolute XPath we use tags or nodes but in relative XPath we use attributes. 

Example:

We are writing the relative XPath for the same input field for which earlier we created an absolute XPath. 

We are writing the relative XPath for the same input field for which earlier we created an absolute XPath.

Relative XPath is: 

//input[@name=’username’]

XPath Functions: 

Types of Xpath Functions

It is not always possible to locate a web element using relative XPath that is because at some times while locating a particular web element there is the possibility of elements that have similar properties, for example, the same id, name, or same class name. So, here the basic XPath won’t work efficiently for finding that web element. Xpath functions are used to write the efficient XPath by locating a web element with a unique value. Basically, there are three types of XPath functions as follows,

a. starts-with() Function:

starts-with() function is very useful in locating dynamic web elements. It is used to find the element in which the attribute value starts with some particular character or text. 

While working on the dynamic web page the starts-with function plays an important role. We can use it to match the starting value of a web element that remains static. 

It can also locate the web element whose attribute value is static.

Syntax:

Xpath = //tagname[starts-wtih(@attribute,’value’)]

Example:

//input[starts-with(@name,’username’)]

start-with() function is very useful in locating dynamic web elements. 

b. contains() Function:
  • Just like the start-with() function explained above, the contains() function is also used to create a unique expression to locate a web element. 
  • It is used when if a part of the value of an attribute changes dynamically the function can navigate to the web element with the partial text present. 
  • We can provide any partial attribute value to locate the web element.
  • It accepts two parameters the first one is the attribute of the tag must validate to locate the web element and the second one is the value of an attribute is a partial value that the attribute must contain. 
Syntax: 

Xpath = //tagname[contains(@attribute,’value’)]

Example:

//input[contains(@name,’username’)]

 Xpath contains() Function:
c. text() Function:

text() Function:

  • The text() function is used to locate web elements with exact text matches.
  • The function only works if the element contains the text. 
  • This method returns the text of the web element when identified by the tag name and compared it with the value provided on the right side. 
Syntax:

Xpath = //tagname[text()=’Actual text present’]

Example:

   //button[text()=’ Login ‘]

 Xpath text() Function:

How to use AND & OR in XPath:

AND & OR expressions can also be used in selenium Xpath expressions. Very useful if you want to use more than two attributes to find elements on a webpage. 

The OR expression requires two conditions and it will check whether the first condition in the statement is true if so then it will locate that web element and if not then it will go for the second condition and if that is true then also it will locate that web element. So, here the point we should remember is that when we are using the OR expression at least either of two of the conditions should be true then, and then only it will find and locate that web element. 

Syntax:

Xpath = //tagname[@attribute=’Value’ or @attribute=’Value’]

Example:

//input[@name=’username’ or @placeholder=’xyz’]     

Here the first condition is true and the second one is false still the web element got located. 

How to use AND & OR in Xpath

Just like the OR expression the AND expression also requires two conditions but the catch here is that both the provided condition must be true then and then only the web element will get located. If either of the conditions is false then it will not locate that web element. 

Syntax:

Xpath = //tagname[@attribute=’Value’ and @attribute=’Value’]

Example:

//input[@name=’username’ and @placeholder=’Username’]

In this case, both the condition provided for an AND expression is true hence the web element got located.

XPath Axis:

XPath Axis

It is a method to identify those dynamic elements that are impossible to find by normal XPath methods. All the elements are in a hierarchical structure and can be either located using absolute or relative Xpaths but it provides specific attributes called XPath axis to locate those elements with unique XPath expressions. The axes show a relationship to the current node and help locate the relative nodes concerning the tree’s current node. The dynamic elements are those elements on the webpage whose attributes dynamically change on refresh or any other operations. The HTML DOM structure contains one or more element nodes and they are known as trees of nodes. If an element contains the content, whether it is other elements or text, it must be declared with a start tag and an end tag. The text defined between the start tag and the end tag is the element content. 

Types of XPath Axis: 

XPath Axis selenium web driver

1. Parent Axis XPath:

With the help of the parent axis XPath, we can select the parent of the current node. Here, the parent node can be either a root node or an element node. The point to consider here is that for all the other element nodes the maximum node the parent axis contains is one. Also, the root node of the HTML DOM structure has no parent hence the parent axis is empty when the current node is the root node. 

Syntax:

Xpath = //tagname[@attribute=’value’]//parent::parent_tagname

Example:

//input[@name=’username’]//parent::div

This Screenshot is about Parent Axis Xpath

2. Child Axis XPath:

As we have seen using the parent axis XPath actually we are creating an XPath by the following bottom-up approach but here in the child axis case, we are going to follow the top-down approach to create an XPath. The child axis selects all the child elements present under the current node. We can easily locate a web element as a child of the current node. 

Syntax:

Xpath = //tagname[@attribute=’value’]//child::child_tagname

Example:

//div[@class=’oxd-form-actions orangehrm-login-action’]//child::button

The child axis selects all the child elements present under the current node. We can easily locate a web element as a child of the current node. 

3. Self Axix XPath:

This type of XPath uses its own current node and selects the web element belonging to that current node. You will always observe only one node that represents the self-web element. The tag name we provide at the start and at the end of XPath are the same as they are on the self-axis of the current node. However, this provides the confirmation of the element present when there is more than one element present having the same value and attribute. 

Syntax:

Xpath = //tagname[@attribute=’value’]//self::self_tagname

Example:

//input[@name=’username’]//self::input

This type of XPath uses its own current node and selects the web element belonging to that current node. You will always observe only one node that represents the self-web element.

4. Descendant Axis XPath:

It selects all the descendants i.e. child and grandchild of the current node. 

Syntax:

Xpath = //tagname[@attribute=’value’]//descendant::child or grandchild_tagname

Example:

//div[@class=’oxd-form-row’]//descendant::input[@name=’username’]

Username and Password
It selects all the descendants i.e. child and grandchild of the current node.

5. Descendant-or-self Axis XPath:

Using this axis we can select the current node and all its descendants i.e. child, grandchild, etc just like a descendant axis. The point to be noticed here is the tag name for descendants and self are the same. 

Syntax: 

Xpath = //tagname[@attribute=’value’]//descendant-or-self::tagname

Example:

//input[@name=’username’]//descendant-or-self::input

Using this axis we can select the current node and all its descendants i.e. child, grandchild, etc just like a descendant axis. The point to be noticed here is the tag name for descendants and self are the same. 

6. Ancestor Axis:

As we understand how the descendant axis works now, the ancestor axis works exactly opposite to that of the descendant axis. It will select or locate all ancestors elements i.e. parent, grandparent, etc of the current node. This axis contains the root node too. 

Syntax:

Xpath = //tagname[@attribute=’value’]//ancestor::ancestor_tagname

Example:

//input[@name=’username’]//ancestor::div[@class=’oxd-form-row’]

7. Ancestor-or-self Axis XPath:

Now, just like the descendant-or-self axis, the ancestor-or-self axis XPath will locate a web element having the same starting and ending tag name. 

Syntax:

Xpath = //tagname[@attribute=’value’]//ancestor-or-self::tagname

Example:

//input[@name=’username’]//ancestor-or-self::input

Now, just like the descendant-or-self axis, the ancestor-or-self axis XPath will locate a web element having the same starting and ending tag name.

8. Following Axis Path:

The following axis XPath selects all the web element that comes after the current node. 

Syntax:

Xpath = //tagname[@attribute=’value’]//following::following_tagname

Example:

//div[@class=’oxd-form-actions orangehrm-login-action’]//following::button

The following axis XPath selects all the web element that comes after the current node. 

9. Following-sibling Axis XPath

Using the following sibling axis method we can select all the nodes that have the same parent as that of the current node and that appear after the current node. 

Syntax:

Xpath = //tagname[@attribute=’value’]//following-sibling::following_tagname

Example:

//div[@class=’oxd-form-actions orangehrm-login-action’]//following-sibling::button

Using the following sibling axis method we can select all the nodes that have the same parent as that of the current node and that appear after the current node. 

10. Preceding Axis XPath

Using this axis method we can locate a web element that has the node that appears before the current node. 

Syntax:

Xpath = //tagname[@attribute=’value’]//preceding::tagname

Example:

//div[@class=’oxd-form-actions orangehrm-login-action’]//preceding::button

Example of Preceding Axis Xpath with username and Password
Using this axis method we can locate a web element that has the node that appears before the current node. 

11. Preceding-sibling Axis XPath:

Using the following sibling axis method we can select all the nodes that have the same parent as that of the current node and that appear before the current node. It works opposite to that of the following sibling axis XPath. 

Syntax:

Xpath = //tagname[@attribute=’value’]//preceding::tagname

Example:

//div[@class=’oxd-form-actions orangehrm-login-action’]//preceding::button

Example of Preceding-sibling Axis Xpath
Using the following sibling axis method we can select all the nodes that have the same parent as that of the current node and that appear before the current node. It works opposite to that of the following sibling axis XPath.

You can try all of these examples mentioned above with the Orange HRM Demo website here.

Conclusion:

In conclusion, XPath is an essential tool for web automation testing when using Selenium, Playwright, and Cypress. It allows for more flexibility and specificity in locating elements on a web page. Understanding the different types of XPath expressions and how to use them can greatly improve the efficiency and effectiveness of the automation testing process. It can be particularly useful in situations where elements do not have unique CSS selectors, or when the structure of the HTML changes frequently. With the knowledge of XPath, you can write more robust and stable automation tests.

Read more blogs here.

API Test Automation using Playwright and Java

API Test Automation using Playwright and Java

 Most of us are familiar with API testing tools like Postman, SoapUI, etc, and API automation libraries like RestAssured and Karate to automate API test cases. A recent entrant in this category is Playwright. The playwright is an Automation Tool provided by Microsoft. It provides cross-browser testing using which we can perform testing on multiple browsers like Chromium, Webkit, Firefox, etc. playwright supports cross-language testing (Java, Node.js, Python, .NET, etc.). However, very few of us know that it can actually do an API Test automation of methods GET, PUT, POST, and DELETE. Let’s see how it can be done.

Can we perform API testing using Playwright?

The playwright provides inbuilt support for API testing that’s why we don’t need any external libraries to handle API. The playwright doesn’t use any browser to call API. It sends requests using Node.js which provides faster execution.

In this tutorial, we will explore basic API methods with the help of Playwright- java. Below are the Four methods.

  1. GET
  2. POST
  3. PUT
  4. DELETE

Pre-requisite:

To get started with API automation with playwright-java first we need playwright to be installed in your system, to do this we can simply add the following dependency in the pom.xml file.

Along with the playwright, we have to add Testing and JSON dependencies.

<dependency>
  <groupId>com.microsoft.playwright</groupId>
  <artifactId>playwright</artifactId>
  <version>1.23.0</version>
</dependency>

<dependency>
   <groupId>org.testng</groupId>
   <artifactId>testng</artifactId>
   <version>7.1.0</version>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>com.googlecode.json-simple</groupId>
   <artifactId>json-simple</artifactId>
   <version>1.1.1</version>
</dependency>

Now let’s see how we can start API automation testing with Playwright-java.

1. GET:

By providing an API endpoint we can read data using a GET request. We must pass a few parameters to get the required data from the API.

We can verify the request by asserting the Response Code. The response code for a successful GET Request is 200. You can also assert a text inside the JSON body response.          

For Example, I am using postman here to send a GET request to ”  ‘/api/users/4’ endpoint of a sample API URL ‘https://reqres.in’

The below code shows the implementation of the GET method through Playwright.

@Test
public void get() throws IOException, ParseException {
   Playwright playwright = Playwright.create();
   String BaseURL="https://reqres.in";
   Map<String, String> headers = new HashMap<>();
   headers.put("content-type", "application/json");
   APIRequestContext apiRequestContext= playwright.request().newContext(new APIRequest.NewContextOptions()
           .setBaseURL(BaseURL).setExtraHTTPHeaders(headers));
   APIResponse response = apiRequestContext.get("/api/users/4");
   int responseCode=response.status();
   Assert.assertEquals(responseCode,200);

   String responseText= response.text();
   JSONParser j= new JSONParser();
   JSONObject json = (JSONObject) j.parse(responseText);

   String responseData= json.get("data").toString();
   JSONObject jsonData = (JSONObject) j.parse(responseData);
   Assert.assertEquals(jsonData.get("email"),"eve.holt@reqres.in");
   Assert.assertEquals(jsonData.get("first_name"), "Eve");
   Assert.assertEquals(jsonData.get("last_name"), "Holt");
   Assert.assertEquals(jsonData.get("avatar"), "https://reqres.in/img/faces/4-image.jpg");
   apiRequestContext.dispose();
   playwright.close();
}

To verify the response data we are parsing the response to JSON Object so that we can verify specific key-value pairs.

2. POST:

POST method is used to add a new record via API where we have to pass data as payload ex. first name, last name, etc. The response code for a successful POST Request is 201.

For example, I am sending a POST request to  ‘/api/users/’ endpoint with base URL ‘https://reqres.in‘ and the body as given below in the screenshot:

To pass the data payloads to the POST/PUT method, first, we have to create a POJO class which will help to create methods to get and set payloads.
below we have created a POJO class employee which will help to get and set data to both POST and PUT calls.

public class Employee {
// private variables or data members of pojo class
private String email;
private String first_name;
private String last_name;
private String avatar;

public String getEmail() {
   return email;
}
public void setEmail(String email) {
   this.email = email;
}
public String getFirstName() {
   return first_name;
}
public void setFirstName(String firstName) {
   this.first_name = firstName;
}
public String getLastName() {
   return last_name;
}
public void setLastName(String lastName) {
   this.last_name = lastName;
}
public String getAvatar() {
   return avatar;
}
public void setAvatar(String avatar) {
   this.avatar = avatar;
}
}

The below code will help you with the POST method through Playwright.

@Test
public void post() throws ParseException {
   employee employee = new employee();
   employee.setEmail("john@reqres.in");
   employee.setFirstName("john");
   employee.setLastName("Holt");
   employee.setAvatar("https://reqres.in/img/faces/4-image.jpg");

   Playwright playwright = Playwright.create();
   String BaseURL = "https://reqres.in";
   Map<String, String> headers = new HashMap<>();
   headers.put("content-type", "application/json");
   APIRequestContext apiRequestContext = playwright.request().newContext(new APIRequest.NewContextOptions()
           .setBaseURL(BaseURL).setExtraHTTPHeaders(headers));
   APIResponse response = apiRequestContext.post("/api/users/", RequestOptions.create().setData(employee));

   Assert.assertEquals(response.status(),201);
   String responseText= response.text();
   JSONParser j= new JSONParser();
   JSONObject json = (JSONObject) j.parse(responseText);
   Assert.assertEquals(json.get("email"), employee.getEmail());
   Assert.assertEquals(json.get("first_name"), employee.getFirstName());
   Assert.assertEquals(json.get("last_name"), employee.getLastName());
   Assert.assertEquals(json.get("avatar"), employee.getAvatar());
   apiRequestContext.dispose();
   playwright.close();
}

3. PUT:

PUT Request is used to update the existing records via the API. we have to pass the data we want to update as a payload ex. first name, last name, etc. The response code for a successful PUT Request is 200.

For example, I am sending a POST request to ‘/api/users/55’ endpoint with the base URL ‘https://reqres.in’ and the body as given below in the screenshot:

The below example shows the implementation of the PUT method. We need to use the above POJO class to pass the data as a payload to the PUT call.

 @Test
public void put() throws ParseException {
   employee employee = new employee();
   employee.setEmail("john@reqres.in");
   employee.setFirstName("Harry");
   employee.setLastName("Eve");
   employee.setAvatar("https://reqres.in/img/faces/4-image.jpg");

   Playwright playwright = Playwright.create();
   String BaseURL = "https://reqres.in";
   Map<String, String> headers = new HashMap<>();
   headers.put("content-type", "application/json");
   APIRequestContext apiRequestContext = playwright.request().newContext(new APIRequest.NewContextOptions()
           .setBaseURL(BaseURL).setExtraHTTPHeaders(headers));
   APIResponse response = apiRequestContext.put("/api/users/55", RequestOptions.create().setData(employee));

   Assert.assertEquals(response.status(),200);
   String responseText= response.text();
   JSONParser j= new JSONParser();
   JSONObject json = (JSONObject) j.parse(responseText);
   Assert.assertEquals(json.get("email"), employee.getEmail());
   Assert.assertEquals(json.get("first_name"), employee.getFirstName());
   Assert.assertEquals(json.get("last_name"), employee.getLastName());
   Assert.assertEquals(json.get("avatar"), employee.getAvatar());
}

4 DELETE:

We can delete existing records using the API by using DELETE Request. Ideally, you must have added a record before you delete it. Hence you would need to append an ID to the DELETE URL. To delete the record using API first we need to pass the record URI (Universal Resource Identifier). The response code for a successful DELETE Request is 200.

For example, I am sending a POST request to ‘https://retoolapi.dev’ endpoint with base URL ‘/3njSPM/calc/43’ and the body as given below in the screenshot:

Following is the code for the DELETE method in Playwright

@Test
public void delete()  {
   Playwright playwright = Playwright.create();
   String BaseURL = "https://retoolapi.dev";
   APIRequestContext apiRequestContext = playwright.request().newContext(new APIRequest.NewContextOptions()
           .setBaseURL(BaseURL));
   APIResponse response = apiRequestContext.delete("/3njSPM/calc/40");
   int responseCode = response.status();
   Assert.assertEquals(responseCode, 200);
   Assert.assertEquals(response.statusText(), "OK");
   apiRequestContext.dispose();
   playwright.close();

 

After performing a DELETE call we can perform a GET call on the same endpoint to verify data is actually deleted. For this GET call, we will get response code 204 as the content is not found.

For Reference: https://playwright.dev/java/docs/api-testing

Conclusion:-

GET, PUT, POST, and DELETE are the basic CRUD API methods used in any Web application. With the help of the inbuilt functionalities of Playwright, API Automation Testing became much easier and faster.

Read more blogs here

How to Automate tests using Taiko with Cucumber in JavaScript

How to Automate tests using Taiko with Cucumber in JavaScript

Hello! In this blog, I will be exploring how to automate tests using Taiko with Cucumber in JavaScript. The Taiko tool is easy to automate and is very reliable, and it works faster to execute and run test cases. It is a user-friendly tool as well. 

What is Takio?

A Taiko is an automation tool that is available for free and it is an open-source browser automation tool. It is built by the ThoughtWorks team. It uses the Node.js library to automate the chrome browser. Taiko is very useful to create maintainable and highly readable JavaScript tests.  

Taiko Features:

The Taiko was explicitly built to test modern web applications. 

The features of Taiko that set it apart from other browser automation solutions are listed below.

  1. Easy Installation
  2. Interactive Recorder
  3. Smart Selectors
  4. Handle XHR and dynamic content
  5. Request/Response stubbing and mocking

We can use Taiko on three platforms:

  1. Windows
  2. macOS
  3. Linux

How to install Taiko?

A Taiko is available on npm: You can use the following NPM command to install the taiko on your system.

npm install -g taiko

What is cucumber?

A Cucumber is a testing tool that allows BDD.  It offers a way to write tests that everyone, regardless of technical ability, can follow. Before developers build their code in BDD, users (business analysts, product owners) first write scenarios or acceptance tests that describe the system behavior from the perspective of the customer. Such scenarios and acceptance tests then are reviewed and approved by the product owners.

How to install Cucumber?

Basically, cucumber is available on npm: You can use the following NPM command to install the cucumber on your system.

npm install --save-dev @cucumber/cucumber

Getting Started 

We will be using Visual Studio code to write our test automation code in JavaScript. We will create a feature file first, then click on the left side of the panel and choose “new file” from the menu that appears. Give a file name after that, such as the Calculator. feature

I’ll start out by introducing the Taiko framework, which integrates BDD and Cucumber. You will be guided through the code in the next step.

Feature: Calculator operations
  @smoke
  Scenario: Addition of 2 numbers
    Given I launch calculator application
    When I click on number 2
    And I click on operator +
    And I click on number 2
    Then I verify the result is 4

I’ll describe how to automate the calculator page in this place. The code shown below builds calculator steps where we must import statements provided by cucumber before navigating to the support folder. 

After that, we can import the cucumber and assertion statements and that will build a page where all the steps are generally placed. 

Basically, this is the step definition file where we need to map the feature file steps and call methods declared in the page file.

After that, we have to import the page file in the step definition file.  And we need to call the methods declared in the page file. 

Following is the code snippet for the step definition file.

const { Given, When, Then } = require('@cucumber/cucumber')
const calculate = require('../../pages/calculatorPage')
Given('I launch calculator application', async function () {
  await new calculate().launch()
})
When('I click on number {string}', async function (num) {
  await new calculate().click_number(num)
})
When('I click on operator {string}', async function (num) {
  await new calculate().click_operator(num)
})
Then('I verify the result is {string}', async function (num) {
  await new calculate().verify_result(num)
})

Now let’s create a page(We are using Page Object Model (POM) structure here) file where we have to declare the class and all the methods cleaning in step definitions.

Following is the code snippet for the page file.

const { Before } = require('@cucumber/cucumber')
var { setDefaultTimeout } = require('@cucumber/cucumber')
setDefaultTimeout(60 * 1000)
const {
  openBrowser,
  goto,
  write,
  click,
  $,
  closeBrowser,
  setConfig,
  button,
  waitFor,
} = require('taiko')
const assert = require('assert')
setConfig({ observeTime: 30000, retryTimeout: 30000, navigationTimeout: 30000 })
class calculate {
  async launch() {
    await openBrowser({ headless: false })
    return goto('calculator.net')
  }
  async click_number(num) {
    waitFor(5000)
    await click($('span[onclick="r(' + num + ')"]'))
  }
  async click_operator(num) {
    await click($(`span[onclick="r('` + num + `')"]`))
  }
  async verify_result(num) {
    waitFor(5000)
    assert.equal(await $('#sciOutPut').text(), num)
    closeBrowser()
  }
}
module.exports = calculate

And, In the above code snippet, methods contain actions like opening the browser, visiting a website, CSS selectors and actions to be performed on it (click), and Assertions. You may notice that we are doing open browser and close browser actions into this page file itself which is not the best practice. You can move it to Before/After hooks and create a nice framework. However, that is for a later blog 🙂

So, to execute the test case, you can run the following command from the terminal.

npx cucumber-js --publish

Happy testing !!!

Conclusion:

We can automate tests using taiko with Cucumber with JavaScript very easily. Taiko is a very powerful tool and easy to implement. It will definitely compete with Selenium and Playwright in the coming years.

Read more blogs here