4

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

dguy73
  • 71
  • 7
  • 1
    Can you elaborate on why the answer you linked did not help you? It seems like a dict would do a lot to alleviate the multiple `if` and `elif` blocks in your example code. Are you just having difficulty figuring out how to hash a function call? That is, how to store a function in a hash. – Mike Holt Mar 18 '19 at 19:32
  • @MikeHolt i'm not able to understand how hashing a function call could help? Kindly give me some insight on it. – dguy73 Mar 18 '19 at 19:50
  • Sorry, my wording was a little sloppy. I was referring to storing (references to) functions as values in a hash (a.k.a. a dict in Python), exactly as shown in chepner's answer. – Mike Holt Mar 18 '19 at 20:14

2 Answers2

11

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)
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thank you very much for clearing this up, i'll try this and update you. – dguy73 Mar 18 '19 at 19:44
  • It's working , but a small question.. the driver object needs to be instantiated before calling the dictionary? – dguy73 Mar 18 '19 at 19:55
  • 1
    As shown here, yes. I'll add a solution using `methodcaller`, which I omitted because it looks a bit more complicated, but makes `d` independent of the driver. – chepner Mar 18 '19 at 19:57
  • chepner, can you help once more? How do i make method caller for explicit wait function in selenium. Example: """ WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "myDynamicElement"))) """ – dguy73 Mar 18 '19 at 21:59
4

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)))
Danielle M.
  • 3,607
  • 1
  • 14
  • 31
  • Hey Danielle, is there any way we can implement this for the following statement? """ WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myDynamicElement"))) """. I have tried the following way: `driver.get('https://www.google.com/') locator_type= 'NAME' element = 'q' exp_wait_str = "WebDriverWait({}, 10).until(EC.presence_of_element_located((By.{}, {})))".format(driver, locator_type, element) fun = getattr(driver, exp_wait_str) web_element = fun(locator_type, element)` – dguy73 Mar 21 '19 at 18:32
  • I'm trying to understand the concept but i'm not able use the method you suggested for the above statement. Kindly look into this. – dguy73 Mar 21 '19 at 18:35
  • 1
    Hi there! What you're trying to do is dynamically pull the locator_type from the By class. I'll update my answer above with how to do that. – Danielle M. Mar 21 '19 at 19:44
  • 1
    Thank you very much Danielle, it worked like a charm and you helped remove more than 30 lines of repetitve code. – dguy73 Mar 22 '19 at 14:50