UI automation using Selenium is getting a good buzz since selenium came up with Webdriver which provides useful and essential methods, properties, and frameworks to automate web UI. Programming languages like Java, Python, C#, etc., are widely used with Selenium to automate web applications. In this article, we are going to talk about how python can be used with Selenium web drivers for creating test automation scripts.

How to build an automation framework

A framework is basically a way of creating a prototype to structure and organize your test automation scripts so that they can be easily run and maintained throughout the lifecycle of your test.

Python provides various inbuilt modules with the help of which an efficient framework can be built using selenium. 

  • Unittest
  • Pytest
  • Page object model
  • Data-driven testing
  • Behavior-driven development (BDD)

Using a combination of all the above framework techniques, hybrid automation framework can be created by following below standards,

  • All the generic methods or static methods that are reusable in nature irrespective of your web application can be created under a class called base class and should be organized in ‘Base’ directory of your project.
  • Page object model feature enables you to create a test script of every individual web page of your web application and such test scripts can be organized in a directory/package called Page.
  • Test methods should be created for all your test cases under test class which can be stored in ‘Tests’ directory/package of your framework.
  • All the screenshots captured from the test methods should be saved and stored in the ‘Screenshots’ directory/package of your framework.
  • All your behavior-driven tests (business use cases/scenarios) should be kept and stored under ‘BDD’ directory/package.
  • While using pytest you need to make sure that all your test methods should follow certain naming conventions like 
  • All the test methods should start with ‘test’ keyword.
  • Class names should start with ‘test’ keyword. The typical folder structure should look like:

UI automation using Selenium Python

 

#1 For BDD using behave, a feature file should be created containing use cases/business scenarios in the below format and this feature file should be kept under Feature folder/directory.

 Feature:

                  Scenario:

                         Given:

                         When:

                         Then:

 

#2 steps directory should contain steps.py file containing step implementation for your scenario defined in the feature file.

   BDD

                 ---features

                      --steps

                          ---steps.py

                     --<filename>.feature

 

#3 Various reports can be generated for test runs like the Html report, Allure report to capture test results.

 

#4 With the help of the inbuilt logging module, logs can be generated for required events/actions.

Base class

The following code depicts a base class having few generic, static and reusable methods defined. 

from __future__ import unicode_literals, print_function

from selenium import webdriver

from selenium.webdriver.common.by import By

from selenium.webdriver.support import expected_conditions as EC

from selenium.webdriver.common import keys

from selenium.webdriver.support.wait import WebDriverWait

import SampleProject.loggingpackage.custom_logger_new as cln

import time

import os

import logging

# This is a base class
class Base():

    __baseURL = 'https://learn.letskodeit.com/p/practice'

    __loginURL = 'https://learn.letskodeit.com/p/login'

    __expURL = 'https://www.expedia.co.in/'

    #__ChromeDriver = webdriver.Chrome()

    #__FirefoxDriver = webdriver.Firefox()

    #__SafariDriver = webdriver.Safari(executable_path='/usr/bin/safaridriver')

    # __init__ constructor

    def __init__(self, driver):

        self.driver = driver

    # launchBrowser method will open and launch the firefox browser

    def launchBrowser(self, browserType):

        if browserType == 'safari':

            self.driver = webdriver.Safari()

            self.driver.maximize_window()

            self.driver.get(self.__baseURL)

            if self.driver.title is not None:

                print('URL {0} is successfully launched and homepage {1} is displayed'.format(self.__baseURL,

                                                                                         self.driver.title))

            else:

                print('URL is not launched successfully')

        if browserType == 'firefox':

            self.driver = webdriver.Firefox()

            self.driver.maximize_window()

            self.driver.get(self.__baseURL)

            if self.driver.title is not None:

                print("URL '{0}' is successfully launched and homepage '{1}' is displayed".format(self.__baseURL,

                                                                                         self.driver.title))

            else:

                print('URL is not launched successfully')

        if browserType == 'chrome':

            self.driver = webdriver.Chrome()

            self.driver.maximize_window()

            self.driver.get(self.__baseURL)

            if self.driver.title is not None:

                print("URL '{0}' is successfully launched and homepage '{1}' is displayed".format(self.__baseURL,

                                                                                         self.driver.title))

            else:

                print('URL is not launched successfully')

        #return self.driver

