Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need to reduce multiple else if in python

I'm trying to automate regression testing for basic html websites using selenium-webdriver using python. I wrote a function that will take input from an excel to determine the type of locator of an element on the webpage which goes like this:

            if locator_type == "ID":
                web_element = self.__driver.find_element_by_id(element)
            elif locator_type == "NAME":
                web_element = self.__driver.find_element_by_name(element)
            elif locator_type == "XPATH":
                web_element = self.__driver.find_element_by_xpath(element)
            elif locator_type == "TAG_NAME":
                web_element = self.__driver.find_element_by_tag_name(element)
            elif locator_type == "LINK_TEXT":
                web_element = self.__driver.find_element_by_link_text(element)
            elif locator_type == "CLASS_NAME":
                web_element = self.__driver.find_element_by_class_name(element)
            elif locator_type == "PARTIAL_LINK_TEXT":
                web_element = self.__driver.find_element_by_partial_link_text(element)

This is so that the we could specify the locator type and give actual locator('element') so that selenium could try to find the web element on the web-page. Is there any way to reduce the elif statements or any other better way to write this part?

Too many if statements

I tried the method in the above link but it didn't help me. Kindly help me resolve this.

EDIT I tried create a dict like this

locator_dict = {
'ID' : driver.find_element_by_id(element),
'NAME' : driver.find_element_by_name(element)}

then i received an error saying that element is not defined

like image 991
dguy73 Avatar asked Dec 06 '22 10:12

dguy73


2 Answers

The only difference between the bodies of the various clauses is which method you actually call. You can factor that out into a dict.

d = {
    "ID": self.__driver.find_element_by_id,
    "NAME": self.__driver.find_element_by_name,
    # etc
}

if locator_type in d:
    web_element = d[locator_type](element)
else:
    # do something if you haven't defined a method for locator_type

You can also use methodcaller, although this first attempt makes d depend on element:

from operator import methodcaller

methods = [
    ("ID", "find_element_by_id"),
    ("NAME", "find_element_by_name"),
    # etc
]
d = { tag: methodcaller(method, element) for tag, method in methods } 

if locator_type in d:
    web_element = d[locator_type](self.__driver)

To be completely independent of the driver or the element, use

d = dict(methods)
if locator_type in d:
    web_element = methodcaller(d[locator_type], element)(self.__driver)
like image 174
chepner Avatar answered Dec 21 '22 23:12

chepner


You can construct the name of the function to call from the locator_type, and then call it:

locator_type_lower = locator_type.lower()

find_function_name = 'find_element_by_{}'.format(locator_type_lower)

driver_function_to_call = getattr(self.__driver, find_function_name)

web_element = driver_function_to_call(element)

You should probably wrap this in some error checking - check if the function exists, check if it's callable, etc.

What this does: converts the locator_type to lowercase, then builds what is hopefully the function name of an existing function in self.__driver, then calls it with your element.

This method has the advantage that you don't have to map strings to functions - if self.__driver has a function called find_element_by_foo_bar_length, then you can simply add FOO_BAR_LENGTH into your excel spreadsheet, and it will get called, no need to update your mapping dict.

Edit: updated for comments

To translate locator_type string into an attribute of the By class:

locator_type = 'ID'
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((getattr(By, locator_type), element)))
like image 35
Danielle M. Avatar answered Dec 21 '22 22:12

Danielle M.