Visual Testing: How to Verify Toggle Colors on Real Devices with Appium and Python

Visual Testing: How to Verify Toggle Colors on Real Devices with Appium and Python

In this blog, we’ll explore how to verify toggle colors on real Android and iOS devices using Appium and Python—for visual testing, a practical guide for mobile automation testers who want to ensure their apps don’t just work, but look right too.

We’ll dive into:

  • Why is color detection essential in domains like e-commerce, healthcare, gaming, and automotive?
  • Three powerful techniques for verifying toggle states:
    • Accessibility Identifiers
    • Image Comparison
    • Pixel-Level RGB Color Extraction

Step-by-step examples for both Android and iOS devices.

Importance of Color Detection in Visual Testing

Color detection plays a crucial role in image verification across various domains, where visual accuracy directly impacts user experience, brand integrity, and functionality. Below are some key applications:

So, let’s dive into verifying toggle colors on Android and iOS app step by step

Set your system for Appium and Python Visual Testing for real Android and IOS testing

For Android testing configuration, use the blog below for reference
How to configure Windows Desktop for Android App Automation using Appium

For IOS testing configuration, use the blog below for reference
How to configure macOS for iOS Mobile App Automation using Appium

Using the Accessibility Identifiers: Utilizing accessibility identifiers (e.g., accessibility_id, content-desc, checked attribute from XPath) to determine the toggle’s state. These identifiers provide semantic information about the element, which is more reliable than relying solely on visual appearance.

Using Image Comparison: Capture a screenshot of the toggle in its “On” state and another in its “Off” state. Then, compare the screenshot of the actual toggle with the stored “On” and “Off” images.

img1 = imageio.imread(ideal_image_path)
img2 = imageio.imread(actual_image_path_repo_root)
if img1.shape != img2.shape:
  	 print("Both images should have the same dimensions")
  	 raise selenium.common.NoSuchElementException('No such element present')
diff = np.sum(np.abs(img1 - img2))
avg = diff / (img1.shape[0] * img1.shape[1] * img1.shape[2])
percentage = (avg / 255) * 100
	
if percentage == 0:
  	 return True
else:
 	  return False

In the above snippet, using opencv library from Python we are comparing the images first using the size of both the images, then calculating the average difference per Pixel for both the images.

Using Pixel Color Extraction:

The RGB (Red, Green, Blue) color model is one of the most widely used systems in digital image processing and display technologies. It is an additive color model, meaning it combines the intensity of these three primary colors(RGB) to create a broad spectrum of colors. Each color in this model is represented as a combination of Red, Green, and Blue values, ranging from 0 to 255 for 8-bit images.

  • For example:
    • (255, 0, 0) represents pure red.
    • (0, 255, 0) represents pure green.
    • (0, 0, 255) represents pure blue.
    • (255, 255, 255) represents white.
    • (0, 0, 0) represents black.

How RGB Detection Works:

RGB detection involves extracting the Red (R), Green (G), and Blue (B) intensity values of individual pixels from digital media such as images or videos. Each pixel acts as a building block of the media, storing its color as a combination of these three values.

  1. For image comparison in Python install pillow package using – from PIL import Image
  2. Load the image – image = Image.open(‘example.jpg’)
  3. Access the pixel at any location – rgb = image.getpixel((50, 50))

This will return the RGB value for that particular point. Open this website https://www.rapidtables.com/web/color/RGB_Color.html. Here you can find the color type according to RGB values, like if this method is returning the (255,215,0), which means it’s GOLD color.

Visual Testing

By entering these values, you can find the color. Also like by entering 0,0,0 you can find the black color.

Visual Testing

For demo purposes, let’s open the settings of android→connections→wifi toggle and check whether it’s turned ON or OFF.

Appium and Python Visual Testing

Use code below for reference of color detection on a real Android device (Python Visual Testing)

Pre-setup for Android Device

  1. Start Appium server using the below command, or you can use Appium GUI as well
    • appium -a 127.0.0.1 -p 4723
  2. Check connected adb devices using the below command, and you should be able to see a  connected device with the  device UDID
    • adb devices
  3. pip install Pillow
import time
from PIL import Image
import io

def app_init_for_android():
   from appium import webdriver
   from appium.webdriver.common.appiumby import AppiumBy

   desired_caps = {
       "appium:deviceName": "my_samsung",
       "appium:udid": "R9ZRskddjk0CHT",
       "platformName": "Android",
       "appium:platformVersion": "13",
       "appium:appPackage": "com.android.settings",  # Settings app package
       "appium:appActivity": "com.android.settings.Settings",  # Main Settings activity
       "automationName": "UiAutomator2"
   }

   # Initialize the Appium driver for the real iOS device
   driver = webdriver.Remote('http://127.0.0.1:4723', desired_caps)
   time.sleep(5)

   driver.find_element(
       AppiumBy.XPATH,
       "//androidx.recyclerview.widget.RecyclerView[@resource-id='com.android.settings:id/recycler_view']/android.widget.LinearLayout[2]"
   ).click()
   time.sleep(5)

   # To install pillow package use: pip install pillow
   element_1 = driver.find_element(AppiumBy.XPATH, "//android.widget.Switch[@content-desc='Wi-Fi']")
   time.sleep(5)