# getBytype method will return the by type

    def getBytype(self, locator):

        if locator == 'id':

            return By.ID

        if locator == 'name':

            return By.NAME

        if locator == 'class':

            return By.CLASS_NAME

        if locator == 'link-text':

            return By.LINK_TEXT

        if locator == 'xpath':

            return By.XPATH

        if locator == 'css':

            return By.CSS_SELECTOR

        if locator == 'tag':

            return By.TAG_NAME

    # getElement method will

    def getElement(self, locator, locatortype):

        locator = self.getBytype(locator)

        wait = WebDriverWait(self.driver, 10)

        #element = self.driver.find_element(locator, locatortype)

        try:

            elementCheck = wait.until(EC.element_to_be_clickable((locator, locatortype)))

            if elementCheck is not None:

                print("#"*30)

                print("Element with locators '{0}{1}{2}' found successfully".format(locator, ",", locatortype))

                #elementCheck.click()

                return elementCheck

            else:

                print("#" * 30)

                print("Element '{0}' is not present".format(locator + locatortype))

        except:

            print("Not able to find element")

        #return self.driver

    def driverClose(self):

        self.driver.close()

Similarly, you can create generic methods for your UI to be tested under this base class.             

Page Class

Page class contains actual automated code for your test for the required page, screen.

E.g. There is a page in your web application which deals with alerts and pop-ups. The code below shows how alerts are handled in page class.

Similarly, you can create automation scripts for your various web pages.

from selenium import webdriver

from SampleProject.Base.SeleniumWebdriver import Base as clB

from SampleProject.loggingpackage.custom_logger_new import cust_log as cln

from selenium.webdriver.common.keys import Keys

import logging

import time

class AlertPopup(clB):

    __alt_Obj = clB(driver=webdriver)

    __alt_lObj = cln()

    def testAlertPopup(self, browserType, locator, locatorType, option):

        self.__alt_Obj.launchBrowser(browserType)

        el_txt = self.__alt_Obj.getElement(locator, locatorType)

        if el_txt.get_attribute("type") == 'text':

            el_txt.send_keys("Hello")

            time.sleep(2)

        alt_Btn = self.__alt_Obj.getElement('id','alertbtn')

        if alt_Btn.get_attribute("type") == 'submit':

            alt_Btn.click()

            time.sleep(2)

            alt_btnP = self.__alt_Obj.driver.switch_to.alert

            alt_txt = alt_btnP.text

            self.__alt_lObj.info("Alert pop '{0}' is displayed".format(alt_txt))

            print("Alert pop '{0}' is displayed".format(alt_txt))

            alt_btnP.accept()

            self.__alt_lObj.info("Alter popup {0} is accepted".format(alt_txt))

            time.sleep(2)

        alt_cBtn = self.__alt_Obj.getElement('id','confirmbtn')

        if alt_cBtn.get_attribute("type") == 'submit':

            alt_cBtn.click()

            time.sleep(2)

            alt_cBtnP = self.__alt_Obj.driver.switch_to.alert

            alt_text = alt_cBtnP.text

            self.__alt_lObj.info("Alert popup '{0}' is displayed".format(alt_text))

            print("Alert popup '{0}' is displayed".format(alt_text))

            if option == 'ok':

                alt_cBtnP.accept()

                self.__alt_lObj.info("Alert popup '{0}' is accepted by user".format(alt_text))

            else:

                alt_cBtnP.dismiss()

                self.__alt_lObj.info("Alert popup '{0}' is rejected by user".format(alt_text))

