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

时间:2012-10-05 13:35:38

标签: java selenium-webdriver

如果使用以下代码检查缺席元素:

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

    // here you go, element not found

}

你得到了正确的结果,但由于findElement方法阻止了隐式等待,因此运行时间总是30秒。

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

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

谢谢,

6 个答案:

答案 0 :(得分:3)

即使元素不再存在,上述方法也会等待所提供的时间。我编写了自己的方法,等待元素可见而不存在。他们为我工作。他们在这里:

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

答案 1 :(得分:2)

您可以使用xpath选择器执行此操作。找到你知道应该在那之前的元素,然后使用&#34; follow-sibling&#34;获得下一个元素。类似的东西:

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

然后检查它是否还没有返回&#34; name&#34;一。当然,这只有在有另一个&#34; td&#34;元件。

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

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

答案 2 :(得分:2)

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

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

如果您要将方法转换为此类方法,则可以删除driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);,以便“不”立即找到元素。

希望这有帮助!

答案 3 :(得分:0)

您需要使用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;
        }
    };
}

然后您可以设置Falkenfighter所描述的FluentWait对象,并且:

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

答案 4 :(得分:0)

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

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

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

答案 5 :(得分:0)

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

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

它有助于使我的代码更清晰,更快捷。我希望它也可以帮助其他人。

默认pooling为500 Millis,但可以在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;
    };