get_rgb_colors(element_1)
def get_rgb_colors(locator):
   element_location = locator.location
   element_size = locator.size

   screenshot = driver.get_screenshot_as_png()
   screenshot_image = Image.open(io.BytesIO(screenshot))
   width, height = screenshot_image.size
   center_x = width // 2
   center_y = height // 2 
   background_color = screenshot_image.getpixel((center_x , center_y))
   return background_color

Let’s break down the code

  • Install the required packages image, io, and time
  • Initialize the Android driver using the correct capability JASON
  • Inspect the locators to navigate to the wi-fi toggle present page
  • Find the center coordinates of the toggle using the locators method
  • Passing those coordinates to the getpixel method will give us the RGB value of that particular pixel
  • Open the website https://www.rapidtables.com/web/color/RGB_Color.html
  • Here you can find the color type according to RGB values

Use the code below for reference of colour detection on a real iOS device (Appium Visual Testing)

Pre-setup for iOS Device

  1. Start Appium server using the below command, or you can use Appium GUI as well
    • appium -a 127.0.0.1 -p 4723
  2. pip install Pillow

We have to use build command to build our project and start our testing on real iOS. For IOS id→xcode→Window→device and simulators→Identifier
(e.g. –xcodebuild -project (path_for_WebDriverAgent.xcodeproj) -scheme WebDriverAgentRunner -destination ‘platform=iOS,id=(id_of_connected_ios) test)

Appium and Python

Consider the code below for color detection on iOS automation

import time
from PIL import Image
import io

def app_init_for_ios():
   from appium import webdriver
   from appium.webdriver.common.appiumby import AppiumBy

desired_caps =
{
   "platformName": "iOS",
   "platformVersion": "ios_devie_version",
   "deviceName": "ios_device_name",
   "udid": "ios_device_udid",
   "bundleId": "com.apple.Preferences",
   "automationName": "XCUITest",
   "xcodeSigningId": "iPhone Developer",
   "xcodeOrgId": "your_xcode_id",
   "autoAcceptAlerts": true,
   "newCommandTimeout": 10000
}

   # Initialize the Appium driver for the real iOS device
   driver = webdriver.Remote('http://127.0.0.1:4723', desired_caps)
   time.sleep(5)
driver.find_element(
       AppiumBy.XPATH,
       "//XCUIElementTypeStaticText[@name="WIFI"]"
   ).click()
   time.sleep(5)

   # To install pillow package use: pip install pillow
   element_1 = driver.find_element(AppiumBy.XPATH, "//XCUIElementTypeSwitch[@name="Wi‑Fi"]")
   time.sleep(5)
get_rgb_colors(element_1)
def get_rgb_colors(locator):
   element_location = locator.location
   element_size = locator.size

   screenshot = driver.get_screenshot_as_png()
   screenshot_image = Image.open(io.BytesIO(screenshot))
   width, height = screenshot_image.size
   center_x = width // 2
   center_y = height // 2 
   background_color = screenshot_image.getpixel((center_x , center_y))
   return background_color

Let’s break down the code – If you see the code, it’s similar to the Android color verification code The two key differences are like first one is the capabilities are different for iOS, and the locator finding strategy is different.

Conclusion

1. Accessibility Identifiers:

This is the most straightforward and reliable approach. Mobile apps often include labels or attributes (like accessibility_id or content-desc) that indicate the current state of a toggle. This method requires no image processing, as it leverages metadata provided by developers—making it both efficient and robust.

2. Image Comparison:

This technique involves capturing screenshots of the toggle in both “on” and “off” states and comparing them to reference images. Tools like OpenCV or scikit-image help analyze visual similarity, accounting for minor differences due to lighting or device variations. It’s especially useful when you need to validate the UI’s visual accuracy.

3. Pixel Color Extraction:

By extracting specific RGB values from toggle regions using libraries like Pillow, this method offers precision at the pixel level. It’s ideal for verifying exact color codes, and the extracted values can be cross-referenced with tools like RapidTables for further validation.
While Android and iOS may differ slightly in setup and element location, the core strategies remain consistent. Depending on your testing needs, you can use these methods individually or in combination to ensure your app displays the correct colors—ultimately contributing to a seamless and visually consistent user experience.

Click here to read more blogs like this.

Different approaches to find verified domains using Python

Different approaches to find verified domains using Python

What is the domain?

A domain name is an online address that offers a user-friendly way to access a website. In the context of Verified domains Python, this refers to verifying that a domain is legitimate and active using Python programming techniques. In the internet world IP address is a unique string of numbers and other characters used to access websites from any device or location. However, the IP address is hard to remember and type correctly, so the domain name represents it with a word-based format that is much easier for users to handle. When a user types a domain name into a browser search bar, it uses the IP address it represents to access the site.

The Domain Name System (DNS) maps human-readable domain names (in URLs or email addresses) to IP addresses. This is the unique identity of any website or company/organization which makes any website unique and verified, It’s still possible for someone to type an IP address into a browser to reach a website, but most people want an internet address to consist of easy-to-remember words, called domain names for example: Google. , Amazon.  Etc. and domain names come with different domain extensions for example: Amazon. in, Google.com

