Protractor 的隐式等待如何与显式等待交互?
How does implicit wait of Protractor interact with explicit wait?
当隐式等待少于显式等待时会发生误解:
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 秒。将 implicitlyWait 设置得更低:3000,结果为 6.865 秒。它是如何工作的?
非常感谢!
这是一个很好的问题,并且在 Selenium documentation 中警告并说明了在使用隐式和显式等待的组合时这些不可预测的等待时间。 browser.wait()
是一个明确的等待,您将其与 implicit wait - browser.manage().timeouts().implicitlyWait()
结合使用
Waiting is having the automated task execution elapse a certain amount
of time before continuing with the next step. You should choose to use
Explicit Waits or Implicit Waits.
WARNING: Do not mix implicit and explicit waits. Doing so can cause
unpredictable wait times. For example setting an implicit wait of 10
seconds and an explicit wait of 15 seconds, could cause a timeout to
occur after 20 seconds.
这是个好问题。很多优秀的 QA 自动化专家都为此伤透了脑筋。
隐式等待
这是特殊的隐藏自动等待,每次 driver.findElement(...)
。
如果在页面 DOM 结构中找不到元素,原始 webdriver (js, python, java) 将抛出 NoSuchElementException。这种等待将在每个 driver.findElement
之前完成,无论您使用哪种定位器。当隐式等待超时时,NoSuchElementException 将在 findElement
函数外抛出。
启用隐式等待
默认情况下,隐式等待超时设置为 0
。 browser.manage().timeouts().implicitlyWait(3000)
使 webdriver 自动 try/catch 此异常,并重新尝试查找此元素。如果 3 秒(超时)过去了,并且元素在 DOM 中仍然不存在 - 只有这样你才会得到 NoSuchElementException。
什么时候好:
您的页面修改了 DOM 结构(99.999% 的网站),一些元素仍然不在 DOM 中,但会在 1-3 秒内出现。为了不进行显式等待并减少代码量 - 您可以尝试设置隐式等待超时。
坏时:
您要测试 DOM 中不存在该元素。这种等待是在每次 .findElement
调用之前添加的,因此当您尝试这样断言时:
expect($('NON-EXIST-ELEMENT').isPresent()).toBeFalsy()
你的 implicitWait
仍然在这里工作。首先,您将等待 3 秒到元素出现,然后将抛出异常,并由 isPresent() 函数捕获,在这种情况下将 return false(我们实际断言)。所以你要多等 3 秒!设置 implicitWait(0)
并在断言元素不存在后将其重新设置是有意义的(这可能真的很烦人)。
结论
当您了解它的工作原理时,隐式等待是好的。我建议不要将隐式等待设置为超过 1-5 秒(您应该为每个网站定义自己的值)。此外,如果您计划断言大量不存在的元素 - 将隐式等待重置为 0,然后将其设置回去。
显式等待
这种你应该自己调用的等待,但它比隐式等待灵活得多。在protractorjs中,当你需要等待某事时,你必须调用browser.wait()
。它接受谓词函数(将 return 仅 true/false 的函数,没有例外)。 Webdriver 将轮询此函数,直到发生超时(您将其指定为第二个参数)。您还可以指定要抛出的错误消息作为第三个参数。
显然,在 Web 自动化中,您大部分时间都在等待某些元素条件。为此,人们创建了谓词函数集合。此函数调用 ExpectedConditions,并将 return true/false 用于传递给它们的元素。
browser.wait(ExpectedConditions.visibilityOf($('NON-EXISTING-ELEMENT')), 3000, 'error message')
什么时候好:
当您必须等待元素的某些棘手条件时。您可以轻松定义自己要等待的条件,指定自定义超时等。
在处理可能尚未准备好的元素之前使用。
坏时:
当您尝试通过组合 browser.sleep()
来帮助您时,隐式等待和显式等待结合在一起。默认情况下 browser.sleep()
是错误的,在 99% 的情况下,您可以根据提供的条件将其替换为 browser.wait()
,或者编写您自己的条件。
当您设置隐式等待并尝试调用显式等待时,会发生更多有趣的事情。
想象:
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。异常发生,执行returns等待函数,已经过了10秒!等待以 TimeOutException 终止,因为它只安排了 5 秒。我们遇到错误,等待时间比预期的长得多。
另外请记住,JS 是异步的,并且由于事件循环而无法保证准确的等待时间。通常这会使等待不准确 - 5200 毫秒而不是 5000(例如)。这是完全不同的故事:)
你的例子发生了什么
隐式超时 - 4000 毫秒。
显式超时 - 5000 毫秒。
- 等待开始。第一次调用谓词函数 -
presenceOf()
- 内部谓词调用原始 webdriverjs 函数 -
driver.findElement(By.xpath('//*[@name='qwer']'))
- 由于设置了隐式等待,我们在抛出错误之前等待它。
- 4000 毫秒的隐式元素等待已过。只是现在我们 return 谓词函数出错。
- 谓词函数捕获错误,returns false 相反
- 因为在显式等待超时之前还有 1000 毫秒 - 再次调用谓词函数。
- 隐式等待再次开始。 4000 毫秒后 - 将错误返回给谓词函数
- 谓词return为假
- 等待函数为假,我们的显式等待超时 - 在理想情况下 - 大约 8000 毫秒,但也要注意异步调用,所以实时时间会更长
- 等待抛出错误 - jasminejs 捕获错误,并且测试失败
希望对您有所帮助!
答案是:两个等待并行等待。隐式等待轮询 4 秒和 returns 失败,之后显式等待仅等待 4 秒并且还有 1 秒。这会再次重新发出隐式等待,并再次轮询 4 秒并再次失败。 8 秒后(隐式等待的 2 次尝试)显式等待 5 秒也超时,我们在浪费了 3 秒后终于得到了错误。例如,如果隐式设置为 8,显式设置为 17,我们将等待 8*3 = 24 秒。请注意,脚本执行需要一些时间,因此如果它很慢,下一次迭代可能不会开始。
当隐式等待少于显式等待时会发生误解:
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 秒。将 implicitlyWait 设置得更低:3000,结果为 6.865 秒。它是如何工作的? 非常感谢!
这是一个很好的问题,并且在 Selenium documentation 中警告并说明了在使用隐式和显式等待的组合时这些不可预测的等待时间。 browser.wait()
是一个明确的等待,您将其与 implicit wait - browser.manage().timeouts().implicitlyWait()
Waiting is having the automated task execution elapse a certain amount of time before continuing with the next step. You should choose to use Explicit Waits or Implicit Waits.
WARNING: Do not mix implicit and explicit waits. Doing so can cause unpredictable wait times. For example setting an implicit wait of 10 seconds and an explicit wait of 15 seconds, could cause a timeout to occur after 20 seconds.
这是个好问题。很多优秀的 QA 自动化专家都为此伤透了脑筋。
隐式等待
这是特殊的隐藏自动等待,每次 driver.findElement(...)
。
如果在页面 DOM 结构中找不到元素,原始 webdriver (js, python, java) 将抛出 NoSuchElementException。这种等待将在每个 driver.findElement
之前完成,无论您使用哪种定位器。当隐式等待超时时,NoSuchElementException 将在 findElement
函数外抛出。
启用隐式等待
默认情况下,隐式等待超时设置为 0
。 browser.manage().timeouts().implicitlyWait(3000)
使 webdriver 自动 try/catch 此异常,并重新尝试查找此元素。如果 3 秒(超时)过去了,并且元素在 DOM 中仍然不存在 - 只有这样你才会得到 NoSuchElementException。
什么时候好:
您的页面修改了 DOM 结构(99.999% 的网站),一些元素仍然不在 DOM 中,但会在 1-3 秒内出现。为了不进行显式等待并减少代码量 - 您可以尝试设置隐式等待超时。
坏时:
您要测试 DOM 中不存在该元素。这种等待是在每次 .findElement
调用之前添加的,因此当您尝试这样断言时:
expect($('NON-EXIST-ELEMENT').isPresent()).toBeFalsy()
你的 implicitWait
仍然在这里工作。首先,您将等待 3 秒到元素出现,然后将抛出异常,并由 isPresent() 函数捕获,在这种情况下将 return false(我们实际断言)。所以你要多等 3 秒!设置 implicitWait(0)
并在断言元素不存在后将其重新设置是有意义的(这可能真的很烦人)。
结论 当您了解它的工作原理时,隐式等待是好的。我建议不要将隐式等待设置为超过 1-5 秒(您应该为每个网站定义自己的值)。此外,如果您计划断言大量不存在的元素 - 将隐式等待重置为 0,然后将其设置回去。
显式等待
这种你应该自己调用的等待,但它比隐式等待灵活得多。在protractorjs中,当你需要等待某事时,你必须调用browser.wait()
。它接受谓词函数(将 return 仅 true/false 的函数,没有例外)。 Webdriver 将轮询此函数,直到发生超时(您将其指定为第二个参数)。您还可以指定要抛出的错误消息作为第三个参数。
显然,在 Web 自动化中,您大部分时间都在等待某些元素条件。为此,人们创建了谓词函数集合。此函数调用 ExpectedConditions,并将 return true/false 用于传递给它们的元素。
browser.wait(ExpectedConditions.visibilityOf($('NON-EXISTING-ELEMENT')), 3000, 'error message')
什么时候好: 当您必须等待元素的某些棘手条件时。您可以轻松定义自己要等待的条件,指定自定义超时等。 在处理可能尚未准备好的元素之前使用。
坏时:
当您尝试通过组合 browser.sleep()
来帮助您时,隐式等待和显式等待结合在一起。默认情况下 browser.sleep()
是错误的,在 99% 的情况下,您可以根据提供的条件将其替换为 browser.wait()
,或者编写您自己的条件。
当您设置隐式等待并尝试调用显式等待时,会发生更多有趣的事情。
想象:
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。异常发生,执行returns等待函数,已经过了10秒!等待以 TimeOutException 终止,因为它只安排了 5 秒。我们遇到错误,等待时间比预期的长得多。
另外请记住,JS 是异步的,并且由于事件循环而无法保证准确的等待时间。通常这会使等待不准确 - 5200 毫秒而不是 5000(例如)。这是完全不同的故事:)
你的例子发生了什么
隐式超时 - 4000 毫秒。
显式超时 - 5000 毫秒。
- 等待开始。第一次调用谓词函数 -
presenceOf()
- 内部谓词调用原始 webdriverjs 函数 -
driver.findElement(By.xpath('//*[@name='qwer']'))
- 由于设置了隐式等待,我们在抛出错误之前等待它。
- 4000 毫秒的隐式元素等待已过。只是现在我们 return 谓词函数出错。
- 谓词函数捕获错误,returns false 相反
- 因为在显式等待超时之前还有 1000 毫秒 - 再次调用谓词函数。
- 隐式等待再次开始。 4000 毫秒后 - 将错误返回给谓词函数
- 谓词return为假
- 等待函数为假,我们的显式等待超时 - 在理想情况下 - 大约 8000 毫秒,但也要注意异步调用,所以实时时间会更长
- 等待抛出错误 - jasminejs 捕获错误,并且测试失败
希望对您有所帮助!
答案是:两个等待并行等待。隐式等待轮询 4 秒和 returns 失败,之后显式等待仅等待 4 秒并且还有 1 秒。这会再次重新发出隐式等待,并再次轮询 4 秒并再次失败。 8 秒后(隐式等待的 2 次尝试)显式等待 5 秒也超时,我们在浪费了 3 秒后终于得到了错误。例如,如果隐式设置为 8,显式设置为 17,我们将等待 8*3 = 24 秒。请注意,脚本执行需要一些时间,因此如果它很慢,下一次迭代可能不会开始。