Selenium Chrome Driver不尊重隐含的等待?

时间:2013-09-29 15:03:26

标签: java selenium-webdriver

好!所以我在Windows 8上使用了硒铬驱动程序(32位)。

我已将隐式等待设置如下:

DesiredCapabilities des=DesiredCapabilities.chrome();
ChromeOptions options = new ChromeOptions();
options.addArguments("window-size=1366,768");
des.setCapability(ChromeOptions.CAPABILITY, options);
dvr= new ChromeDriver(des);
    driver = new EventFiringWebDriver(dvr);
    driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

但在我的测试中,我得到一个staleElementException ...如下所示:

我并不太担心staleElementException,困扰我的是异常中的以下行:命令持续时间或超时:14毫秒

当超时已经隐含地设置为30秒那么为什么我会得到14毫秒的超时....任何建议或解决方法都将是非常感谢!

public static WebElement grabElementByPureXPath(String xpath)
{
    WebElement element = null;
    int attempts=1;
    try
    {
        while(attempts<7)
        {
            try
            {
                element=driver.findElement(By.xpath(xpath));
            }
            catch(StaleElementReferenceException e){}
                attempts++;
        }
    }
    catch(Throwable t)
    {
        try
        {
            element=driver.findElement(By.cssSelector(xpath));
        }
        catch(Throwable T)
        {
            takeScreenShot(xpath);
            Assert.assertTrue(t.getMessage(),false);
        }
    }

    return element;
}

&lt; -----------------------以下例外情况---------------- -----------------------------&GT;

org.openqa.selenium.StaleElementReferenceException: stale element reference: element   is not attached to the page document
  (Session info: chrome=29.0.1547.76)
  (Driver info: chromedriver=2.1,platform=Windows NT 6.2 x86_64) (WARNING: The server did not provide any stacktrace information)
**Command duration or timeout: 14 milliseconds**
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.35.0', revision: 'c916b9d', time: '2013-08-12 15:42:01'
System info: os.name: 'Windows 8', os.arch: 'amd64', os.version: '6.2', java.version: '1.7.0_25'
Session ID: aee4999cb9dc120f7e17629cc1621d7d
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities [{platform=WIN8, acceptSslCerts=true, javascriptEnabled=true, browserName=chrome, chrome={chromedriverVersion=2.1}, rotatable=false, locationContextEnabled=true, version=29.0.1547.76, cssSelectorsEnabled=true, databaseEnabled=true, handlesAlerts=true, browserConnectionEnabled=false, webStorageEnabled=true, nativeEvents=true, applicationCacheEnabled=false, takesScreenshot=true}]
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:191)
    at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:145)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:554)
    at org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:268)
    at org.openqa.selenium.remote.RemoteWebElement.click(RemoteWebElement.java:79)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement$1.invoke(EventFiringWebDriver.java:327)
    at com.sun.proxy.$Proxy12.click(Unknown Source)
    at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement.click(EventFiringWebDriver.java:340)
    at testCases.TC5663.test5663(TC5663.java:87)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.rules.Verifier$1.evaluate(Verifier.java:35)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runners.Suite.runChild(Suite.java:127)
    at org.junit.runners.Suite.runChild(Suite.java:26)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

/ * ** * ** * *** < EM> --------------------- ** * ** < EM> * ** * ** * ** * ** * ** < EM> * * /


1 个答案:

答案 0 :(得分:6)

编辑 - 您在此答案下面的评论和我对它的回答更接近您最初的要求:

  

根据记录的行为,StaleElementException似乎是   隐含的等待证明。这似乎是我的Selenium的一个缺点   图。

它与隐式等待无关,真的。当元素被搜索时,它被发现了!然后改变了一些东西,元素被卸载并且可能再次加载,但你无法确定它的位置。考虑一下:

driver.findElements(By.className("hello"));

这会找到12个元素,你可以从中获取第三个元素并尝试对它采取行动 - 但是oops,它现在已经过时了! Selenium应该做什么?

最合理的事情可能是重新找到所有元素并再次从中获取第三个元素。但页面明显改变了(因为元素是陈旧的),事实上,我们的元素现在不是第三,而是第四。而Selenium不可能知道这一点。如何克服这个?


