Python Selenium | How can I remove these repetitive code every time I add let's say a new "item"

Question:

I’m designing a Automated Acceptance Test using Python Selenium to fill in web form and check for the errors that are shown on the webpage then compared to my expected results to give me some sort of "Success" and "Fail" type of result.

Currently, I am testing on a website with only 3 fields.

It only requires IC, Postcode and Vehicle Registration Number. However, let’s say if there was another field "Name". I’d have to add these repetitive code into my code

# Enter Name
input_Name = driver.find_element(By.ID, "name")
input_Name.send_keys(entry['Name'])
time.sleep(1)

and to test the "error". It varies between testing if the button is working or if the element is visible, etc.

So, how can I do this without having to repeat this process over and over, every time a new field is added? I was thinking of adding the fields into an excel sheet and use a for loop on it but I can’t really figure out where to place it in my code.

Here is my code:

import datetime
import os.path
import time

import numpy as np
import openpyxl
import pandas as pd
import selenium
from selenium import webdriver
from selenium.common.exceptions import (ElementClickInterceptedException,
                                        ElementNotInteractableException,
                                        NoSuchElementException)
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

ProjectName = 'Automated UAT'

# Excel Path
ExcelPath = 'Excel Path'
# Convert Excel Data Type
ExcelConverters = {'IC':str, 'Postcode':str, 'VehRegNum':str}

# Web Form url
url = 'url'

# Set ChromeDriver path
DriverPath = 'Driver Path'
driver = webdriver.Chrome(executable_path=DriverPath)

# Empty Result Data Frame
result_df = pd.DataFrame(columns = ['e1', 'e2', 'e3', 'e4'])



try:
    try:
        df1 = pd.read_excel (ExcelPath, header = 0, converters = ExcelConverters)
        df = df1.replace(np.nan, '', regex= True)
        data = df.iloc[:,0]
        time.sleep(1)

        wb = openpyxl.load_workbook(ExcelPath)
        ws = wb.worksheets[0]
        maxrow = ws.max_row

        # for loop
        for i in range(0, maxrow+1):
            print("attempt row " + str(i))

            # Get Website
            driver.get(url)

            # Maximize Window
            driver.maximize_window()

            entry = df.loc[i]

            # Enter IC Number
            input_IC = driver.find_element(By.ID, "icno")
            input_IC.send_keys(entry['IC'])
            time.sleep(1)

            # Enter Postcode
            input_Postcode = driver.find_element(By.ID, "postcode")
            input_Postcode.send_keys(entry['Postcode'])
            time.sleep(1)

            # Enter Vehicle Registration Number
            input_VehRegNum = driver.find_element(By.ID, "registerno")
            input_VehRegNum.send_keys(entry['VehRegNum'])
            time.sleep(1)

            # Click on Button
            button = driver.find_element(By.NAME, 'btnGenQuote')
            button.click()
            time.sleep(5)

            # Error Messages/ Results and Locating errors
            ### Problem: Error must be in order for it to work ###
            try:
                try:
                    try:
                        try:

                            IC_error1 = driver.find_element(By.XPATH, "//*[contains(text(), 'IC number is mandatory')]")
                            e1 = ["T"]
                            print("IC_error1 found")

                        except NoSuchElementException:

                            e1 = ["F"]
                            print("IC_error1 not found")

                        IC_error2 = driver.find_element(By.XPATH, "//*[contains(text(), 'Invalid IC format')]")
                        e2 = ["T"]
                        print("IC_error2 found")

                    except NoSuchElementException:

                        e2 = ["F"]
                        print("IC_error2 not found")

                    Postcode_error = driver.find_element(By.XPATH, "//*[contains(text(), 'Postcode number is mandatory')]")
                    e3 = ["T"]
                    print("Postcode_error found")

                except NoSuchElementException:

                    e3 = ["F"]
                    print("Postcode_error not found")

                RegNo_error = driver.find_element(By.XPATH, "//*[contains(text(), 'Vehicle Registration Number is mandatory')]")
                e4 = ["T"]
                print("RegNo_error found")

            except NoSuchElementException:

                e4 = ["F"]
                print("RegNo_error not found")
                
            time.sleep(3)

 

            new_result = pd.DataFrame(zip(e1,e2,e3,e4), columns = ['e1', 'e2', 'e3', 'e4'])
            result_df = pd.concat([result_df, new_result])
            result_df.reset_index(drop=True, inplace=True)
            print(result_df)

 

            # For screenshot
            # timestamp = datetime.datetime.now().strftime('%d%m%Y_%H%M%S')
            # driver.get_screenshot_as_file(ProjectName + '_' + timestamp + '.png')

            driver.get_screenshot_as_file(ProjectName + '_' + str(i) + '.png')
            time.sleep(2)

    except KeyError:
        print("If the number of attempts is lesser than the number of data rows, it could be caused by the Excel Column Row don't match with the code.")
        print("Process is COMPLETED")
        result_df.to_excel('Result.xlsx', sheet_name= 'result') # Ensure "Result" workbook is closed to update
        pass # to skip KeyError when there isn't any data afterwards


finally:
    driver.quit()

If you have any ideas on how to enhance my code even more. I’m open to suggestions :")

Asked By: Taetoro

||

Answers:

You can put it into a function to remove the repeating code. The function can have two parameters.

  1. An enum coresponding to the search type (i.e if you want to search element by name or by id)

  2. The search value

Example:

import enum
class idOrName(enum.Enum):
    id: Id
    name: Name

def findAndSendKey(searchType, searchString, entryValue):
    element = ''
    if searchType == idOrName.id:
        element = driver.find_element(By.ID, searchString)
    if searchType == idOrName.name:
        element = driver.find_element(By.NAME, searchString)

    element.send_keys(entry[entryValue])
    time.sleep(1)

Now you can run it as:

findAndSendKey(idOrName.id, "registerno", "VehRegNum")

And call this function where ever required

Answered By: KingKong BigBong