Question

I am writing some functional tests and it takes 5 minutes to do some simple single-page testing because the find_element function takes 30 seconds to complete when the element is not found. I need to test for the absence of an element without having to wait for a timeout. I've been searching but so far have not found any alternative to find_element(). Here's my code:

def is_extjs_checkbox_selected_by_id(self, id):
    start_time = time.time()
    find_result =  self.is_element_present(By.XPATH, "//*[@id='" + id + "'][contains(@class,'x-form-cb-checked')]")  # This line is S-L-O-W
    self.step(">>>>>> This took " + str( (time.time() - start_time) ) + " seconds")
    return find_result

def is_element_present(self, how, what):
    try: self.driver.find_element(by=how, value=what)
    except NoSuchElementException, e: return False
    return True

Thanks.

Well, I followed most of the recommendations here and in the other links and in the end, and have failed to achieve the goal. It has the exact same behavior of taking 30 seconds when the element is not found:

# Fail
def is_element_present_timeout(self, id_type, id_locator, secs_wait_before_testing):
    start_time = time.time()
    driver = self.driver
    time.sleep(secs_wait_before_testing)
    element_found = True
    try:
        element = WebDriverWait(driver, 0).until(
            EC.presence_of_element_located((id_type, id_locator))
        )
    except:
        element_found = False
    elapsed_time = time.time() - start_time
    self.step("elapsed time : " + str(elapsed_time))
    return element_found

Here's a second approach using the idea of getting all elements

# Fail
def is_element_present_now(self, id_type, id_locator):
    driver = self.driver
    # This line blocks for 30 seconds if the id_locator is not found, i.e. fail
    els = driver.find_elements(By.ID, id_locator)
    the_length = els.__len__()
    if the_length == 0:
        result = False
    else:
        result = True
    self.step('length='+str(the_length))
    return result

Note, I've unaccepted previous answer since following poster's advice did not produce a successful result.

Was it helpful?

Solution 2

After researching this and getting nowhere I finally came up with an acceptable solution. It's kind of embarrassingly simple how easy this was to solve. All I had to do was to manipulate the timeout with the implicitly_wait() function, which for some reason I was ignoring/not noticing. I'm using the syntax provided by Louis but it would be even simpler and would work just the same by taking my original is_element_present() function above and adding the driver.implicitly_wait() functions before and after the find_element() function.

# Success
def is_element_present(self, id_type, id_locator):
    driver = self.driver
    element_found = True
    driver.implicitly_wait(1)
    try:
        element = WebDriverWait(driver, 0).until(
            EC.presence_of_element_located((id_type, id_locator))
        )
    except:
        element_found = False
    driver.implicitly_wait(30)
    return element_found

OTHER TIPS

Given the code you show in the question, if you have a 30 second wait before Selenium decides that the element is missing, this means you are using implicit waits. You should stop using implicit waits and use explicit waits exclusively. The reason is that sooner or later you'll want to use an explicit wait to control precisely how long Selenium waits. Unfortunately, implicit and explicit waits just don't mix. The details are explained in this answer.

There are two general methods I use to test for the absence of an element, depending on conditions. The one problem that we face when using Selenium to test dynamic applications is the following: at which point can you be certain that the element whose absence you want to check is not going to show up a fraction of a second later than the moment at which you check? For instance, if you perform a test which clicks a button, which launches an Ajax request, which may cause an element indicating an error to be added to the DOM when the request fails, and you check for the presence of the error element immediately after asking Selenium to click the button, chances are you're going to miss the error message, if it so happens that the request fails. You'd have to wait at least a little bit to give the Ajax request a chance to complete. How long depends on your application.

This being said, here are the two methods that I use.

Pair an Absence With a Presence

I pair the absence test with a presence test, use find_elements rather than find_element, check for a a length of zero on the array returned by find_elements.

By "I pair the absence test with a presence test" I mean that I identify a condition like the presence of another element on the page. This condition must be such that if the condition is true, then I do not need to wait to test for the absence: I know that if the condition is true, the element whose absence I want to test must be either finally absent or present on the page. It won't show up a fraction of a second later.

For instance, I have a button that when clicked performs a check through an Ajax call, and once the Ajax call is done puts on the page <p>Check complete</p> and under it adds paragraphs with error messages if there are errors. To test for the case where there is no error, I'd wait for <p>Check complete</p> to come up and only then check for the absence of the error messages. I'd use find_elements to check for these error messages and if the length of the returned list is 0, I know there are none. Checking for these error messages themselves does not have to use any wait.

This also works for cases where there is no Ajax involved. For instance, if a page is generated by the server with or without a certain element, then there is no need to wait and the method described here can be used. The "presence" of the page (that is, the page has finished loading) is the only condition needed to check for the absence of the element you want to check.

Wait for a Timeout

In cases where I cannot benefit from some positive condition to pair with my absence check, then I use an explicit wait:

import selenium.webdriver.support.expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from nose.tools import assert_raises

def test():
    WebDriverWait(driver, timeout).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, ".foo")))

assert_raises(TimeoutException, test)

In the code above driver a Selenium's WebDriver previously created, timeout is the desired timeout, and assert_raises is just an example of an assertion that could be used. The assertion passes if TimeoutException is raised.

I wrote a simple method in Java:

public boolean isElementPresent(long waitTime, String elementLocation) {
    WebDriverWait wait = new WebDriverWait(driver, waitTime); // you can set the wait time in second
    try {
        wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(elementLocation)));
    } catch (Exception e) {
        return false;
    }
    return true;
}

(you can see examples in python: http://selenium-python.readthedocs.org/en/latest/waits.html)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top