A domain also serves several important purposes on the internet. Here are some key reasons why a domain is necessary:

  • Identification: Domain names are easier to remember than IP addresses, making it simpler to locate resources online.
  • Branding: A domain name is vital for building a professional online identity, reflecting the nature and purpose of a business.
  • Credibility: Owning a domain enhances professionalism, showing commitment to a unique online presence.
  • Email Address: A personalized email linked to a domain looks more professional and builds trust.
  • Control: Domain ownership gives you control over hosting, email management, and associated content.
  • SEO: A relevant, keyword-rich domain can improve search engine visibility.
  • Portability: Owning a domain allows you to change hosting providers while keeping the same web address, ensuring consistency.

Why do we need domain verification?

Verifying a domain name is a key step for businesses and individuals looking to establish credibility, and control over their content, and enhance their presence on digital platforms.

Let’s Understand this using the example:

Verifying your domain helps Facebook to allow rightful parties to edit link previews directly to your content.

This allows you to manage editing permissions over links and contents and prevents misuse of your domain. This includes both organic and paid content.

These verified editing permissions ensure that only trusted employees and partners represent your brand.

Domain Verification Techniques:

Domain verification is a crucial step to make sure your domain is active and not expired. When a domain is verified, users are automatically added to the Universal Directory, so they don’t have to wait for personal approval to log in. This process helps confirm that the domain is legitimate and prevents issues related to fake or misused domains. These are some techniques through which we can verify our domain.

  • WHOIS Lookup
  • Requests & Sockets
  • DNS Verification

Let’s see how we can verify valid domains to find verified domains using Python, you can employ several approaches listed below.

1) WHOIS Lookup:

  • Use the WHOIS module in Python to perform a WHOIS lookup on a domain. This method provides information about the domain registration, including the registrar’s details and registration date.
  • Install the whois module using pip install python-whois.

2) Request & Socket

  • Use Python’s request lib and socket to find verified domains For this we need to install these python dependencies requests & socket

In above function output should be True if provided url is correct

And contains correct domain name otherwise it will return false

Here we are passing hostname as a parameter and socket.gethostbyname(hostname) will give us the IP address for the host socket.create_connection((ip_address, 80)) is used for the socket to bind as a source address before making the connection. When we pass hostname or domain name with the correct extension to this function for example as given in the above function i.e “google.net” it will return True
And if the hostname/domain is incorrect it will return false.

To verify a domain in Python, you can use various approaches depending on the type of verification required. Here, is one of the common methods: DNS verification

DNS Verification:

DNS verification involves checking if a specific DNS record exists for the domain. For example, you might check for a TXT record with a specific value.

This is a Valid example of the above function where the domain is “google.com”, the function returns True when the record type is “TXT” and the expected value matches Google’s SPF TXT record. If no match is found or if the domain does not exist (it will give an NXDOMAIN exception), it returns False.

Check out the GitHub repository for more details
https://github.com/jjadhav-dj/DNS-Verification-using-python.git

Conclusion:

 A domain name is a crucial component of your online identity, providing a way for people to find and remember your website or online services. Whether for personal use, business, or any other online endeavor, having a domain name is an essential part of establishing a presence on the internet.

Each approach serves a distinct purpose in verifying a domain’s legitimacy. Choose the verification method based on your specific use case and requirements. Verified domains Python methods like DNS verification are often used for domain ownership verification, while WHOIS Lookup provides essential registration details.

Click here to read more blogs like this and learn new tricks and techniques of software testing.

Building a robust mobile test automation framework using Appium in Python

Building a robust mobile test automation framework using Appium in Python

In this blog, we will explore how to build a robust mobile test automation framework using Appium in Python (behave framework). As a result, it will be very useful for executing the program.

Mobile test automation can be more challenging than web automation, as inspecting and interacting with mobile elements requires additional effort. However, with the help of Appium, an open-source tool, it is possible to overcome these challenges and build a powerful mobile test automation framework. In this blog, we will explore how to create a robust framework using Appium in conjunction with the Behave framework in Python.

Let’s talk about robust test frameworks

Robust test automation framework ranks highly on the list of Software Testing “must-haves”.

It helps improve the overall quality and reliability of software when executed in a structured manner.

If we don’t build the right framework then the results will be: Inconsistent test results, Non-modularized tests, and Maintenance difficulties. The automation framework needs to be well organized to make it easier to understand. An organized framework provides an easier way to maintain and expand.

There are many features that we should consider to make the automation framework more robust. 

  • Scalability – The automation framework that you have in your organization should be scalable. It should not just apply to one project. Your automation framework should be applied throughout projects across the organization. It should be an organization-wide test automation framework.
  • Re-portability – Every automation framework should have a good reporting capability. The test framework engineer can choose a third-party reporting library.
  • Configurable – A framework should be configurable. It should execute scripts in different test environments. The automation framework should not be restricted to a single test environment. The user credentials should not be “hard-coded” in the automation script itself. 
  • Re-usability – The framework should follow re-usability. We should use the same methods, and page objects in all the test scenarios in the test automation framework.
  • Extendability –  You should be able to integrate easily with other third-party tools via APIs. Automation frameworks should be easily integrated with security testing tools, web proxy debugging tools, test case management tools, or with other global frameworks thereby making it more hybrid in nature. 

Benefits

  • Increase product reliability – Accurate, efficient, automated regression tests – reduce risks
  • Reduce the product release cycle time – Improve the time to market, Reduce QA cycle time

Let’s start with basic

Appium is an open source Test Automation Framework which is used for automating mobile applications.

Appium supports Android, IOS mobile apps, and Windows PC Desktop apps. We can automate Native, Hybrid, and Mobile web apps using Appium.

