September 22, 2015
How to Get Fewer False Positives Using Wrappers for Selenium Calls
Many times we believe that we are completing some actions on a webpage, when in reality nothing has happened on the page and we simply didn’t notice it. We believe that we’ve clicked a button, but in reality the button is not clickable. We believe that some text has been entered into an input field, when that field doesn’t actually allow all of the characters that we have sent to it.
To ensure that these events don’t happen, we can easily create wrappers around the Selenium cells, which will create a better interaction with the DOM.
One of the best features in Selenium is Expected Conditions, which, when used with WebDriverWait, can create explicit waits that wait for different states of an element.
Below is an outline of how to create some wrappers over our most used Selenium calls using Expected Conditions. For future calls, we will be using the following imports and variables. These examples are given in Python, but similar calls are available in other programming languages as well.
from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait IMPLICIT_WAIT = 10 selector = (By.ID, 'element_id') text = 'some text'
Below is a simple example of how WebDriverWait and Expected Conditions work:
wait = WebDriverWait(driver, IMPLICIT_WAIT) element = wait.until( EC.visibility_of_element_located( selector )
WebDriverWait continually calls Expected Conditions every 500 milliseconds to check that the value is “true” until the condition is met or the time allotted has passed. If the condition is met in the IMPLICIT_WAIT time that has been given, then the found element is return. If not, the TimeoutException is raised.
Now it’s time to use this information in our tests.
The first example is our own click wrapper, which waits for the element to be clickable before clicking on it.
def click(selector): element = WebDriverWait(driver, IMPLICIT_WAIT).until( EC.element_to_be_clickable( selector ) ) element.click()
EC.element_to_be_clickable() method returns the element if the element is clickable. If the element is unable to be located, or the element is not enabled in the given IMPLICIT_WAIT, then a TimeoutException will be raised. This way, we’re sure that the button or link we’re trying to click is actually there and clickable, instead of an empty space on the page.
Another commonly used example is the
send_keys method. When using Selenium, a good method is finding the element, clicking on it, and then sending the text to it. Though this is good, we can improve upon it by going a bit further.
def enter_text(selector, text): element = WebDriverWait(driver, IMPLICIT_WAIT).until( EC.visibility_of_element_located(selector) ) element.click() element.clear() element.send_keys(text) WebDriverWait(driver, IMPLICIT_WAIT).until( EC.text_to_be_present_in_element(selector, text) )
By doing this, we are initially ensuring that the element we’re interacting with is available. After this, we follow the usual steps of clicking the element, clearing the text from it so we know exactly what text we’re entering into that input field, entering the actual text we need, and finally checking that the entered text is present in our input field.
However, if we do not want to go through these steps all of the time, we can easily add flags to our method and remove some steps from the process.
def enter_text(selector, text, clear=True, verify=False): element = WebDriverWait(driver, IMPLICIT_WAIT).until( EC.visibility_of_element_located(selector) ) element.click() if clear: element.clear() element.send_keys(text) if verify: WebDriverWait(driver, IMPLICIT_WAIT).until( EC.text_to_be_present_in_element(selector, text) )
Below is the final example used for handling alerts.
def accept_alert(accept=True): alert = WebDriverWait( driver, IMPLICIT_WAIT).until( EC.alert_is_present() ) if accept: alert.accept() else: alert.dismiss()
Once we input this, we then wait for the alert to be present and then either accept or dismiss it. Just like before, if the webdriver doesn’t find the alert in the IMPLICIT_WAIT time that has been given, then a TimeoutException will be raised.
These wrappers are good practice when using Selenium and can be used for almost all interactions with the DOM. With them, you can reduce the number of false positives generated and create far better waits than when using time-based wrappers.
Additional information on this can be found here.