Test Class

Now that you have created a generic method in Base Class and you also have an automation test script for your page in Page Class, let’s create a test method to execute a relative test case.

from selenium import webdriver

from SampleProject.Page.ActionAlertPopups import AlertPopup as Ap

from SampleProject.loggingpackage.custom_logger_new import cust_log as cln

import logging

import unittest

class testAlertPopup(unittest.TestCase):

    def setUp(self) -> None:

        self.__tcls_Obj = Ap(driver=webdriver)

        self.__tcls_lObj = cln()

    def test_alertPopUp(self):

        self.__tcls_Obj.testAlertPopup('chrome','id','name','ok')

    def tearDown(self) -> None:

        self.__tcls_lObj.info('This test "{0}" has successfully been executed'.format(__name__))

The above code is derived using ‘unittest’ i.e. unit testing framework of a Python.

So the test method defined above ‘test_alertPopUps’ inherits an actual code method from page class method ‘testAlertPopup’ using an object ‘__tcls_Obj’ of page class ‘AlertPopup’. So here we have converted our test in a framework in a more simplified manner. We have also made use of ‘logging’ module in python to log a messaged, so here, in this case, we are logging an info message after the successful execution of a test method. 

Similarly, we can create multiple test classes for our test cases and maintain them in the Test Directory.

from selenium import webdriver

from SampleProject.Page.ActionCalenderDatePicker import Calendar as cld

from SampleProject.Page.ActionMouseHover import TestMouseHover as Mh

from SampleProject.loggingpackage.custom_logger_new import cust_log as cln

import time

import unittest

import pytest

class TestCalendar(unittest.TestCase):

    @classmethod

    def setUpClass(cls):

        cls.__flight_locator = 'xpath'

        cls.__flight_locatorType = "//button[@id='tab-flight-tab-hp']"

        cls.__browserType = 'chrome'

        print("Test pre-requisites setup is completed")

    def setUp(self):

        self.__t_cObj = cld(driver=webdriver)

        self.__t_cObj1 = Mh(driver=webdriver)

        self.__t_clObj = cln()

        print('This is onetime setup method which runs before every test method gets executed')

    @pytest.mark.run('second')

    def test_calendarDate(self):

        self.__t_cObj.TestCalendar(self.__browserType, self.__flight_locator, self.__flight_locatorType, 32)

        self.__t_clObj.info("Required date is successfully selected from calendar date picker")

        print('Test method is executed successfully')

    @pytest.mark.run('last')

    def test_assertion(self):

        self.assertEqual('a', 'a', 'This is true')

        print('Assertion test method executed successfully')

    @pytest.mark.run('first')

    def test_practice(self):

        Msg = self.__t_cObj1.TestMouseHover('chrome', 'id', 'mousehover')

        self.assertIsNotNone(Msg, 'Element is not none')

        self.__t_clObj.info('Mouse hover test is completed')

    def tearDown(self):

        print("Test '[{0}]' is completed successfully and driver has been closed".format(__name__))

    @classmethod

    def tearDownClass(cls):

        print("Driver closed successfully")

In the above test script extract, ‘pytest’ framework is used to make it more efficient and effective.

Run the test scripts using pytest and generate html report.

(venv) pravin-a:SampleProject pravin.a$ pytest –html=Result.html

Test Class section

Pytest will look for all the scripts that start with ‘test’ keyword and execute it. As shown above, it will give you the details like what all tests are run and their status.

The HTML report is generated at the current directory location.

UI automation

Also, log files will be generated as per the logging module that we have used in our framework.

Logs contain information about when the tests are run with date and timestamp, from which class, test method logs are generated, type log (info, debug, error, critical, etc.) and the log message.

selenium python

selenium python ui automation

Running tests in TestSuite

