1

好的!所以我在 Windows 8 上使用 selenium chrome 驱动程序(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;
}

<------------------------下面的异常----------- ---------------------->

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)

/ * ** * ** * ***---------------------** * ** * ** * ** * ** * ** * ** * * /


4

1 回答 1

6

编辑-您在此答案下方的评论以及我对此的回答更接近您最初的要求:

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

真的,它与隐式等待无关。当元素被搜索时,它被找到了!然后发生了一些变化,该元素被卸载并可能再次加载,但您无法确定该元素在哪里。考虑一下:

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

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

最合理的事情可能是重新找到所有元素并再次从中取出第三个元素。但是页面显然以某种方式发生了变化(因为元素已过时),事实上,我们的元素现在不是第三个,而是第四个。Selenium 不可能知道这一点。如何克服这一点?


原答案:

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

grabElementByPureXPath()找到并返回正确的元素。稍后,当您尝试click()找到 Selenium throws 时找到的元素时StaleElementReferenceException

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

  1. 您单击某个会异步加载新页面或至少更改它的内容。
  2. 您立即(在页面加载完成之前)通过...搜索元素,grabElementByPureXPath()然后找到并存储它!
  3. 页面最终卸载并加载新页面。
  4. 您尝试使用click()以前找到的元素,但现在它已经过时了,即使新页面也包含相同的元素。但从 Selenium 的角度来看,这只是原始元素的同卵双胞胎,而不是原始元素本身。

隐式等待与此无关 - 元素已找到但现在不再存在。Selenium 可能尝试使用传递的原始对象再次搜索元素By,但它故意不这样做

您可以在此处尝试我的答案中的一种解决方案:如何解决,过时元素异常?如果元素不再附加到 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.

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

    catch(StaleElementReferenceException e){}
        attempts++;
    

    如果我没有寻找这样的陷阱,我可能会认为attempts增量只发生在StaleElementReferenceException或类似的东西上。上面代码的通常和更易读的形式是:

    catch (StaleElementReferenceException e)
    {
        // do nothing
    }
    attempts++;
    

    注释你的空代码块也消除了有人删除你的代码的风险。如果这不是一个try-catch块,而是一个没有代码的无参数构造函数,我强烈建议您在此处发表评论以解释为什么必须在此处明确说明构造函数(有时必须)。否则,迟早有人会以错误的方式删除/修改它。

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

    • 它为您的代码带来了一个神奇的数字。魔术数字很糟糕。
    • 它显然是任意选择的,没有评论解释为什么它是 6。
    • 即使该方法被记录为重试 6 次,但说“它将重试 6 次”对用户没有任何意义。
    • 它在不同的浏览器或不同负载的计算机上会表现不同。在快速计算机和 Chrome/Firefox 上搜索时,重新查找 6 次可能需要 10 毫秒id,但在 IE7 上通过一些复杂的 XPath 表达式搜索或在负载较重的计算机上或在远程计算机上搜索时可能需要 30 秒。

    考虑改用超时。它可以很容易地记录在案,在每台计算机/浏览器上的行为都相同,并且每个人都明白这500 ms意味着什么。最简单的写法是这样的:

    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 */ }
        }
    }
    
  5. 不抓Throwable t。如果您真的必须这样做并且您知道原因,请在旁边留言。捕捉 aThrowable被认为是一种不好的做法。见这个这个。有关详细信息,请参阅Oracle 官方Expections教程

  6. 您不应该By.cssSelector()在名为grabElementByPureXPath(). 一个方法应该只做它的名字(和文档)所暗示的事情。删除/更改该代码,或将方法名称更改为grabElementByXPathOrCssSelector().

  7. 代替

    Assert.assertTrue(t.getMessage(), false);
    

    您可以使用

    Assert.fail(t.getMessage());
    

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

于 2013-09-29T15:29:54.903 回答