Danny Brown

A Blog on Code and Occasionally Other Things

Filling Out Input Elements on a Web Form with One Method Call Using Python and Selenium

Danny BrownSeptember 18, 2019

I’m constantly needing to fill out forms when unit testing, so I need to make this process as simple as possible. Today I’ll cover a simple method that allows you to fill out all text inputs on a page just by sending a Python dictionary. This is an expansion on my last post, but today I’ll be including the needed import statements and the class this method is contained in, as well as a method to start the Chrome driver with options specified by the user.

Lots to cover, so let’s get to it! Here’s the full code we’ll be going over today:

from selenium import webdriver
from selenium.common.exceptions import *

class SeleniumHelper(object):

    """ Method to start the Chrome driver with specified helpful options """ 
    def start_chrome_driver(self): 
        self.options = webdriver.ChromeOptions() 
        self.options.add_argument("start-maximized") 
        self.options.add_argument("disable-infobars") 
        self.options.add_argument("--disable-extensions") 
        self.driver = webdriver.Chrome( 
            options=self.options, 
            executable_path=CHROME_PATH ) 
        self.driver.implicitly_wait(1)
 
    """ Method accepts a dictionary of any size with keys equal to partial
        but still unique IDs or an xpath and values equal to desired text to be
        inserted in the specified input and textbox elements """
    def set_input_elements(self, data):
        for key, value in data.items():
            # Select each input and textarea element
            try: # partial but unique ids are easiest
                element = self.driver.find_element_by_xpath(
                    f"//*[contains(@id, '{key}')]"
                )
                element.send_keys(value)
            except NoSuchElementException:
                try: # xpaths are allowed for inputs without id attributes
                    element = self.driver.find_element_by_xpath(key)
                    element.send_keys(value)
                except NoSuchElementException:
                    print(f"Element matching {key} can't be found!")

    def click_button(self, identifier):
        See code here

Starting the Web Driver

Before we can do anything, we need to start a web driver that Selenium can control. I prefer the ChromeDriver and have it downloaded locally. A variable called CHROME_PATH points to its local path, which for me is a directory below the one where my script is called:

import os
CHROME_PATH = os.getcwd() + '\\..\\chromedriver_win32\\chromedriver.exe'

os.getcwd() is a neat little function that returns a string of the path of your current working directory.

Here’s the method itself:

    """ Method to start the Chrome driver with specified helpful options """ 
    def start_chrome_driver(self): 
        self.options = webdriver.ChromeOptions() 
        self.options.add_argument("start-maximized") 
        self.options.add_argument("--disable-extensions") 
        self.driver = webdriver.Chrome( 
            options=self.options, 
            executable_path=CHROME_PATH 
         ) 
        self.driver.implicitly_wait(1)

Before we start the driver, we first instantiate an object from the webdriver.ChromeOptions class, where we add two arguments:

  1. "start-maximized": This starts the driver with a maximized window, which would otherwise not be the case.
  2. "--disable-extensions": This disables extensions the user may have installed on their version of Chrome, which could interfere with the testing process.

Once these arguments are added, we open the web driver with the specified options.

    self.driver = webdriver.Chrome( 
        options=self.options, 
        executable_path=CHROME_PATH 
    ) 

Finally, we add a self.driver.implicitly_wait(1). This tells the driver to wait for one second before throwing an error. The amount of time can be increased, but in my testing, most of the time an error cannot be avoided by waiting for more time – and when it does happen, an explicit wait is simple enough to add. If an error is going to be thrown, I generally want to know about it pretty quickly.

Filling Out Text Input Elements

This is a fairly simple method that uses a lot of the same techniques covered in the click_button() method from my last post.

    """ Method accepts a dictionary of any size with keys equal to partial
        but still unique IDs or an xpath and values equal to desired text to be
        inserted in the specified input and textbox elements """
    def set_input_elements(self, data):
        for key, value in data.items():
            # Select each input and textarea element
            try: # partial but unique ids are easiest
                element = self.driver.find_element_by_xpath(
                    f"//*[contains(@id, '{key}')]"
                )
                element.send_keys(value)
            except NoSuchElementException:
                try: # xpaths are allowed for inputs without id attributes
                    element = self.driver.find_element_by_xpath(key)
                    element.send_keys(value)
                except NoSuchElementException:
                    print(f"Element matching {key} can't be found!")

