web automation

Web Automation with Selenium & Python Part 2: [Homepage Automation]

Today we are going to do real work as I promised. We will build a project step by step which will do the automation testing in our target application which is a quiz application. Where admin can log in and create/update/delete categories, subcategories questions, options. Nice and simple right?
Today we are going to do real work as I promised. We will build a project step by step which will do the automation testing in our target application which is a quiz application. Where admin can log in and create/update/delete categories, subcategories questions, options. Nice and simple right?

In our last episode, we set up our virtual environment and installed the required packages. Today we are going to do real work as I promised. We will build a project step by step which will do the automation testing in our target application which is a quiz application. Where admin can log in and create/update/delete categories, subcategories questions, options. Nice and simple right? Well yes, and we are going to automate the testing for this application. Let’s see our modules first to understand what we are really doing.

  • Homepage
  • Login
  • Category
  • Subcategory
  • Question
  • Option

These are the main modules of the application which we will test and eventually we will add more modules in our list.

The Automation Testing Application Structure

As we will be using selenium with python, we need to think about our application structure. Of course, we will be using the modular pattern. And one thing I want you all to keep in mind is, writing bad code is a sin, and making your project structure messy is also a sin. If I do that sin, please punish me any way you like. I will respectfully accept that. Always be careful with that. And today we will test the homepage of that quiz application. So for that our automation testing application’s folder and file structure will be like this:

.
├── application
│   ├── app.py
│   ├── __init__.py
│   └── utils
│       ├── constants.py
│       ├── __init__.py
│       └── urls.py
├── homepage
│   ├── homepage.py
│   ├── __init__.py
├── README.md
├── requirements.txt
└── settings.py
└── .env

Application package: This is the core module of our application. You see that we have a sub-package in our application package which is utils. We will use it for string constants and URLs and helping function later.

homepage package:  This is where our homepage automation code will be.

settings.py: We will keep our application settings here.

.env: env is for user credential or any kind of secret data of our application.

web automation

Let’s Start Automating the Homepage

First of all, on our homepage most of the cases there will be a navbar, Some sections like about-us and many more. Our target is to test the homepage according to our testing requirements. So let’s meet our requirements first:

  1. Visit the homepage of the quiz application if the main URL is working or not.
  2. Check the navbar items if it contains all the elements. In our case, they are Home, About Us, Feature, How It Works, and Sign In.
  3. Check if navbar items are clickable on fullscreen and they are taking us on specific sections.
  4. Check if navbar items are clickable on the custom window size like mobile and they are taking us on specific sections.

[ Step:01 ] Add Constants And URLs In Our Application

In our application/utils/urls.py file let’s add the homepage URL.

HOMEPAGE_URL = "https://querkiz.itech-theme.com/"

Now let’s add these constants in our application/utils/constants.py file.

HOMEPAGE_NAV_BAR = ["Home", "About Us", "Feature", "How It Works", "Sign In"]

FULL_WINDOW_NAV_ITEMS_X_PATH = [
    '//*[@id="nav_menu"]/li[2]/a',
    '//*[@id="nav_menu"]/li[3]/a',
    '//*[@id="nav_menu"]/li[4]/a',
    '//*[@id="scrolltop"]/a'
]

MOBILE_MENU_NAV_BAR_X_PATH = '//*[@id="scrolltop"]/header/div/div/div/div[' \
                               '3]/div/div/a '

MOBILE_SCREEN_NAV_ITEMS_X_PATH = [
    '//*[@id="scrolltop"]/header/div/div/div/div[3]/div/div/nav/ul/li[2]/a',
    '//*[@id="scrolltop"]/header/div/div/div/div[3]/div/div/nav/ul/li[3]/a',
    '//*[@id="scrolltop"]/header/div/div/div/div[3]/div/div/nav/ul/li[4]/a',
    '//*[@id="scrolltop"]/a'
]

Now we understand that HOMEPAGE_NAV_BAR is an array of navbar element’s names. But what is FULL_WINDOW_NAV_ITEMS_X_PATH? What is X Path first of all? Well, the Full form of X Path is the XML path. And we can find any element from a webpage using it. Now let’s explain the constants.