Uses of Appium:

  • Appium is open source and it is free of cost.
  • Appium supports Android, IOS, and Windows to run test scripts.
  • Appium supports languages such as Python, Java, Perl, PHP, C#, and Ruby
  • Appium supports different operating systems such as Mac, Windows, Linux, UNIX, etc.
  • Functional test cases of mobile applications can be easily automated.

Appium Inspector

Appium Inspector is a tool for QA engineers who want to automate mobile applications. Basically, this tool also serves as the standard procedure for identifying mobile application elements.

The following are the used for inspecting the mobile element for both Android and iOS.

  • Download the Appium inspector.exe.

This is the link: https://github.com/appium/appium-inspector

  • After downloading the exe file launch the Appium inspector.exe file. On top of the web page, select to Cloud-based platform – BrowserStack

BrowserStack is a cloud-based real devices platform that provides support for both manual and automated testing of mobile apps for both Android and iOS devices. One of its standout features is the App Live feature, which allows users to manually test their mobile apps on over 3000 real Android and iOS devices.

BrowserStack supports testing across different environments, including Dev, QA, Staging, and Production apps from the play store or app store. This makes it easy for developers to test their apps in various environments and ensure that they are working correctly in each environment.

To proceed further we need BrowserStack Username and BrowserStack Access Key

For that, Go to https://www.browserstack.com/

Following are the steps that will guide the process

  • Log in to your BrowserStack account ->Navigate to the “Account” section ->Then Go to Summary
  • After going to Summery Section you will get Username and Password. Copy both Username and Password and paste them into Appium inspector fields
  • Go to Desired Capabilities -> Here we need to add basic capabilities which are required for starting the session. Below image will guide you
  • To add capabilities need to click on the “+” symbol as shown in the below image
  • Add the capabilities with desired values as shown in the below image
  • In the Value field, we need to add data that we want to add.

The most important thing here is for the last field “Appium: app”, we have to upload the .ipa or .ipk or .aab file on BrowserStack.

For that, there are 2 ways mentioned below

  1. Through Command Line
  2. Directly through BrowserStack

The most important thing here is for the last field “Appium: app”, we have to upload the .ipa or .ipk or .aab file on BrowserStack.

Let’s start

  • Through Command Line

To upload the .ipa or .ipk or .aab file on BrowserStack, the following Curl command is very useful.

Curl is a command line tool that enables data exchange between a device and a server through a terminal. Using this command line interface (CLI), a user specifies a server URL (the location where they want to send a request) and the data they want to send to that server URL.

Go to cmd and write this curl command

curl -u “username:accesskey” -X POST “https://api-cloud.browserstack.com/app-automate/upload” -F “file=@path of the file where you save your apk or IPA file” -F “custom_id=any name “

  • Directly through BrowserStack:

 Go to “App Live” -> Click on Uploaded Apps -> And Upload your file.

But here 1st one is preferable: Through this command, we get “app_url” which is required in Desired Capabilities:

Copy that app_url and paste it into Desired Capabilities.

Here you need to Click on Start Session -> You will get below the window.

You can select the element in the App or from the App Source section and the attributes including ID, Name, Text, etc will be displayed on the right side under the Selected Element section and you can create Xpaths using those attributes.

Developing Framework

  1. Python: https://www.python.org/downloads/ visit the site to download and install Python in your system if it is not there. 
  2. 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/ 

  1. Pycharm IDE (Professional or Community): https://www.jetbrains.com/pycharm/download/ 
  1. Install Appium-Python-Client:

pip install Appium-Python-Client

For more details please visit: https://pypi.org/project/Appium-Python-Client/

  1. Install allure for report generating using:

pip install allure-behave 

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

  1. 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 mobile test automation framework using Appium in Python.

You have to follow the below 7 steps to build a robust mobile test automation framework using Appium.

Step 1: 

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

Step 2:

In this step, we will be creating a Features folder in which we will be creating our feature files for different scenarios. Every step in a Feature File describes the action we are going to perform on UI. 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. Both scenario and scenario outlines contain steps that are easy to understand for non-technical persons. We are giving tags for the feature files. We can also give it for the scenarios present in that file. Depending on our test cases. Note that the feature file should end with a .feature extension.

@iOS
#@android
Feature: Simple Calculator
 Addition of two numbers
 Scenario: Verify addition of two numbers
   Given I am on calculator home page
   When I enter '4'
   And I enter operator of addition
  And I enter operator of addition this should be like And I enter 
 ‘+’ operator so if possible you can update code as per this
   And Enter number '2'
   And I enter operator '='
   Then I see result as '6'

Step 3:

After creating the feature file now create a step file. Both feature files and step files are essential parts of the BDD framework. The steps with ‘When’ are related to the user actions like navigation, clicking on the button, and filling in the information in input boxes. The steps with ‘Then’ are related to the verifications or Assertions. In this, we are using both iOS and Android, so the step file should look like this. We are creating only one-step files for both iOS and Android.

Purpose of Step file: The step file is to attach steps from the feature file to the page file, where actual implementation is available.

from behave import *
use_step_matcher("parse")
@given("I am on calculator home page")
def step_impl(context):
   print("User is on Homepage")

@when("I enter '{number}'")
def step_impl(context, number):
   str = context.config.userdata["deviceType"]
   print("str " + str)
   if str == "['iOS']":
       context.iOS_cal.iOS_tap_number1(number)
   else:
       context.android_cal.tap_number1()