‘Unittest’ framework of Python provides great features and methods to collect multiple test methods/cases from different-different test classes and bundle them together in a test suite. Basically, we are collecting test methods from various classes and bundling them into a test suite as shown below.

from SampleProject.tests.test_actionRadioBtn import test_Action as TC1

from SampleProject.tests.test_ActionsMultiSelect import testMultSelect as TC2

from SampleProject.tests.test_checkBox import TestCheckBox as TcChB

from SampleProject.tests.test_actionSwitchWindow import Test_SWClass as tcSW

import unittest

# Load tests from 'test_Action' class

tc1 = unittest.TestLoader().loadTestsFromTestCase(TC1)

# Load tests from 'testMultSelect' class

tc2 = unittest.TestLoader().loadTestsFromTestCase(TC2)

tc_cb = unittest.TestLoader().loadTestsFromTestCase(TcChB)

tc_SW = unittest.TestLoader().loadTestsFromTestCase(tcSW)

# Create Test suite 'suite' and load tests from test classes

suite = unittest.TestSuite()

suite.addTests([tc1,tc2])

unittest.TextTestRunner(verbosity=2).run(suite)

# Create Test Suite 'suite1' and load tests from test classes 

suite1 = unittest.TestSuite()

suite1.addTest(tc_SW)

unittest.TextTestRunner(verbosity=2).run(suite1)

Behavior Driven Development (BDD)

The most effective and stable framework for BDD in Python is ‘behave’.

You just need to install behave using pip3 (If you are using Python3)

--> pip3 install behave

As stated above in point #9 in ‘How to build an automation framework’ section, BDD related stuff i.e. feature file, steps declaration, and actual code files are typically organized in below hierarchy.

Behavior Driven Development

Feature file : login.feature

feature file will contain the features you want to test e.g. in this case feature is ‘Verify login functionality’ or it could be just ‘login’

Scenario: Scenarios pertaining to feature Login e.g. login with valid credentials, login with invalid credentials.

@given –> This is nothing but a pre-requisite to run your test.

@When –> Any action performed on UI.

@Then –> End result to verify after any action is performed.

Feature: Verify login functionality

Scenario: Login with invalid credentials

  • Given the user is on the login page
  • When User enters an invalid username
  • And User enters an invalid password
  • And clicks on the login button
  • Then login is unsuccessful

Scenario: Verify hide and show button functionality

  •     Given User is on practice home page
  •     When the user clicks on the Hide button
  •     Then the text box will be hidden
  •     When the user clicks on the Show button
  •     Then the text box will be visible

Scenario: Verify mouse hover functionality

  •     Given User is on Practice home page of letskodeit
  •     When User mouse hover an element
  •     Then mouse hover action is completed

Steps Declaration

Steps declaration: steps.py

from behave import *

from BDD.login import TestDriver

from BDD.HideShow import testHideShow

from BDD.MouseHover import MouseHover

# This is a pre-requisite to access login page

@given('The user is on login page')

def step_impl(context):

    context.driver = TestDriver()

    context.driver.test_launchBrowser('chrome')
# This is a pre-requisite where user enters username

@when('User enters invalid username')

def step_impl(context):

    context.driver.test_uname('letskodeit@gmail.com')

# This is a pre-requisite where user enters password

@when('User enters invalid password')

def step_impl(context):

    context.driver.test_pwd('admin')

# This is a pre-requisite where user clicks on login button

@when('clicks on login button')

def step_impl(context):

    context.driver.test_click()

@then('Login is unsuccessful')

def step_impl(context):

    context.driver.test_unsuccessful()

#This is another scenario

@given('User is on practice home page')

def step_impl(context):

    context.obj = testHideShow()

    context.obj.test_HomePage()

@when('user clicks on Hide button')

def step_impl(context):

    context.obj.test_ActHide()

@then('text box will be hidden')

def step_impl(context):

    context.obj.test_HideBtn()

@when('user clicks on Show button')