FULL_WINDOW_NAV_ITEMS_X_PATH is an array of XPaths of navbar elements when the window size is large.
MOBILE_MENU_NAV_BAR_X_PATH is the XPath of the navbar menu when the window size is small.
MOBILE_SCREEN_NAV_ITEMS_X_PATH is an array of XPaths of navbar elements when the window size is small.

A demo of finding XPath is in the below video so that you can get XPath easily.

 

[ Step: 02 ] Install Packages For Further Development

Before installing packages, let’s activate our virtual environment first. Run this below command from the virtual environment directory.

. bin/activate

Now run these below commands to install our project dependencies.

pip install python-dotenv

pip install termcolor

And I’m sure you have installed python selenium package from our previous episode.

[ Step: 03 ] Developing Homepage Package

Write those below codes in our homepage/homepage.py file.

from time import sleep
from selenium import webdriver
from termcolor import colored
from application.utils.constants import FULL_WINDOW_NAV_ITEMS_X_PATH, \
    MOBILE_SCREEN_NAV_ITEMS_X_PATH, MOBILE_MENU_NAV_BAR_X_PATH


class HomepageTest:
    """
        Automate homepage visiting process
    """

    def __init__(self, url: str, nav_bar_elements: list):
        """
        :param url: url of the web application
        :param nav_bar_elements: nav bar elements of the web application
        """
        self._browser = webdriver.Chrome()
        self._browser.maximize_window()
        self.url = url
        self.nav_bar_elements = nav_bar_elements

    def visit_homepage(self) -> bool:
        """
        Visit the web application's given homepage url
        :return: bool
        """
        try:
            self._browser.get(self.url)
            console_message = colored('[success] ', 'green',
                                      attrs=['bold']) + colored(
                '[Homepage OK!]', 'cyan')
            print(console_message)
            sleep(5)

            return True
        except Exception as e:
            console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                              colored('Homepage not OK', 'cyan')
            print(console_message)
            print(colored(str(e), 'red'))
            return False

    def nav_bar_content_testing(self) -> bool:
        """
        Test nav bar has all desired elements
        :return: bool
        """
        try:
            nav_menu = self._browser.find_element_by_id("nav_menu")
            nav_list = nav_menu.find_elements_by_tag_name('li')
            nav_element_matched = self.compare_nav_elements(nav_list)

            console_message = colored('[success] ', 'green',
                                      attrs=['bold']) + colored(
                '[Nav bar OK!]', 'cyan')
            print(console_message)

            return nav_element_matched
        except Exception as e:
            console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                              colored('Nav bar is not OK!', 'cyan')
            print(console_message)
            print(colored(str(e), 'red'))
            self.close_browser()

            return False

    def compare_nav_elements(self, nav_list) -> bool:
        """
        Compare given nav list and web page nav list
        :param nav_list: Fetched from web page
        :return: bool
        """
        nav_elements_to_check = []
        for li in nav_list:
            nav_elements_to_check.append(li.text)

        nav_has_all_menu = all(element in self.nav_bar_elements
                               for element in nav_elements_to_check)

        return nav_has_all_menu

    def click_nav_elements_on_fullscreen(self) -> bool:
        """
        This will test nav bar element by clicking on their link while window
        size is in full screen
        :return: bool
        """
        try:
            for element in FULL_WINDOW_NAV_ITEMS_X_PATH:
                self.click_on_element(element)
            sleep(2)
            console_message = colored('[success] ', 'green',
                                      attrs=['bold']) + colored(
                '[Nav elements link OK!]', 'cyan')
            print(console_message)

            return True
        except Exception as e:
            console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                              colored('Nav elements link is not OK!', 'cyan')
            print(console_message)
            print(colored(str(e), 'red'))
            self.close_browser()

            return False

    def click_nav_elements_on_mobile_screen(self) -> bool:
        """
        This will test nav bar element by clicking on their link after
        decreasing window size
        :return: bool
        """
        try:
            self.set_window_screen()
            self.click_on_element(MOBILE_MENU_NAV_BAR_X_PATH)
            for element in MOBILE_SCREEN_NAV_ITEMS_X_PATH:
                self.click_on_element(element)
            console_message = colored('[success] ', 'green',
                                      attrs=['bold']) + colored(
                '[Nav elements link on full screen OK!]', 'cyan')
            print(console_message)

            return True
        except Exception as e:
            console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                              colored('Nav elements link on mobile screen is '
                                      'not OK!', 'cyan')
            print(console_message)
            print(colored(str(e), 'red'))
            self.close_browser()

            return False

    def click_on_element(self, x_path: str):
        """
        Will click on given x_path of html element
        :param x_path: x_path of html element
        """
        element = self._browser.find_element_by_xpath(x_path)
        element.click()
        sleep(1)

    def set_window_screen(self) -> bool:
        """
        This method is resize the browser window
        :return: bool
        """
        try:
            sleep(2)
            self._browser.set_window_size(400, 862)
            console_message = colored('[success] ', 'green',
                                      attrs=['bold']) + colored(
                '[Custom window size OK!]', 'cyan')
            print(console_message)

            return True
        except Exception as e:
            console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                              colored('Custom window size is not OK!', 'cyan')
            print(console_message)
            print(colored(str(e), 'red'))
            self.close_browser()

            return False

    def close_browser(self):
        """
        Close the browser
        """
        self._browser.quit()

 

