Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does implicit wait of Protractor interact with explicit wait?

The misunderstanding happens when implicit wait is less than explicit:

var timeOut = 5000;
var search = element(by.xpath(`//*[@name='qwer']`));
browser.manage().timeouts().implicitlyWait(4000);
browser.ignoreSynchronization = true;

describe('Protractor Test', function () {
    beforeEach(function () {
        browser.get('https://www.google.com.ua');
    });
    it('EC', function () {
        console.log('START');
        // browser.sleep(timeOut);
        browser.wait(protractor.ExpectedConditions.presenceOf(search), timeOut);
    });
});

Overall time: 8.613 seconds. Set implicitlyWait a second lower: 3000 and result is 6.865 seconds. How does it work under the hood? Big thanks in advance!

like image 867
Gennadiii Avatar asked Mar 06 '17 14:03

Gennadiii


People also ask

What elements does the implicit wait affects in protractor?

Implicitly wait in protractor, sets the maximum time that we are going to wait for the element to be available on the Website. Implicit wait affects only element and element.

Why implicit wait is not recommended?

Hey Aaron, the main disadvantage of implicit wait is that it slows down test performance. The implicit wait will tell to the web driver to wait for certain amount of time before it throws a "No Such Element Exception". The default setting is 0.

Does implicit wait applied to all elements?

Note: Implicitly wait is applied globally which means it is always available for all the web elements throughout the driver instance. It implies if the driver is interacting with 100 elements then, Implicitly wait is applicable for all the 100 elements.

What is the difference between the implicit wait & WebDriver wait?

Implicit Wait directs the Selenium WebDriver to wait for a certain measure of time before throwing an exception. Once this time is set, WebDriver will wait for the element before the exception occurs. Once the command is in place, Implicit Wait stays in place for the entire duration for which the browser is open.


1 Answers

Thats nice question. A lot of good QA automation guys broke their heads with this.

Implicit waits

This is special hidden automatic wait, on each driver.findElement(...). Original webdriver (js, python, java) throws NoSuchElementException if element cannot be found in page DOM structure. This kind of wait will be done before EVERY driver.findElement, no matter what kind of locator do you use. When implicit wait timed out, NoSuchElementException will be thrown outside findElement function.

Enabling implicit wait

By default implicit wait timeout is set to 0. browser.manage().timeouts().implicitlyWait(3000) makes webdriver automatically try/catch this exception, and retry to find this element. If 3 seconds (timeout) passed, and element is still not present in DOM - only then you are getting NoSuchElementException.

When it is good:

Your page modify DOM structure (99.999% of website) and some elements still not in the DOM, but appear within 1-3 seconds. To not make explicit waits, and reduce amount of code - you can try to set implicit wait timeout.

When it is bad: You want to test that element is not present in the DOM. This kind of wait is added before every .findElement call, so when you are trying to assert like this:

expect($('NON-EXIST-ELEMENT').isPresent()).toBeFalsy()

Your implicitWait still working here. First you will wait for 3 seconds to element to be present, then exception will be thrown, and caught by isPresent() function, that will return false in this case (what we actually asserting). So you are waiting for 3 extra seconds! It makes sense to set implicitWait(0) and then set it back after asserting element is not present (which might be really annoying).

Conclusion Implicit waits are good, when you understand how it is works. I recommend to not set implicit wait more than 1-5 seconds (you should define own value for each website). Also if you plan to assert lot of not-present elements - reset implicit wait to 0, and then set it back.

Explicit waits

This kind of waiting that you should call by yourself, but it much more flexible than implicit waits. In protractorjs, when you need to wait for something, you must call browser.wait(). It accepts predicate function (function that will return only true/false, no exceptions). Webdriver will poll this function until timeout occurs (you specify it as second param). Also you can specify error message that you want to throw as third parameter.

Obviously, that in web automation you wait for some element conditions most of the time. For this guys have created collection of predicate functions. This functions calls ExpectedConditions, and will return true/false for element that was passed to them.

browser.wait(ExpectedConditions.visibilityOf($('NON-EXISTING-ELEMENT')), 3000, 'error message')

When it is good: When you have to wait for some tricky conditions of your elements. You can easily define own conditions that you want to wait, specify custom timeout and so on. Use before manipulating with elements that might not be ready yet.

When it is bad: When you try to help you by combining browser.sleep(), implicit waits and explicit waits together. browser.sleep() is bad by default, in 99% of cases you can replace it with browser.wait() with provided conditions, or write your own condition.

Much more fun happens when you have your implicit wait set, and you trying to call explicit wait. Imagine: browser.manage().timeouts().implicitlyWait(10000) browser.wait(EC.stalenessOf($('NON-EXIST-ELEMENT')), 5000) //waiting for 5 seconds for element to disappear

What happens here: Wait function calls stalenessOf() function for your element. Inside it, driver.findElement() got called. Implicit wait don't let this function to throw error immediately, and pools webpage for 10 seconds until implicit wait timeout happens, and we are getting NoSuchElementException. Exception happens, and execution returns to wait function, 10 seconds are passed already! Wait is terminated with TimeOutException, because it was scheduled only for 5 seconds. We are getting error with wait time much longer that expected.

Also keep in mind that JS is async, and cannot guarantee exact wait time because of Event Loop. Usually this makes waiting not exact - 5200 ms instead 5000 (as example). This is absolutely different story :)


What happens in your example

implicit timeout - 4000 milliseconds.

explicit timeout - 5000 milliseconds.

  1. Wait started. First time calling predicate function - presenceOf()
  2. Internally predicate calls original webdriverjs function - driver.findElement(By.xpath('//*[@name='qwer']'))
  3. Since implicit wait is set, we are waiting for it before throw error.
  4. 4000 milliseconds of implicit element waiting passed. Only now we are returning error to predicate function.
  5. Predicate function catch error, and returns false instead
  6. Since we still have 1000 milliseconds before timeout of explicit wait - calling predicate function again.
  7. Implicit wait started again. 4000 milliseconds later - throwing error back to predicate function
  8. Predicate returns false
  9. Wait function got false, and our explicit wait is out of time - in ideal case - it would be about 8000 milliseconds, but also be aware about async calls, so real time would be more
  10. Wait throws error - jasminejs catch error, and fails test

I hope this will help!

like image 84
Xotabu4 Avatar answered Oct 21 '22 21:10

Xotabu4