@step("I enter operator of addition")
def step_impl(context):
   str = context.config.userdata["deviceType"]
   print("str " + str)
   if str == "['iOS']":
       context.iOS_cal.iOS_tap_operator()
   else:
       context.android_cal.tap_operator()

@step("Enter number '{number}'")
def step_impl(context, number):
   str = context.config.userdata["deviceType"]
   print("str " + str)
   if str == "['iOS']":
       context.iOS_cal.iOS_tap_number1(number)
   else:
       context.android_cal.tap_number2()

@step("I enter operator '{operator}'")
def step_impl(context, operator):
   str = context.config.userdata["deviceType"]
   print("str " + str)
   if str == "['iOS']":
       context.iOS_cal.iOS_equals(operator)
   else:
       context.android_cal.equals()

@then("I see result as '{result}'")
def step_impl(context, result):
   str = context.config.userdata["deviceType"]
   print("str " + str)
   if str == "['iOS']":
       flag = context.iOS_cal.iOS_verify_result()
       assert flag == True
   else:
       flag = context.android_cal.verify_result()
       assert flag == True

Step 4:

In this step, we are creating two-page files one for iOS and one for Android 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.

iOS page file:

import time
from appium.webdriver.common.mobileby import MobileBy
from selenium.common import NoSuchElementException
from selenium.webdriver.support import expected_conditions as EC
from time import sleep

from Features.Pages.BasePage import Basepage

class iOS_Calculator_Page(Basepage):
   def __init__(self, context):
       Basepage.__init__(self, context.driver)
       self.context = context

       self.add_operator = "//XCUIElementTypeStaticText[@name='+']"
       self.result = "(//XCUIElementTypeStaticText)[1]"

   def iOS_tap_number1(self,number):
       time.sleep(2)
       tap_on = self.wait.until(
           EC.presence_of_element_located((MobileBy.XPATH, "//XCUIElementTypeButton[@name='"+number+"']")))
       tap_on.click()

   def iOS_tap_operator(self):
       time.sleep(2)
       tap_on = self.wait.until(
           EC.presence_of_element_located((MobileBy.XPATH, self.add_operator)))
       tap_on.click()

   def iOS_equals(self, operator):
       time.sleep(2)
       tap_on = self.wait.until(
           EC.presence_of_element_located((MobileBy.XPATH, "//XCUIElementTypeStaticText[@name='"+operator+"']")))
       tap_on.click()

   def iOS_verify_result(self):
       sleep(5)
       try:
           verify_element = self.wait.until(EC.presence_of_element_located(
               (MobileBy.XPATH, self.result))).is_displayed()
       except NoSuchElementException:
           verify_element = False
       return verify_element

Android page file:

from appium.webdriver.common.mobileby import MobileBy
from selenium.common import NoSuchElementException
from selenium.webdriver.support import expected_conditions as EC
import time

from Features.Pages.BasePage import Basepage

class android_Calculator_Page(Basepage):
   def __init__(self, context):
       Basepage.__init__(self, context.driver)
       self.context = context
       self.number1 = "(//android.widget.Button)[5]"
       self.add = "//android.widget.Button[@content-desc='plus']"
       self.number2 = "(//android.widget.Button)[9]"
       self.operator_equals = "(//android.widget.Button)[13]"
     self.verify = "(//android.widget.TextView)[2]"
   def tap_number1(self):
       time.sleep(2)
       tap_on = self.wait.until(
           EC.presence_of_element_located((MobileBy.XPATH, self.number1)))
       tap_on.click()

   def tap_operator(self):
       time.sleep(2)
       tap_on = self.wait.until(
           EC.presence_of_element_located((MobileBy.XPATH, self.add)))
       tap_on.click()

   def tap_number2(self):
       time.sleep(2)
       tap_on = self.wait.until(
           EC.presence_of_element_located((MobileBy.XPATH, self.number2)))
       tap_on.click()

   def equals(self):
       time.sleep(2)
       tap_on = self.wait.until(
           EC.presence_of_element_located((MobileBy.XPATH, self.operator_equals)))
       tap_on.click()

   def verify_result(self):
       try:
           verify_element = self.wait.until(EC.presence_of_element_located(
               (MobileBy.XPATH, self.verify))).is_displayed()
       except NoSuchElementException:
           verify_element = False
       return verify_element

Base Page File:

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. On this page, we can create a method that gets used frequently in our code like the click() method or send_keys() method, etc.

from selenium.webdriver.support.ui import WebDriverWait
# In the base page we are creating an object of the 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, 60)
       self.implicit_wait = 25

Step 5:

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. We have added before feature hooks.

In the feature file, we have given tags(@iOS and @android) before the feature.

  • def before_feature hook: This will check for which device type (iOS or Android) we are executing the code.
  • def before_scenario hook: We are checking the execution mode and within that adding device type conditions for iOS and Android. 

Here we are using  “context. config.userdata[]” This will read data from the behave.ini file

Added condition for iOS and Android

import json
from appium import webdriver
from allure_commons.types import AttachmentType
from allure_commons._allure import attach
from Features.Pages.BasePage import Basepage
from Features.Pages.android_Calculator_Page import android_Calculator_Page
from Features.Pages.iOS_Calculator_Page import iOS_Calculator_Page

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