[ Step: 04 ] Code Breakdown & Explanation

Lets breakdown all the methods and codes to understand what has been done and why we will extend our code base with more features. We have used try-except to handle any unwanted exceptions. Always remember to handle exceptions.

 

Initialize the attributes of HomepageTest class

In our __init__  method we are initializing url and nav_bar_elements. Also, we have initialized our web driver as _browser. Let’s break more,

self._browser = webdriver.Chrome()

This is how we will initiate our web driver. And we used the Chrome web driver.

Visit Homepage

Our visit_homepage() method is responsible for visiting the given url. We will hit the homepage URL.

self._browser.get(self.url)

Print the message with style.

console_message = colored('[success] ', 'green',
                          attrs=['bold']) + colored(
    '[Homepage OK!]', 'cyan')
print(console_message)

Wait for 5 seconds.

sleep(5)

 

Test Navbar Elements

Our nav_bar_content_testing() method is responsible for comparing the navbar elements with the given navbar elements. First, select the main navbar.

nav_menu = self._browser.find_element_by_id("nav_menu")

find_element_by_id() will find the element in HTML DOM with given ID. Now let’s get all the list items from nav_menu.

nav_list = nav_menu.find_elements_by_tag_name('li')

find_elements_by_tag_name() will find all the HTML elements with the given tag name. Now check if the nav_list texts match with given navbar items from the constants.

nav_element_matched = self.compare_nav_elements(nav_list)

compare_nav_elements() is our class method witch will compare two lists and returns boolean. Again, print a nice message.

console_message = colored('[success] ', 'green',
                          attrs=['bold']) + colored(
    '[Nav bar OK!]', 'cyan')
print(console_message)

If things went wrong then print error message in except block with color like this.

console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                  colored('Nav bar is not OK!', 'cyan')
print(console_message)
print(colored(str(e), 'red'))

Close the browser and return False.

self.close_browser()

return False

close_browser() is our class method and inside it, we closed our browser with the below method.

self._browser.quit()

Click Navbar Elements On Fullscreen

Our click_nav_elements_on_fullscreen() is responsible for clicking the nav elements to test if they are working well on fullscreen. We made another method click_on_element() to follow DRY(Don’t repeat yourself) principle. This method will be only responsible for clicking on the element for the given x_path.

Iterated throw FULL_WINDOW_NAV_ITEMS_X_PATH and clicked on the nav items.

for element in FULL_WINDOW_NAV_ITEMS_X_PATH:
    self.click_on_element(element)

Wait for 2 seconds.

sleep(2)

Print nice color message and return True.

