java Selenium 2 - 如何在隐式等待时检查元素是否不存在?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12747444/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 10:03:09  来源:igfitidea点击:

Selenium 2 - How to check if element not present while implicitly waiting?

javaselenium-webdriver

提问by Marco Bolis

If you check absent elements with the following code:

如果您使用以下代码检查不存在的元素:

// ...
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
try {
    driver.findElement(By.cssSelector("td.name"));
} catch (NoSuchElementException e) {

    // here you go, element not found

}

You get right result, but running time is always 30 seconds, due to findElementmethod blocking on the implicit wait.

您得到正确的结果,但由于findElement隐式等待上的方法阻塞,运行时间始终为 30 秒。

Is there a way to avoid this behavior, while keeping the implicit wait in place?

有没有办法避免这种行为,同时保持隐式等待?

<EDIT>tests are going to be generated through Selenium IDE by non-developers, so I need a solution that keeps their job as simple as possible (that's keeping waits implicit!). </EDIT>

<EDIT>测试将由非开发人员通过 Selenium IDE 生成,因此我需要一个解决方案,使他们的工作尽可能简单(即保持隐式等待!)。 </EDIT>

Thanks,

谢谢,

Marco

马可

回答by Istvan Kis

The methods above wait for the provided amount of time even if the element is not present anymore. I wrote my own methods for waiting until element is visible and not present. They work for me. Here they are:

即使元素不再存在,上述方法也会等待提供的时间量。我编写了自己的方法来等待元素可见且不存在。他们对我来说有效。他们来了:

public void waitUntilElementExists(By by, int waitSeconds,
        int noOfRetries) {
    getDriver().manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
    boolean foundElement = false;
    for (int i = 0; i < noOfRetries; i++)
        try {
            getDriver().findElement(by);
            foundElement = true;
            break;
        } catch (Exception e) {
        }
    assertTrue("The searched element was not found after " + noOfRetries * waitSeconds + " seconds!", foundElement);
}

public void waitUntilElementDoesntExist(By by, int waitSeconds,
        int noOfRetries) {
    getDriver().manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
    boolean elementDisappeared = false;
    for (int i = 0; i < noOfRetries; i++)
        try {
            getDriver().findElement(by);
            waitABit(1000 * waitSeconds);
        } catch (Exception e) {
            elementDisappeared = true;
            break;
        }
    assertTrue("The searched element did not disappear after " + noOfRetries * waitSeconds + " seconds!", elementDisappeared);
}

回答by Nick Wilson

You might be able to do it with xpath selectors. Find the element just before it that you know should be there, then use "following-sibling" to get the next element. Something like:

您也许可以使用 xpath 选择器来实现。找到您知道应该在它之前的元素,然后使用“following-sibling”来获取下一个元素。就像是:

//td.previous/following-sibling::td

Then check to see that it hasn't returned the "name" one. Of course that would only work if there is another "td" element.

然后检查它是否没有返回“名称”。当然,这只有在有另一个“td”元素时才有效。

Personally I'd be tempted to drop the implicit waits and just use waits when they are required.

就我个人而言,我很想放弃隐式等待,只在需要时使用等待。

private WebElement cssWait( final String css )
{
    return new WebDriverWait( driver, 30 ).until( new ExpectedCondition< WebElement >()
    {
        @Override
        public WebElement apply( WebDriver d )
        {
            return d.findElement( By.cssSelector( css ) );
        }
    } );
}

回答by Falkenfighter

Instead of setting up timeouts I use fluentWait which were introduced in 2.25.

我没有设置超时,而是使用 fluentWait,它是在 2.25 中引入的。

public void waitForElement(WebDriver driver, final String xpath)
{
 //Set up fluentWait to wait for 35 seconds polling every 1
 Wait<WebDriver> fluentWait = new FluentWait<WebDriver>(driver)
     .withTimeout(35, TimeUnit.SECONDS)
     .pollingEvery(1, TimeUnit.SECONDS)
     .ignoring(NoSuchElementException.class);

 WebElement element;

 //Look for element, if not found start fluentWait
 try
 {
     element = driver.findElement(By.xpath(xpath));
 }
 catch (WebDriverException e)
 {
     logger.info("[getElementByXpath] Element not initially found. Starting fluentWait ["+xpath+"]");

     try
     {
         element = fluentWait.until(new Function<WebDriver, WebElement>() {
             public WebElement apply(WebDriver d) {

                 return d.findElement(By.xpath(xpath));
             }
         });
     }
     catch (WebDriverException f)
     {
         logger.info("[getElementByXpath] FluentWait findElement threw exception:\n\n" + f +"\n\n");

         throw new WebDriverException("Unable to find element ["+xpath+"]");
     }
 }

 //Once we've found the element wait for element to become visible
 fluentWait.until(ExpectedConditions.visibilityOf(element));
}

If you were to convert your methods to something like this, you would be able to remove your driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);allowing you to 'Not' find an element instantly.

