2

I am using Selenium (python) to test a web application. However, sometimes a line of code triggers an error cause the first line didn't have time enough to run. For instance:

...
driver.find_element_by_id("form_widget_date").click()
driver.find_element_by_link_text(str(self.day)).click()
...

As a result, the second line (sometimes) woudn't find the link_text because Selenium, apparently, does not have time to finish the first line. The error does not occur when I put a sleep between the lines.

My question is: Is there a way to automatically wait a bit longer for each line of code instead of solving it like this:

...
time.sleep(2)
driver.find_element_by_id("form_widget_date").click()
time.sleep(2)
driver.find_element_by_link_text(str(self.day)).click()
time.sleep(2) 
...

?

almanegra
  • 653
  • 8
  • 21

3 Answers3

10

There are basically three choices for what you want to do.

  1. Use time.sleep(...).

    This is the worst possible option for real world development. This is terrible because you are guaranteed to wait every single time for the amount of time you specify. If you find that sometimes you need 5 seconds for your test to pass, then if the condition you are waiting for is ready in 1 second, you are still going to wait 5 seconds. In a substantial test suite, these waits will add up and add minutes to the total time it takes to run the suite.

  2. Use Selenium's implicit wait.

    The advantage is that you can set an implicit wait and forget about it. You don't have to specify wait, after wait, after wait. The problem though is that implicit waits do not mix with explicit waits. The details of why they do not mix are explained quite well in this answer. In my experience using implicit waits works well only in extremely simple cases.

  3. Use Selenium's explicit waits.

    If your application is of the dynamic kind, if you have Ajax, if you modify the DOM on the fly, you are going to have to use explicit waits sooner or later, and at that point you'll have stop relying on implicit waits. The disadvantage is that your code is going to be more verbose. You can mitigate it with wrapper functions. Here's an example of how you could do it, assuming driver is set to a WebDriver object:

    from selenium.webdriver.common.by import By
    from selenium.webdriver.support.ui import WebDriverWait
    
    
    class MyDriver(object):
        def __init__(self, driver, default_timeout=2):
            self.driver = driver
            self.timeout = default_timeout
    
        def find_element(self, locator):
            return WebDriverWait(self.driver, self.timeout).until(
                EC.presence_of_element_located(locator))
    
    mydriver = MyDriver(driver)
    foo = mydriver.find_element((By.CSS_SELECTOR, ".foo"))
    
Community
  • 1
  • 1
Louis
  • 146,715
  • 28
  • 274
  • 320
2

The reality is you your code is going to often end up with a lot of waits in it. You do want to avoid using sleeps thought. Take a look at the use of Explicit waits. This allows a set time period to wait or the execution will time out, however when the element is found it will automatically more on regardless of how much time is still remaining in the wait period.

From the selenium HQ docs:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0

ff = webdriver.Firefox()
ff.get("http://somedomain/url_that_delays_loading")
try:
    element = WebDriverWait(ff, 10).until(EC.presence_of_element_located((By.ID, "myDynamicElement")))
finally:
    ff.quit()
Dan Snell
  • 2,185
  • 3
  • 21
  • 35
  • I think that's quite the same case. I still have to use it for every line of code that I want to set a "wait". And, anyway, sometimes I do not open a new url, I just open a dialog box or whatever (for instance, my example above) – almanegra Apr 14 '14 at 19:27
  • As I noted you are going to end up with a lot of waits in your code. You could create a wrapper class that you pass an element into to it becomes a little neater. Think about how you want to model your actions. Some steps will need waits between then and others will not. – Dan Snell Apr 14 '14 at 19:31
  • As I realized that sometimes the second line executes and sometimes doesnt, my fear is that I'm going to have a code two times bigger, cause I have to strive for consistency :/ – almanegra Apr 14 '14 at 19:37
  • Welcome to the wonderful world of UI automation. In a lot of my page classes I have a simple method to do an action but it looks something like this (java): public String CheckListingsPage() { _wait.until(ExpectedConditions.visibilityOf(listingResults)); return bodyTag.getAttribute("id"); } Different browsers execute commands differently so this keeps it from working in one and breaking in another without slowing the whole test way down with sleeps. – Dan Snell Apr 14 '14 at 19:41
2

There are two kind of waits explicit waits and implicit waits.

The solution that Dan provided is an example of an explicit wait, where as I think what you are looking for is an implicit wait.

An implicit wait is to tell WebDriver to poll the DOM for a certain amount of time when trying to find an element or elements if they are not immediately available. The default setting is 0. Once set, the implicit wait is set for the life of the WebDriver object instance.

You can define the implicit wait time once in the beginning like so:

driver = webdriver.Firefox()
driver.implicitly_wait(10) # seconds

Reference : Explicit and Implicit waits

Amey
  • 8,470
  • 9
  • 44
  • 63