console_message = colored('[success] ', 'green',
                          attrs=['bold']) + colored(
    '[Nav elements link OK!]', 'cyan')
print(console_message)

return True

If anything went wrong then print the colored error messages, close the browser and return False.

console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                  colored('Nav elements link is not OK!', 'cyan')
print(console_message)
print(colored(str(e), 'red'))
self.close_browser()

return False

 

Resize Window

The set_window_screen() will simply resize the window size. Resize the window with 400, 862 widths and height but before that wait for 2 seconds.

sleep(2)
self._browser.set_window_size(768, 1024)

Print a nice colored message and return True

console_message = colored('[success] ', 'green',
                          attrs=['bold']) + colored(
    '[Custom window size OK!]', 'cyan')
print(console_message)

return True

If anything goes wrong then print the colored error message, close browser, and return False.

console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                  colored('Custom window size is not OK!', 'cyan')
print(console_message)
print(colored(str(e), 'red'))
self.close_browser()

return False

 

Click Navbar Elements On Mobile Screen

The click_nav_elements_on_mobile_screen() method is responsible for operating the same navbar element testing but only in small screen size. First, invoke the window resize method.

self.set_window_screen()

Click the navbar menu with the XPath

self.click_on_element(MOBILE_MENU_NAV_BAR_X_PATH)

And iterate through MOBILE_SCREEN_NAV_ITEMS_X_PATH and click every element.

for element in MOBILE_SCREEN_NAV_ITEMS_X_PATH:
    self.click_on_element(element)

Print the nice colored message and return True

console_message = colored('[success] ', 'green',
                          attrs=['bold']) + colored(
    '[Nav elements link on full screen OK!]', 'cyan')
print(console_message)

return True

If anything goes wrong then print a colored error message, close the browser and return False.

console_message = colored('[failed] ', 'red', attrs=['bold']) + \
                  colored('Nav elements link on mobile screen is '
                          'not OK!', 'cyan')
print(console_message)
print(colored(str(e), 'red'))
self.close_browser()

return False

[ Step:05 ] It’s Time, Run The Automation

In our core package application, we will run the homepage automation testing. Write the below codes in the application/app.py file.

 

from homepage.homepage import HomepageTest
from application.utils.urls import HOMEPAGE_URL
from application.utils.constants import HOMEPAGE_NAV_BAR


if __name__ == '__main__':
    homepage = HomepageTest(HOMEPAGE_URL, HOMEPAGE_NAV_BAR)
    homepage.visit_homepage()
    homepage.nav_bar_content_testing()
    homepage.click_nav_elements_on_fullscreen()
    homepage.click_nav_elements_on_mobile_screen()
    homepage.close_browser()

 

Explanation

  • Import all the things we need.
    from homepage.homepage import HomepageTest
    from application.utils.urls import HOMEPAGE_URL
    from application.utils.constants import HOMEPAGE_NAV_BAR
  • Create an object of the HomepageTest class.
    homepage = HomepageTest(HOMEPAGE_URL, HOMEPAGE_NAV_BAR)
  • Run the automation testing methods of the HomepageTest class, step by step.
    homepage.visit_homepage()
    
    homepage.nav_bar_content_testing()
    
    homepage.click_nav_elements_on_fullscreen()
    
    homepage.click_nav_elements_on_mobile_screen()
  • Finally, close the browser.
    homepage.close_browser()

 

Demo Of Successful Automation Testing

 

Conclusion

Congratulation, you have done well. Want some more in your project? Well, why don’t you add some more requirements? You can check the headings of all sections. We have basically demonstrated click events and fetching information from the HTML DOM. We will automate the boring login in our next episode. Where our tool automatically types in input fields. Sounds exciting right? I will keep my promise. Till then practice what you have learned, stay at home. Feel free to share this article and ask anything you like. Stay tuned!

You will find all the codes in my Github repository.

 

Farhat Shahir
Passionate backend developer with 2 years of experience. Worked with backend technologies like PHP and Python stack mostly. Has good experience in managing git. Proficient with database design and query language.