原始回答:

这是按预期工作的,它是记录在案的行为。

您的grabElementByPureXPath()找到并返回正确的元素。在Selenium抛出click()时,当您尝试StaleElementReferenceException找到的元素时,就会发生这种情况。

这种情况经常发生在这样的情况下:

  1. 您点击异步加载新页面或至少更改它的内容。
  2. 您立即(在页面加载完成之前)通过grabElementByPureXPath()搜索元素...然后找到并存储它!
  3. 页面最终卸载,新页面加载。
  4. 您尝试click()以前找到的元素,但现在它已过时,即使新页面也包含相同的元素。但是从Selenium的观点来看,这只是原始元素的双胞胎,而不是原始元素本身。
  5. 隐式等待与此无关 - 找到该元素但现在不再存在。 Selenium可能可以尝试使用传递的原始By对象再次搜索元素,但intentionally doesn't

    您可以在我的答案中尝试其中一种解决方案:How to resolve, Stale element exception? if element is no longer attached to the DOM?


    你的方法的最终挑剔。我知道你没有要求,但我觉得有必要告诉你:

    1. 您每次都会找到该元素6次。即使它是在第一次尝试时发现的。改变你的状况

      while(attempts<7)
      

      while ((element == null) && (attempts < 7))
      
    2. 此:

      try
      {
          element=driver.findElement(By.xpath(xpath));
      }
      catch(StaleElementReferenceException e) {}
      

      永远不会触发。 StaleElementReferenceException不会发生driver.findElement(),也不会发生NoSuchElementException。它根本不可能。此方法永远不会抛出异常。它只会抛出catch(StaleElementReferenceException e){} attempts++;

    3. 至少尝试评论您的空代码块。看到这样的代码有点令人困惑:

      attempts

      如果我没有找到这样的问题,我可能会认为StaleElementReferenceException增量只发生在catch (StaleElementReferenceException e) { // do nothing } attempts++; 或类似的事情上。上面代码的通常和更易读的形式是:

      try-catch

      评论空代码块还可以消除有人删除代码的风险。如果这不是id块,而是没有代码的无参数构造函数,我强烈建议你在那里发表评论来解释为什么必须在那里明确说明构造函数(有时,它必须)。否则,有人会迟早将其删除/以错误的方式修改它。

    4. 搜索元素时,请不要简单地重试6次。出于以下几个原因,这是错误的:

      • 它为您的代码带来了一个神奇的数字。 Magic numbers are bad.
      • 显然是任意选择的,没有任何评论解释为什么它确实是6。
      • 即使记录该方法重试6次,说“它将重试6次”对用户没有任何说明。
      • 在不同的浏览器或不同负载下的计算机上,它的行为会有所不同。在500 ms搜索时,在快速计算机和Chrome / Firefox上重新搜索6次可能需要10毫秒,但在IE7上通过某些复杂的XPath表达式或在负载较重的计算机上搜索时可能需要30秒,或者在远程机器上。

      考虑使用超时。它可以很容易地记录,在每台计算机/浏览器上表现相同,每个人都理解WebElement element = null; long targetTime = System.currentTimeMillis() + TIMEOUT_TIME; try { while ((element == null) && (System.currentTimeMillis() < targetTime)) { try { element = driver.findElement(By.xpath(xpath)); } catch (NoSuchElementException e) { /* do nothing */ } } } 的含义。编写它的最简单方法是:

      Throwable t
    5. 不要抓住Throwable。如果您确实必须而且您知道原因,请在旁边留言。捕获By.cssSelector()被认为是一种不好的做法。请参阅thisthis。有关详细信息,请参阅the official Oracle Expections tutorial

    6. 您不应在名为grabElementByPureXPath()的方法中使用grabElementByXPathOrCssSelector()搜索元素。方法应该只做它的名字(和文档)建议。删除/更改该代码,或将方法名称更改为Assert.assertTrue(t.getMessage(), false);

    7. 而不是

      Assert.fail(t.getMessage());
      

      你可以使用

      {{1}}

      它更短,更清楚地描述了你的意图。