def before_feature(context, feature):
   tags = str(feature.tags)
   print("Tags " + tags)
   context.config.userdata["deviceType"] = tags
   print("Device Type :" + context.config.userdata["deviceType"])

def before_scenario(context, scenario):
   if context.config.userdata["executionMode"] == "Browserstack":
       if context.config.userdata["deviceType"] == "['iOS']":
           print(context.config.userdata["deviceType"])
           context.driver = webdriver.Remote(
               command_executor='https://' + context.config.userdata["userName"] + ':' + context.config.userdata[
                   "accessKey"] + '@hub-cloud.browserstack.com/wd/hub',
               desired_capabilities={
                   "platformName": "iOS",
                   "build": context.config.userdata["iOS_browserstack_build"],
                   "deviceName": context.config.userdata["iOS_browserstack_device"],
                   "os_version": context.config.userdata["iOS_device_os_version"],
                   "app": context.config.userdata["iOS_browserstack_appUrl"],
               }
           )
       else:
           context.driver = webdriver.Remote(
               command_executor='https://' + context.config.userdata["userName"] + ':' + context.config.userdata[
                   "accessKey"] + '@hub-cloud.browserstack.com/wd/hub',
               desired_capabilities={
                   "platformName": "android",
                   "build": context.config.userdata["android_browserstack_build"],
                   "deviceName": context.config.userdata["android_browserstack_device"],
                   "os_version": context.config.userdata["android_device_os_version"],
                   "app": context.config.userdata["android_browserstack_appUrl"],
               }
           )
   else:
       print("...")
   context.driver.switch_to.context('NATIVE_APP')
   baseobject = Basepage(context.driver)
   context.android_cal = android_Calculator_Page(baseobject)
   context.iOS_cal = iOS_Calculator_Page(baseobject)
   context.stepid = 1

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

Step 6:

INI files are configuration files used by Windows to initialize program settings. The main role is to set values for parameters and configuration data required at startup or used by setup installers.

The configuration files should begin with the keyword [behave] and follow Windows INI style format.

Here we are giving BrowserStack Capabilities.

[behave]
color=False
show_snippets=True
show_skipped=True
dry_run=False
show_source=True
show_timings=True
stdout_capture=True
stderr_capture=True
log_capture=True
default_format=pretty
[behave.formatters]
allure=allure_behave.formatter:AllureFormatter
[behave.userdata]
executionMode=Browserstack
userName=harishekal_QYol9T
accessKey=RG6juTxk2Coom4p5JPSN
iOS_browserstack_appUrl=bs://f3b6763a19d710fc72fdc615db7119ea654e06da
iOS_browserstack_build=0.0.0
iOS_browserstack_device=iPhone 11
iOS_device_os_version=15.4
iOS_deviceType = iOS

android_browserstack_appUrl=bs://495dd3b36cae77a1ae2f4ce6f439456999e0bb45
android_browserstack_build=0.0.1
android_browserstack_device=Google Pixel 3
android_device_os_version=9.0
android_deviceType = Android

Copy user userName and accessKey of the user BrowserStack account. And iOS_broserstack_appUrl – Uploaded .ipa file through curl command. android_broserstack_appUrl – Uploaded .apk file through curl command.

Congratulations, finally we have created our own Python Selenium Behave BDD framework. 

Step 7:

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/Calculator.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

I have added this framework to the following Git Repository.

https://github.com/spurqlabs/PythonAppiumMobileFramework

Conclusion: 

Creating a robust mobile testing framework using Appium is very important as well as feels like a tedious task but with the right guidelines, everyone can create a testing framework. This framework helps improve the quality and efficiency of the testing process. I hope this blog will help everyone to create a robust mobile testing framework using Appium. Here, we choose a behave framework over other existing frameworks because of its better understanding, ease of adaptation, and ease to understand for end users.

API BDD Test automation framework using Behave and Python

API BDD Test automation framework using Behave and Python

API’s the term we heard a lot and wanted to know more about it. The questions that come to our mind are what is it? Why is it so important? How to test it? So, let’s just explore these questions one by one. API testing is accessible only if you know what to test and how to test. Again, a proper framework will help you to achieve your goals and deliver a good quality of work. The importance of automation framework and the factors we should consider for choosing the proper framework are described in our previous blog. Please go through the blog here, then you can start reading this blog because you will have a good understanding of automation testing frameworks. 

To build the API testing framework we will be using the BDD approach. Again, why I have chosen a BDD framework for API testing the reason is very simple the BDD approach provides you with an easy understanding of the framework, you can easily maintain the framework and they have feature files that are very easy to understand for a non-technical person. 

What is API?

API (Application Programming Interface) is like a mechanism that works between the two software components and helps them to communicate with each other. The communication happened using sets of definitions and set protocols. In simple language, API works as an intermediate between two systems and helps them exchange data and communicate. The working mechanism of Rest API is straightforward they work by sending requests and receiving a response in a standardized format. Usually, the standardized format used for Rest API is JSON i.e. (JavaScript Object Notation) 

Let’s understand it better with an example. Consider you are using a ticket booking app to book a flight ticket. As the app is connected to the internet so it will set data to the server. When the server receives the data it interprets it and performs the required actions and sends it back to your device. Then the application translates that data and display the information in a readable way. So this is how API works. I hope you have understood the working mechanism of API’s now let’s discuss the next topic i.e. 

What is API Testing?

