How To Handle Dropdowns In Selenium WebDriver Using Python?
While dealing with access forms, you'd often have to handle dropdown. Here's how you can handle them with Selenium WebDriver in Selenium using the Select class.
Join the DZone community and get the full member experience.
Join For FreeDropdowns are an integral part of modern websites. And like any other HTML element, these need to be tested as well, especially when you are performing automated browser testing. UI/UX designers love dropdown elements, but it’s the automation testing engineers who get to play with it. Therefore, it becomes important to know how to handle dropdowns in Selenium WebDriver when you are handling access forms or testing websites.
Designers prefer using aesthetically appealing dropdown menus or boxes. Mostly because dropdowns tend to utilize the available screen space frugally. It is useful when you want only specific inputs from users and not some crap data. However, dropdown design bloopers could be a serious turn-off for users.
I have come across websites with poorly implemented navigational dropdown menus, where I had no way to exit the menu without clicking on an option. It’s an erroneously irritating experience! In mobile browsers, it gets even trickier, where ideally, dropdown options have to be optimized for the thumb interface. To ensure a seamless user experience across the application or website, test automation engineers are often required to investigate any possible flaw with the dropdowns thoroughly.
This insight aims to be your go-to destination for dealing with how to handle dropdowns in Selenium WebDriver using Python. You may consider bookmarking this page for future automation testing tutorials and LambdaTest’s offer updates.
Different Types of Dropdowns, an Automation Test Engineer Must Know
In HTML, we encounter four types of dropdown implementations:
- Dropdown Navigation Options: These are usually encountered in NavBar of a website with dropdown links to other web pages.
- Dropdown Command Options: Like navigation dropdown options, these are found at the top, but these are meant to perform some action on the active page itself. Example – Google Docs Menu Bar.
- Attribute Selection Dropdown Options: These are commonly used to implement search filtering features and customization options like changing the color template or language of a website.
- Form Filling Dropdowns Options: These sort of dropdown options are used for registration forms or product/service booking forms.
How To Handle Dropdowns in Selenium Webdriver
Testing all sorts of dropdown elements can be handled using CSS and/or XPATH selectors in Selenium Python. But as an additional feature, we have the SELECT class in Selenium WebDriver. We can use it to automate interactions with dropdown options implemented using the <select> HTML tag.
What Is ‘Select’ Class in Selenium WebDriver?
Selenium provides the Select class to implement the HTML Select elements. The Select class in Selenium is an ordinary Java class that you can create as a new object using the keyword New and specifying a web element’s location. In this tutorial on how to handle dropdowns in Selenium WebDriver, we shall see different SELECT class functions:
class selenium.webdriver.support.select.Select(webelement)
Under the hood, the Selenium select class checks if the HTML web element we are using it with is a <select> tag or not. If not, Selenium WebDriver throws UnexpectedTagNameException
.
Note: Selenium ‘Select’ is only meant to be used with HTML <select> tag and none else.”
The selenium SELECT class comprises of following sub-functions:
options(self)
This function finds all <options> tags within the target <select> tag.
all_selected_options(self)
This function loops over all the options and checks if selected using is_selected() method and returns a list of selected “options.”
first_selected_option(self)
This is similar to the above function, loops over all available options under the <select> tag but returns as soon as it finds the first is_selected() <option>.
select_by_value(self, value)
This function makes use of a CSS selector to evaluate the value attributes of web elements. It returns all the <options> with matching values.
select_by_index(self, index)
This SELECT class function of Selenium evaluates the index of <option> tags by using get_attribute(“index”) and returns the matching option.
select_by_visible_text(self, text)
This function is implemented using XPATH and multiple if-else. Selects the option elements which contain string equal to text attribute between opening and closing option tags i.e, <option>dummy_text_attribute</option>.
Similarly, there are functions to deselect the selected options too:
deselect_by_index(self, index)
deselect_by_value(self, value)
deselect_all(self)
deselect_by_visible_text(self, text)
The two functions which are internally used by Selenium WebDriver’s SELECT class to implement the above functions for “selecting or deselecting” options are:
def _setSelected(self, option):
if not option.is_selected():
option.click()
def _unsetSelected(self, option):
if option.is_selected():
option.click()
For all the below demonstrations, we shall be using a webpage with three different dropdown elements hosted on the demo page as shown below:
Selecting a Dropdown Option in Selenium WebDriver With Matching Text
To automate testing of <option> selection, based on the criteria whether it matches a particular text string or not, we use the select_by_visible_text(self, text)
method of a SELECT class in Selenium.
Example
Under the “Demo for individual select” section in the aforementioned webpage, we have four options – “Python, Java, C#, PHP”. To select “Java” using string match:
dropdown1 = Select(driver.find_element_by_id('lang1'))
dropdown1.select_by_visible_text('Java')
Selecting a Dropdown option in Selenium WebDriver using the value attribute
Often, <select> and <options> tags when used inside a form are implemented by assigning a “value” attribute to options. The text between opening and closing <option> tags is visible to the users, but it is the value assigned to the “value” attribute sent to the server on form submit. As said before, Selenium WebDriver provides an API for automation test engineers to select options based on option values too, i.e., select_by_value(self, value)
.
On our demo page, the first “Select your lang:” section is a form element implemented using select and option with the value attribute. To select python using select_by_value
method –
dropdown = Select(driver.find_element_by_id('lang'))
dropdown.select_by_value('python')
Selecting a Dropdown Option in Selenium WebDriver Using Index
Javascript provides a DOM property to select <options> using index –
document.getElementById("myCourses").selectedIndex = "3";
Similarly, Selenium Python provides a method i.e., select_by_index(self, index)
to automate the selection of option(s). In the “Demo for individual select” section on our test webpage, we can select C# by passing an index of ‘3’.
dropdown = Select(driver.find_element_by_id('lang1'))
dropdown.select_by_index(3)
Using Selenium WebDriver for Handling Dropdowns With Multiple Select Options Enabled
Multiple option selection is enabled by adding an attribute named “multiple” to <select> elements. To check if a <select> dropdown allows multiple selections, we can use XPath or get attribute or both. Internally, Selenium in its __init__() constructor use the following approach to evaluate if the drop-down list allows multiple option selection –
self._el = webelement
multi = self._el.get_attribute("multiple")
self.is_multiple = multi and multi != "false"
We first identify a <select> webElement using xpath or CSS selectors and then evaluate if it contains a “multiple” attribute using the get_attribute() method.
dropdown = driver.find_element_by_tag_name('select')
if dropdown.get_attribute("multiple"):
print("multiple select options can be chosen")
else:
print("only one select option can be selected")
Selecting Multiple Dropdown Options in Selenium WebDriver
Once you know that multiple options can be selected in a dropdown, we can iterate over options and choose them using Selenium WebDriver’s “select” class methods. Alternatively, we can also use the “actionchains” class of Selenium WebDriver to select multiple choices. The strategy is first to get the <select> web element and then perform chained click actions on it with the “ctrl” key pressed. For our demo test page, we demonstrate both approaches to select multiple options in Selenium Python.
For the “action chains” demo, we first evaluate if the <select> element with id “lang2” is having a “multiple” attribute. If yes, then we select webelements with “PHP” and “C#” options respectively –
myOption = driver.find_element_by_xpath("//select[@multiple]/option[contains(text(), 'C#')]")
myOption1 = driver.find_element_by_xpath("//select[@multiple]/option[contains(text(), 'PHP')]")
And then we can execute chained actions on it using the following code –
ActionChains(driver).key_down(Keys.CONTROL).click(myOption).key_up(Keys.CONTROL).perform()
ActionChains(driver).key_down(Keys.CONTROL).click(myOption1).key_up(Keys.CONTROL).perform()
For the Selenium SELECT class demo, we first find the <select> element with id “lang2” having a “multiple” attribute. If yes, then we select options with “Java,” “PHP,” and “Python” using select_by_index, select_by_value, select_by_visible_text
, respectively.
dropdown = Select(driver.find_element_by_id('lang2'))
dropdown.select_by_index(3)
dropdown.select_by_value('php')
dropdown.select_by_visible_text('Python')
Selecting all the Dropdown Options in Selenium WebDriver
To choose all options, we can loop over available options and select using Selenium WebDrivers SELECT APIs. For our example demo, to select all options of webelement with id “lang2” –
dropdown = Select(driver.find_element_by_id('lang2'))
for opt in dropdown.options:
dropdown.select_by_visible_text(opt.get_attribute("innerText"))
Deselecting or Clearing Already Selected Dropdown Options in Selenium WebDriver
To deselect an option we can use any of:
deselect_by_index(self, index)
deselect_by_value(self, value)
deselect_by_visible_text(self, text)
And to deselect all, we have the method deselect_all(self)
In our example, we can deselect python from multiple selected options using:
dropdown = Select(driver.find_element_by_id('lang2'))
dropdown.deselect_by_visible_text('Python')
Using LambdaTest to Perform an Automated Select Deselect Test Operation in the Cloud Using Selenium Grid
Now, we shall run an automated test in the cloud using LambdaTest Selenium Grid and Python to see how to handle dropdowns in Selenium using the Selenium SELECT class. If you’re new to LambdaTest, it is an affordable, efficient and scalable testing platform, compatible with all major testing frameworks, CI/CD tools, and languages. Intuitive documentation, in-depth insights, and testing tutorials make it pretty easy for even beginners to test across 2000+ browsers seamlessly, OS and devices combination.
Please read comments in the following test script to understand the program flow:
# import the packages we would be using to automate this test
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import time
# Username, Access Token and grid URL can be obtained through automation dashboard -
username = "user_name"
accessToken = "access_key"
gridUrl = "hub.lambdatest.com/wd/hub"
# environment configs, for simplicity we have only two configs. You can have more than 2000 if required
browsers = [
{
"browser":"Chrome",
"version":"88.0",
"OS" : "windows 10",
"resolution" : "1024x768"
},
{
"browser":"Firefox",
"OS" : "windows 10",
"version":"71.0",
"resolution" : "1024x768"
}
]
# looping over environment to perform cross-browser testing
for cap in browsers:
desired_cap = {
# operating system
'platform' : cap["OS"],
# Browser
'browserName' : cap["browser"],
'version' : cap["version"],
# Resolution of machine
"resolution": cap["resolution"],
"name": "Select Option in selenium webdriver",
"build": "Select Option in selenium webdriver",
"network": True,
"video": True,
"visual": True,
"console": True,
}
url = "https://"+username+":"+accessToken+"@"+gridUrl
print("Initiating remote driver on platform: "+desired_cap["platform"]+" browser: "+desired_cap["browserName"]+" version: "+desired_cap["version"])
# driver instance creation
driver = webdriver.Remote(
desired_capabilities=desired_cap,
command_executor= url
)
# maximizing the browser window to fit the screen resolution
driver.maximize_window()
# loading the passed url into browser
driver.get('https://pynishant.github.io/dropdown-selenium-python-select.html')
# identifying select element with id="lang2" using selectors and checking if attribute "multiple" is present in it.
dropdown = driver.find_element_by_id('lang2')
if dropdown.get_attribute("multiple"):
# xpath selector to find the element with text "C#" and "PHP" respectively
myOption = driver.find_element_by_xpath("//select[@multiple]/option[contains(text(), 'C#')]")
myOption1 = driver.find_element_by_xpath("//select[@multiple]/option[contains(text(), 'PHP')]")
# using actionchains to select multiple options
ActionChains(driver).key_down(Keys.CONTROL).click(myOption).key_up(Keys.CONTROL).perform()
# pausing program execution for few seconds
time.sleep(2)
ActionChains(driver).key_down(Keys.CONTROL).click(myOption1).key_up(Keys.CONTROL).perform()
try:
# creating a selenium Select webelement of html <select> tag
dropdown = Select(driver.find_element_by_id('lang2'))
# using selenium webdriver's select class methods to find all selected options and printing it's text. Ideally, here output should be "PHP, C#"
print("All selected options using ActionChains : \n")
for opt in dropdown.all_selected_options:
print(opt.get_attribute('innerText'))
# clearing the php selection we performed in last step
dropdown.deselect_by_visible_text('PHP')
# Again printing the active selections, this time output should be only c#
print("Remaining selected options after deselecting PHP : \n")
for opt in dropdown.all_selected_options:
print(opt.get_attribute('innerText'))
time.sleep(2)
# Using selenium select class method to deselect all options
dropdown.deselect_all()
time.sleep(2)
# Using different select class methods, to select multiple options
# selecting by index, Java
dropdown.select_by_index(3)
time.sleep(2)
# selecting by value - php
dropdown.select_by_value('php')
time.sleep(2)
# selecting by text - python
dropdown.select_by_visible_text('Python')
# printing active selections - output should include all three php, python, java
print("All selected options after selecting using different select class methods : \n")
for opt in dropdown.all_selected_options:
print(opt.get_attribute('innerText'))
dropdown.deselect_all()
# to select all possible options
dropdown = Select(driver.find_element_by_id('lang2'))
for opt in dropdown.options:
dropdown.select_by_visible_text(opt.get_attribute("innerText"))
# all four selected options should be output
print("Selected option lists after selecting all of them : \n")
for opt in dropdown.all_selected_options:
print(opt.get_attribute('innerText'))
driver.quit()
except Exception as e:
print(e)
print("error")
else:
print("get_attribute didn't work!")
Output
You can check the status of your successful test runs or builds on your LambdaTest Automation dashboard. For the above program, here is the dashboard’s screenshot:
Common Error While Working with Dropdowns: Handling ElementNotInteractable Exception
When you’re interacting with dropdowns, make sure:
- The element is clickable.
- The element is visible.
- The element is enabled.
- Use the try-except-finally approach in Python.
ElementNotInteractable exception arises when the element is either not accessible (hindered by another element) or inactive. Certain elements are not clickable. If you try to click operation on them, it results in an error.
If you target element is AJAX rendered or depends on another event to turn clickable then the following approach might help:
rom selenium.webdriver.common.by import By
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome(path_to_your_chrome_driver)
driver.maximize_window()
# some code...
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "id_of_your_target_element"))
)
# some code...
Here, you explicitly wait for a duration before your target element becomes clickable.
You may also check if a target element is enabled using:
element.is_enabled()
Or if the target element is visible:
element.is_displayed()
Let’s demonstrate why checking an element’s visibility is important before interacting with it.
This time we use a slightly different version of the above demo url: pynishant.github.io/dropdown-visibility-demo.html
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.maximize_window()
driver.get('pynishant.github.io/dropdown-visibility-demo.html')
try:
# try clicking invisible element
langDropdown = driver.find_element_by_xpath('//select[@id="lang1"]')
ActionChains(driver).click(langDropdown).perform()
except Exception as e:
print(e)
# try clicking visible element
langDropdown = driver.find_element_by_xpath('//select[@id="lang1"]/following-sibling::div[1]')
ActionChains(driver).click(langDropdown).perform()
# try clicking an element with another element overflowing it
myOption = driver.find_element_by_xpath('//select[@id="lang"]/option[@value="python"]')
ActionChains(driver).click(myOption).perform()
time.sleep(3)
# Get the selected value of element under target and element hovering target
selectedOption = Select(driver.find_element_by_xpath('//select[@id="lang"]'))
selectedOption1 = Select(driver.find_element_by_xpath('//select[@id="lang1"]'))
try:
selected_option = selectedOption.first_selected_option.text
except Exception as e:
print(e)
selected_option = "None"
try:
selected_option1 = driver.find_element_by_xpath('//select[@id="lang1"]/following-sibling::div[1]').text
except Exception as e:
print(e)
selected_option1 = "None"
print("Selection output for id='lang' element with lang1 expanded : " +str(selected_option))
print("Selection output for id='lang1' element with lang1 expanded : " +str(selected_option1))
time.sleep(3)
# try clicking 'lang select' elements with car dropdown elements hovering over it. Notice Car elements are not clickable.
carDropdown = driver.find_element_by_xpath('//select[@id="cars"]')
ActionChains(driver).click(carDropdown).perform()
time.sleep(3)
myOption = driver.find_element_by_xpath('//select[@id="lang"]/option[@value="python"]')
ActionChains(driver).click(myOption).perform()
# get selected value of target and car elements
selectedOption = Select(driver.find_element_by_xpath('//select[@id="lang"]'))
selectedOption2 = Select(driver.find_element_by_xpath('//select[@id="cars"]'))
try:
selected_option = selectedOption.first_selected_option.text
except Exception as e:
print(e)
selected_option = "None"
try:
selected_option2 = selectedOption2.first_selected_option.text
except Exception as e:
print(e)
selected_option2 = "None"
print("Selection output for id='cars' element with lang1 expanded : " +str(selected_option2))
print("Selection output for id='lang' element with car dropdown expanded : " +str(selected_option))
time.sleep(5)
driver.quit()
Output on executing this in the command line using:
python demo-visibility.py
In the above code, we try interacting with an invisible element and it throws JS error:
try:
# try clicking invisible element
langDropdown = driver.find_element_by_xpath('//select[@id="lang1"]')
ActionChains(driver).click(langDropdown).perform()
except Exception as e:
print(e)
Next we try selecting an element in <select id=”lang”> tag with <select id=”lang1”> tag expanded and hovering over <select id=”lang”>.
# try clicking visible element
langDropdown = driver.find_element_by_xpath('//select[@id="lang1"]/following-sibling::div[1]')
ActionChains(driver).click(langDropdown).perform()
# try clicking an element with another element overflowing it
myOption = driver.find_element_by_xpath('//select[@id="lang"]/option[@value="python"]')
ActionChains(driver).click(myOption).perform()
time.sleep(3)
# Get the selected value of element under target and element hovering target
selectedOption = Select(driver.find_element_by_xpath('//select[@id="lang"]'))
try:
selected_option = selectedOption.first_selected_option.text
except Exception as e:
print(e)
selected_option = "None"
try:
selected_option1 = driver.find_element_by_xpath('//select[@id="lang1"]/following-sibling::div[1]').text
except Exception as e:
print(e)
selected_option1 = "None"
As apparent from the output, we instead end up selecting “Java,” an element from <select id=”lang1”>. This could lead to incorrect testing at times. So, do observe the behavior of elements.
Conclusion
In this Selenium WebDriver tutorial, we demonstrated how to handle dropdowns in Selenium WebDriver using the methods provided by selenium.webdriver.support.select.Select(webelement) class of Selenium WebDriver. We explored ways to select/deselect an option from a dropdown. We also detailed on “checking if multiple selections are allowed, and selecting multiple options if required.”
The article also shares tac for automation testers to avoid ElementNotInteractable error when automating interactions with dropdown elements. For scalable automation needs, adhere to best test automation practices, and consider testing in the cloud to cut costs.
That’s all folks, I hope you liked this Selenium WebDriver tutorial for Selenium test automation. For any clarification, please comment. If you found this helpful, please share it in your circle and on social media.
Happy Testing!!!
Published at DZone with permission of Harshit Paul, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments