Protractor 的隐式等待如何与显式等待交互?

当隐式等待小于显式时,就会发生误解:

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);
    });
});

总时间:8.613 秒.设置隐式等待一秒:3000,结果为 6.865 秒.它是如何在引擎盖下工作的?提前非常感谢!

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!

推荐答案

这是个好问题.很多优秀的 QA 自动化人员对此感到震惊.

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

这是每个 driver.findElement(...) 上的特殊隐藏自动等待.如果在页面 DOM 结构中找不到元素,原始 webdriver(js、python、java)会抛出 NoSuchElementException.这种等待将在每个 driver.findElement 之前完成,无论您使用哪种定位器.当隐式等待超时时,将在 findElement 函数之外抛出 NoSuchElementException.

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.

启用隐式等待

默认情况下,隐式等待超时设置为 0.browser.manage().timeouts().implicitlyWait(3000) 使 webdriver 自动尝试/捕获此异常,并重试查找此元素.如果 3 秒(超时)过去了,并且元素仍然不存在于 DOM 中 - 只有这样你才会得到 NoSuchElementException.

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.

好的时候:

您的页面修改了 DOM 结构(网站的 99.999%)并且一些元素仍然不在 DOM 中,但会在 1-3 秒内出现.为了不进行显式等待并减少代码量 - 您可以尝试设置隐式等待超时.

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.

不好的时候:您想测试 DOM 中不存在该元素.这种等待是在每个 .findElement 调用之前添加的,所以当你尝试这样断言时:

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()

您的 implicitWait 仍在此处工作.首先,您将等待 3 秒元素出现,然后将抛出异常,并由 isPresent() 函数捕获,在这种情况下将返回 false(我们实际断言的内容).所以你要多等 3 秒!设置 implicitWait(0) 并在断言元素不存在后将其设置回来是有意义的(这可能真的很烦人).

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).

结论当您了解它的工作原理时,隐式等待很好.我建议不要将隐式等待设置为超过 1-5 秒(您应该为每个网站定义自己的值).此外,如果您打算断言许多不存在的元素 - 将隐式等待重置为 0,然后将其设置回来.

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.

这种等待你应该自己调用,但它比隐式等待灵活得多.在protractorjs中,当你需要等待某些东西时,你必须调用browser.wait().它接受谓词函数(只返回真/假,没有异常的函数).Webdriver 将轮询此函数,直到发生超时(您将其指定为第二个参数).您还可以指定要作为第三个参数抛出的错误消息.

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.

显然,在网络自动化中,您大部分时间都在等待某些元素条件.为此,这些人创建了谓词函数集合.此函数调用 ExpectedConditions,并将为传递给它们的元素返回 true/false.

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, '错误信息')

好的时候:当您必须等待元素的一些棘手条件时.您可以轻松定义自己想要等待的条件、指定自定义超时等.在处理可能尚未准备好的元素之前使用.

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.

不好的时候:当您尝试通过组合 browser.sleep() 来帮助您时,隐式等待和显式等待结合在一起.browser.sleep() 默认是不好的,在 99% 的情况下你可以用 browser.wait() 替换它并提供条件,或者编写你自己的条件.

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.

当您设置了隐式等待并尝试调用显式等待时,会发生更多有趣的事情.想象:browser.manage().timeouts().implicitlyWait(10000)browser.wait(EC.stalenessOf($('NON-EXIST-ELEMENT')), 5000)//等待5秒让元素消失

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

这里发生了什么:等待函数为您的元素调用 stalenessOf() 函数.在其中,driver.findElement() 被调用.隐式等待不要让这个函数立即抛出错误,并将网页池化 10 秒,直到发生隐式等待超时,我们得到 NoSuchElementException.发生异常,执行返回等待函数,已经过了 10 秒!等待以 TimeOutException 终止,因为它只安排了 5 秒.我们收到错误,等待时间比预期的要长.

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.

另外请记住,JS 是异步的,由于事件循环,不能保证准确的等待时间.通常这会使等待不准确 - 5200 毫秒而不是 5000 毫秒(例如).这是完全不同的故事:)

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 :)

隐式超时 - 4000 毫秒.

implicit timeout - 4000 milliseconds.

显式超时 - 5000 毫秒.

explicit timeout - 5000 milliseconds.

  1. 等待开始.第一次调用谓词函数 - presenceOf()
  2. 内部谓词调用原始 webdriverjs 函数 - driver.findElement(By.xpath('//*[@name='qwer']'))
  3. 由于设置了隐式等待,我们在抛出错误之前等待它.
  4. 经过了 4000 毫秒的隐式元素等待.只是现在我们将错误返回到谓词函数.
  5. 谓词函数捕获错误,并改为返回 false
  6. 由于在显式等待超时之前我们还有 1000 毫秒的时间 - 再次调用谓词函数.
  7. 隐式等待再次开始.4000 毫秒后 - 将错误返回到谓词函数
  8. 谓词返回 false
  9. Wait 函数为 false,我们的显式等待超时 - 在理想情况下 - 大约为 8000 毫秒,但也要注意异步调用,因此实时会更多
  10. 等待抛出错误 - jasminejs 捕获错误,并且测试失败

我希望这会有所帮助!

相关文章