It accepts one argument, data, which must be a dictionary, as the first thing we are doing is iterating through the keys and values of each of the items in the dictionary:

    def set_input_elements(self, data):
        for key, value in data.items():

Like last time, we’re looking for an input element by a partial but still unique ID:

            try: # partial but unique ids are easiest
                element = self.driver.find_element_by_xpath(
                    f"//*[contains(@id, '{key}')]"
                )
                element.send_keys(value)

If that’s unsuccessful, we can try by XPath. I’ve found that most web forms have good IDs to target, but sometimes XPath is still necessary. I haven’t found CSS selectors uniquely useful to be necessary for this method (yet), so I haven’t added them here, although one certainly could.

            except NoSuchElementException:
                try: # xpaths are allowed for inputs without id attributes
                    element = self.driver.find_element_by_xpath(key)
                    element.send_keys(value)

Each of these try clauses ends with element.send_keys(value). Don’t make the mistake I did of adding this just once outside of these clauses. This can have the unintended consequence of appending text to a previous input element when the next input element can’t be found by its identifier.

If an input element cannot be found by either partial but unique ID or XPath, a message is printed to let the user know: print(f"Element matching {key} can't be found!"). This does not break the loop, however, allowing one to include items in the dictionary that may or may not appear on the form in question.

Simple Example Usage

Save this script in a file named selenium_helper.py, then in a Python shell enter the following:

from selenium_helper import SeleniumHelper()
sh = SeleniumHelper()
sh.start_chrome_driver()

This pulls up a blank Chrome shell that looks like this:

Next, we enter:

sh.driver.get("http://www.google.com")

Which pulls up a familiar looking website:

Inspecting the source code of Google’s home page, I’m actually a little surprised there is no id attribute for its searchbox:

That means we’ll need to send a valid XPath, which shouldn’t be too hard. Let’s type the following into our Python shell:

sh.set_input_elements({"//input[@name='q']" : "hello world"})

Here, we’re calling our set_input_elements with a dictionary targeting an input element that has a name attribute equal to “q”, then sending the text “hello world” to that element. When we press enter in our shell, like magic, that exact thing happens!

Finally, we just need to click the search button using the method we defined last time. We’ll likewise be using an XPath here, as Google hasn’t provided an id attribute to use:

sh.click_button("//input[@value='Google Search']")

And we have our results! We’ll continue expanding this class with more methods in the future.

Posted In code | Python | Selenium
Tagged unit testing

Post navigation

PreviousWriting an Element Clicker with Python and Selenium That Takes ID, CSS Selector, or XPath
NextPrepping for the Python MTA Certification (Exam 98-381): Data Types and Operators

Danny Brown

A Dev Blog with Some Tangents

About

Categories

  • code
    • APIs
    • Bash
    • CSS
    • Django
    • HTML
    • JavaScript
    • Python
    • S3
    • Selenium
    • Serverless
    • TypeScript
  • games
  • music
    • concert reviews
    • synthesizers
  • opinion
  • sports
  • tech
    • Bitbucket
    • Git
    • GitHub
    • MS Teams
    • WordPress
  • theater

Recent Posts

  • Open Pull Requests from the Terminal (One of My Favorite Dotfiles Scripts)
  • Dotfiles Script for a New TypeScript/Node Project
  • So I Told You to Go See a Broadway Play? Tips for Theater in New York
  • Build a Simple Microsoft Teams Bot Easily, No SDK Required
  • Creating a GUI for Conway’s Game of Life Using Pygame and Numpy

External Links

  • GitHub
  • LinkedIn

Recent Posts

  • Open Pull Requests from the Terminal (One of My Favorite Dotfiles Scripts)
  • Dotfiles Script for a New TypeScript/Node Project
  • So I Told You to Go See a Broadway Play? Tips for Theater in New York
  • Build a Simple Microsoft Teams Bot Easily, No SDK Required
  • Creating a GUI for Conway’s Game of Life Using Pygame and Numpy

Categories

  • code
    • APIs
    • Bash
    • CSS
    • Django
    • HTML
    • JavaScript
    • Python
    • S3
    • Selenium
    • Serverless
    • TypeScript
  • games
  • music
    • concert reviews
    • synthesizers
  • opinion
  • sports
  • tech
    • Bitbucket
    • Git
    • GitHub
    • MS Teams
    • WordPress
  • theater
Copyright © 2025. Danny Brown
Powered By WordPress and Meritorious