As we have understood what is an API and how they work so let’s see why their testing is important. Basically, API testing is a part of software testing that includes the testing of the functionality, reliability, security, and performance of API. API is used for data transfer and to establish communication between the two systems so testing APIs includes verifying that the APIs are meeting its requirement, performing as per the expectations, and can handle a variety of inputs. This testing provides you the information that the API’s functionality is correct and efficient and the data they return is accurate and consistent. 

Why is API Testing Important?

API testing is an important part of a Software testing process as it helps you to understand the functionality of the working APIs and validate any defect present before the application is released to the end users. The other key reasons why API testing is important to include: 

  • Ensuring Functionality
  • Validating data integrity
  • Enhancing the Security
  • Improving the Performance
  • Detecting Bugs and Issues
  • Improving readability and stability
  • Facilitating integration and collaboration 

All the above-mentioned points get checked and validate in API testing. Till now we have discussed what is api, what is api testing, and why it is important. Let’s see what different tools are available to conduct the manual as well as automation testing of API. 

Tools for Manual API Testing:

  1. Postman
  2. SoapUI
  3. Insomnia
  4. Paw
  5. Advanced REST Client (ARC)
  6. Fiddler
  7. cURL

Tools for API Automation Testing:

  1. Postman
  2. SoapUI
  3. RestAssured
  4. RestSharp
  5. Apache HTTP client
  6. JMeter
  7. Karate
  8. Newman
  9. Pact.js
  10. Cypress.js

These are just a few examples of the tools available for both manuals as well as automation testing of API. Each mentioned tool has its own strength and weakness and the choice of the right tool for your API testing depends upon the requirement and the specific needs of the project. These tools will help us to ensure that the APIs meet the desired functionality and performance requirements. 

Now we are more familiar with APIs so let’s start the main topic of our discussion and i.e. Python Behave API Testing BDD Framework. 

Framework Overview:

To validate all the above-mentioned points creating a robust API testing framework is very essential. With the help of the below-mentioned steps, you will come to know how to create your own API testing framework. Here, we are going to create a BDD framework. Please go through this blog before starting to read this blog as the previous blog will help you to understand the advantages of BDD and this blog is linked to the previous blog topics. You can read the previous blog here

This framework structure contains a feature file, a step file, and a utility file. We will be discussing all these terms shortly. To create such a framework you need to follow certain steps to make your work tedious-free and easy. 

Step1: Prerequisites

  1. Python: https://www.python.org/downloads/ visit the site to download and install python in your system if it is not there.
  2. Pycharm IDE (Professional or Community): https://www.jetbrains.com/pycharm/download/ 
  3. Install all the required packages using the below command as long as you have all the packages mentioned in rquirement.txt with the right version number

pip install -r requirement.txt

  1. To know more about behave, allure report please visit https://pypi.org/project/behave/ & https://pypi.org/project/allure-behave/
  2. We can also install the mentioned packages from the settings of Pycharm IDE 

Step2: Creating Project

After understanding the prerequisites the next step is to create a project in our IDE. Here I am using a Pycharm Professional IDE. As mentioned in the above step, we will install the packages mentioned in the requirement.txt file. Please note it is not compulsory to use Pycharm Professional IDE to create this framework you can use the community version too. 

Step3: Creating a Feature File

In this, we will be creating a feature file. A feature file consists of steps. These steps are mentioned in the gherkin language. The feature is easy to understand and can be written in the English language so that a non-technical person can understand the flow of the test scenario. In this framework we will be automating the four basic API request methods i.e. POST, PUT, GET and DELETE.  We are taking https://reqres.in/

We can assign tags to our scenarios mentioned in the feature file to run particular test scenarios based on the requirement. The key point you must notice here is the feature file should end with .feature extension. We will be creating four different scenarios for the four different API methods. 

Feature: User API
Verify the GET PUT POST DELETE methods of User API
  @api
  Scenario: Verify GET call for single user
    When User sends "GET" call to endpoint "api/users/2"
    Then User verifies the status code is "200"
    And User verifies GET response contains following information
      | First_name | Last_name | Mail-id                |
      | Janet      | Weaver    | janet.weaver@reqres.in |

  @api
  Scenario: Verify POST call for single user
    When User sends "POST" call to endpoint "api/users"
      | Name   | Job  |
      | Yogesh | SDET |
    Then User verifies the status code is "201"
    And User verifies POST response body contains following information
      | Name   | Job  |
      | Yogesh | SDET |

  @api
  Scenario: Verify PUT call for single user
    When User sends "PUT" call to endpoint "api/users/2"
      | Name   | Job  |
      | Yogesh | SDET |
    Then User verifies the status code is "200"
    And User verifies PUT response body contains following information
      | Name   | Job  |
      | Yogesh | SDET |

  @api
  Scenario: Verify DELETE call for single user
    When User sends DELETE call to the endpoint "api/users/2"
    Then User verifies the status code is "200"

Step4: Creating a Step File

Unlike the automation framework which we have built in the previous blog, we will be creating a single-step file for all the feature files. In the BDD framework, the step files are used to map and implement the steps described in the feature file. Python’s behave library is very accurate to map the steps with the steps described in the feature file. We will be describing the same steps in the step file as they have described in the feature file so that behave will come to know the step implementation for the particular steps present in the feature file. 

from behave import *
from Utility.API_Utility import API_Utility
api_util = API_Utility()