如果您要将您的方法转换为这样的方法,您将能够删除driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);允许您立即“不”找到元素的选项。

Hope this helps!

希望这可以帮助!

回答by Tim Band

You need a function like this, that uses findElements, not findElement:

你需要一个这样的函数,它使用findElements,而不是findElement

public static ExpectedCondition<Boolean> elementCountIs(final By sel, final int count) {
    return new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver driver) {
            return driver.findElements(sel).size() == count;
        }
    };
}

Then you can set up a FluentWaitobject as described by Falkenfighter and:

然后,您可以FluentWait按照 Falkenfighter 的描述设置一个对象,然后:

fluentWait.until(elementCountIs(By.cssSelector("td.name"), 0);

回答by lvanzyl

You'll have to update the ImplicitWait temporarily, and reset it after you're done.

您必须暂时更新 ImplicitWait,并在完成后重置它。

This is the way we've handled this situation - save the current default, update the ImplicitWait temporarily, then change back to the default afterwards.
This is based off Mozilla's suggestion, which is how they handle this situation where you are expecting something to notbe present: https://blog.mozilla.org/webqa/2012/07/12/webdrivers-implicit-wait-and-deleting-elements/

这是我们处理这种情况的方式 - 保存当前默认值,临时更新 ImplicitWait,然后更改回默认值。
这是基于 Mozilla 的建议,这就是他们如何处理您期望某些东西存在的情况:https: //blog.mozilla.org/webqa/2012/07/12/webdrivers-implicit-wait-and-删除元素/

public bool ElementExists(By by, int waitMilliseconds)
{
        var defaultWebDriverTimeout = 30000;// Get default timeout that you're using
        WebDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(waitMilliseconds));

        try
        {
            WebDriver.FindElement(by); //Note could be FindElements instead, but this is quicker
            return true;
        }
        catch (NoSuchElementException)
        {
            return false;
        }
        finally
        {
            WebDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(defaultWebDriverTimeout));
        }
}

回答by cbaldan

No, you cannot. implicitwait time will have precedence over explicit waits. If your implicittime is 30s, any finds you run will at least 30s in case the element is not present. What you could do is manipulate the implicitwait time on your framework, but not sure how that goes with the IDE, I've never worked with it.

你不能。 implicit等待时间将优先于显式等待。如果您的implicit时间是 30 秒,那么在元素不存在的情况下,您运行的任何发现至少需要 30 秒。您可以做的是操纵implicit框架上的等待时间,但不确定 IDE 的情况如何,我从未使用过它。

I created a custom method that returns a booleanwith the result. The input is any By locatorsupported by WebDriver (CSS, xpath, etc). Or, you can modify it as you like.

我创建了一个返回boolean结果的自定义方法。输入是WebDriver(CSS、xpath 等)支持的任何By locator。或者,您可以根据需要对其进行修改。

It helped to make my code cleaner and faster. I hope it helps other people too.

它有助于使我的代码更清晰、更快。我希望它也能帮助其他人。

The default poolingis 500 Millis, but it can be changed on the waitobject.

默认pooling值为 500 毫秒,但可以在wait对象上更改。

public boolean isElementNotPresent(final By locator) {
        boolean result = false;
        // use your custom timeout here
        long timeout = ConfigurationProvider.getWebDriverWaitTimeout();

        // log4j used
        msg = "isElementNotPresent: " + locator;
        LOG.info(msg);

        Wait<WebDriver> wait = new FluentWait<WebDriver>(
                getDriver()).withTimeout(timeout, TimeUnit.SECONDS);

        try {
            result = wait.until(new Function<WebDriver, Boolean>() {
                @Override
                public Boolean apply(WebDriver driver) {
                    return driver.findElements(locator).size() == 0;
                }
            });
        } catch (TimeoutException e) {
            msg = String.format("Element remained visible after %.2f seconds",
            ((float) timeout / 1000));
            LOG.debug(msg);
        } catch (Exception e) {
            msg = "Exception at isElementNotPresent()\n" + e.getMessage();
            // I use jUnit to fail my test
            Assert.fail(msg);
        }

        return result;
    };