def step_impl(context):

    context.obj.test_ActShow()

@then('text box will be visible')

def step_impl(context):

    context.obj.test_ShowBtn()

# ****** This is mouse hove scenario ******

@given('User is on Practice home page of letskodeit')

def step_impl(context):

    context.driver = MouseHover()

    context.driver.test_homePage()

@when('User mouse hover an element')

def step_impl(context):

    context.driver.test_mouseHover()

@then('mouse hover action is completed')

def step_impl(context):

    context.driver.test_complete()

Step declaration above contains a method that is nothing but an implementation of your actual test script for each statement defined in your feature file.

E.g. 

@given(‘The user is on login page’)

def step_impl(context):

    context.driver = TestDriver()

    context.driver.test_launchBrowser(‘chrome’)

Code above shows for @given statement from feature file ‘The user is on login page’, test method ‘test_launchBrowser’ is defined in step_impl method here which has all the code required to launch a browser shown below.

Method ‘test_launchBrowser’:

def test_launchBrowser(self, browserType):

        if browserType==‘chrome’:

            self.driver.maximize_window()

            self.driver.get(‘url’)

Note: Just to show you how the actual test method looks like, I have not copied the actual URL above and just referred it as ‘url’ in self.driver.get(‘url’). You need to pass actual url (https://*****.com)

Above code can be run using behave command along with the path as an argument where you feature file resides

(venv) pravin-a:Selenium_Projects pravin.a$ behave BDD/features/

Selenium_Projects

(venv) pravin-a:Selenium_Projects pravin.a$ behave BDD/features/

Feature: Verify login functionality # BDD/features/login.feature:1

Scenario: Login with invalid credentials  # BDD/features/login.feature:2

    Given The user is on login page   # BDD/features/steps/steps.py:7 5.906s

    When User enters an invalid username  # BDD/features/steps/steps.py:13 2.188s

    And User enters an invalid password   # BDD/features/steps/steps.py:18 2.076s

      And clicks on login button    # BDD/features/steps/steps.py:23 3.123s

     Then Login is unsuccessful     # BDD/features/steps/steps.py:27 0.163s

 Scenario: Verify hide and show button functionality  # BDD/features/login.feature:9

    Given User is on practice home page   # BDD/features/steps/steps.py:32 6.472s

    When user clicks on Hide button       # BDD/features/steps/steps.py:37 4.422s

    Then text box will be hidden          # BDD/features/steps/steps.py:41 0.000s

    When user clicks on Show button       # BDD/features/steps/steps.py:45 0.465s

    Then text box will be visible         # BDD/features/steps/steps.py:49 0.000s

Scenario: Verify mouse hover functionality      # BDD/features/login.feature:16

  •     Given User is on Practice home page of letskodeit # BDD/features/steps/steps.py:54 0.001s
  •     When User mouse hover an element      # BDD/features/steps/steps.py:59 8.105s
  •     Then mouse hover action is completed  # BDD/features/steps/steps.py:63 0.000s

1 feature passed, 0 failed, 0 skipped

3 scenarios passed, 0 failed, 0 skipped

13 steps passed, 0 failed, 0 skipped, 0 undefined

Took 0m32.920s

(venv) pravin-a:Selenium_Projects pravin.a$ 

As you can see above it will give you all the information after once all the scenarios are executed successfully.

E.g. How many features are executed/passed, how many scenarios in total are executed and passed/failed/skipped and the total no. of steps executed/passed/skipped.

You can also generate a report called ‘allure’ report for your test run using below command

(venv) pravin-a:Selenium_Projects pravin.a$ behave -f allure_behave.formatter:AllureFormatter -o %allure_result_folder% BDD/features

AllureFormatter

View the generated allure-report

generated allure-report

Putting it all together. 

You can make use of this hybrid automation test framework to automate UI’s of your application and you can customize this framework as per your need.

0 comments

Leave a Reply