@when('User sends "{method}" call to endpoint "{endpoint}"')
def step_impl(context, method, endpoint):
    global response
    response = api_util.Method_Call(context.table, method, endpoint)
@then('User verifies the status code is "{status_code}"')
def step_impl(context, status_code):
    actual_status_code = response.status_code
    assert actual_status_code == int(status_code)

@step("User verifies GET response contains following information")
def step_impl(context):
    api_util.Verify_GET(context.table)
    response_body = response.json()
    assert response_body['data']['first_name'] == context.table[0][0]
    assert response_body['data']['last_name'] == context.table[0][1]
    assert response_body['data']['email'] == context.table[0][2]

@step("User verifies POST response body contains following information")
def step_impl(context):
    api_util.Verify_POST(context.table)
    response_body = response.json()
    assert response_body['name'] == context.table[0][0]
    assert response_body['job'] == context.table[0][1]

@step("User verifies PUT response body contains following information")
def step_impl(context):
    api_util.Verify_PUT(context.table)
    response_body = response.json()
    assert response_body['Name'] == context.table[0][0]
    assert response_body['Job'] == context.table[0][1]

@when('User sends DELETE call to the endpoint "{endpoint}"')
def step_impl(context, endpoint):
    api_util.Delete_Call(endpoint)

Step5: Creating Utility File

Till now we have successfully created a feature file and a step file now in this step we will be creating a utility file. Generally, in Web automation, we have page files that contain the locators and the actions to perform on the web elements but in this framework, we will be creating a single utility file just like the step file. The utility file contains the API methods and the endpoints to perform the specific action like, POST, PUT, GET, or DELETE. The request body i.e. payload and the response body will be captured using the methods present in the utility file. So the reason these methods are created in the utility file is that we can use them multiple times and don’t have to create the same method over and over again. 

import json
import requests
class API_Utility:
    data = json.load(open("Resources/config.json"))
    api_url = data["APIURL"]
    global response

    def Method_Call(self, table, method, endpoint):
        if method == 'GET':
            uri = self.api_url + endpoint
            response = requests.request("GET", uri)
            return response

        if method == 'POST':
            uri = self.api_url + endpoint
            payload = {
                "name": table[0][0],
                "job": table[0][1]
            }
            response = requests.request("POST", uri, data=payload)
            return response

        if method == 'PUT':
            uri = self.api_url + endpoint
            reqbody = {
                "Name": table[0][0],
                "Job": table[0][1]
            }
            response = requests.request("PUT", uri, data=reqbody)
            return response

    def Get_Status_Code(self):
        status_code = response.status_code
        return status_code

    def Verify_GET(self, table):
        for row in table:
            first_name = row['First_name']
            last_name = row['Last_name']
            email = row['Mail-id']
            return first_name, last_name, email

    def Verify_POST(self, table):
        for row in table:
            name = row['Name']
            job = row['Job']
            return name, job

#Following method can be merged with POST, however for simplicity I kept it
    def Verify_PUT(self, table):
        for row in table:
            name = row['Name']
            job = row['Job']
            return name, job

    def Delete_Call(self, endpoint):
        uri = self.api_url + endpoint
        response = requests.request("DELETE", uri)
        return response

Step6: Create a Config file

A good tester is one who knows the use and importance of config files. In this framework, we are also going to use the config file. Here, we are just going to put the base URL in this config file and will be using the same in the utility file over and over again. The config file contains more data than just of base URL when you start exploring the framework and start automating the new endpoints then at some point, you will realize that some data can be added to the config file. 

Additionally, the purpose of the config files is to make tests more maintainable and reusable. Another benefit of a config file is that it makes the code more modular and easier to understand as all the configuration settings are stored in a separate file and it makes it easier to update the configuration settings for all the tests at once. 


     "APIURL": "https://reqres.in/"

Step7: Execute and Generate Allure Report

The reason behind using allure reports as a reporting medium is because the allure report provides detailed information about the test execution process and results which includes the test status, test steps, duration, and screenshots of the test run. The report is generated in HTML i.e. web format making it easy to share with team members and with clients and easy to understand. It provides a dashboard that is user-friendly having interactive charts and graphs that provide a detailed analysis of the test results. 

Let’s understand how to execute API tests and generate an allure report for automated API calls. To generate the report we will have to execute the test using the terminal or command line. There are two steps to follow sequentially they are as follows:

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

The purpose of the above command is to execute the test present in the mentioned feature file and generate a JSON report folder. 

  1. allure generate Report_Json -o Report_Html –clean

This command is used to generate an HTML report from the JSON report. So, that it is easy to understand and can be shared with team members or clients. 

Please find the attached GitHub repository link. I have uploaded the same project to this repository and also attached a Readme.md file which explains the framework and the different commands we have used so far in this project. 

https://github.com/spurqlabs/PythonBehaveApiFramework

Conclusion:

Before creating a framework it is very important to understand the concept and I hope I have provided enough information for the different queries on APIs. In conclusion, creating a BDD API testing framework using Python and Behave is easy to process if you know how to proceed further. By following the steps outlined in this blog I am sure you can create a powerful and flexible framework that will help you to define and execute the test cases, generate a detailed report with allure and also iterate with other testing tools and systems.  Again I am suggesting you check out the previous blog here because that will clear most of your doubts on automation testing frameworks and will help you to create your own automation testing framework. 

Read more blogs here