Selenium WebDriver可靠的测试

时间:2013-04-04 12:38:05

标签: selenium selenium-rc selenium-webdriver

我知道此问题之前曾多次被问过,但我仍然找不到适用于我的解决方案。当我使用Selenium WebDriver运行我的测试时,大多数情况下它们会因“NoSuchElementException”而失败。我尝试使用Explicit和Implicit Waits,但似乎没有任何效果。那么,除了使用Waits之外还有其他方法可以使我的测试更可靠吗?

我在FirefoxDriver中使用selenium-java-2.31.0。下面是我尝试使测试更可靠的一些代码示例:

public void waitAndClickElement(WebDriver driver, final By selector) {
        Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
                .withTimeout(50, TimeUnit.SECONDS)
                .pollingEvery(5, TimeUnit.SECONDS)
                .ignoring(NoSuchElementException.class);
        WebElement elementToClick = wait
                .until(new Function<WebDriver, WebElement>() {
                    public WebElement apply(WebDriver driver) {
                        return driver.findElement(selector);
                    }

                });
        waitForElementVisible(driver, selector);
        elementToClick.click();
         }

..而且:

public WebElement waitForElementPresent(WebDriver driver, final By selector){
    Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
            .withTimeout(70, TimeUnit.SECONDS)
            .pollingEvery(5, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class);
    WebElement elementToClick = wait
            .until(new Function<WebDriver, WebElement>() {
                public WebElement apply(WebDriver driver) {
                    return driver.findElement(selector);
                }
            });
    return elementToClick;

    } 

......而且这个:

WebDriverWait wait = new WebDriverWait(driver, 50);
WebElement user_name = wait.until(visibilityOfElementLocated(By.xpath("//*@id='userName']")));

......而且这个:

driver.manage().timeouts().implicitlyWait(50, TimeUnit.SECONDS);

...最后我尝试使其中一项测试变得更可靠:

@Test
public void test1{
 waitAndClickElement(driver, By.xpath("//*[@id='linkLogIn']"));
        waitForElementPresent(driver, By.xpath("//*[@id='userName']")).sendKeys("name");
        waitForElementPresent(driver, By.xpath("//*[@id='inputEmail']")).sendKeys("email@gmail.com");
        waitForElementPresent(driver,By.xpath("//*[@id='resetPassword']")).click();
        assertTrue(isElementPresent(By.xpath("//*[@id='moduleMain']")));

}

谢谢!

6 个答案:

答案 0 :(得分:1)

尝试以下自定义方法。它对我来说很好,

public boolean waitForElementToBePresent(By by, int waitInMilliSeconds) throws Exception
    {
        WebDriver driver = getDriver();
        int wait = waitInMilliSeconds;
        int iterations  = (wait/250);
        long startmilliSec = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++)
        {
            if((System.currentTimeMillis()-startmilliSec)>wait)
                return false;
            List<WebElement> elements = driver.findElements(by);
            if (elements != null && elements.size() > 0)
                return true;
            Thread.sleep(250);
        }
        return false;
    }

使用它,

waitForElementToBePresent(By.id("linkLogIn", 5000);
driver.findElement(By.id("linkLogIn")).click(); 

答案 1 :(得分:1)

如果您正确处理异常,WebDriver非常稳定。问题是,ExpectedConditions类的方法不会为您处理异常,尽管大多数人会像回复那样回复您的问题。

如果需要,您可以尝试我的方法。此方法返回0到90秒,具体取决于方案。您可能更喜欢稍微改变这种方法,但它应该有效。这里的重要概念是:

1. Use the new FluentWait class with the .ignoring method (or .ignoreAll() ).
2. Use findElement() BUT make sure you catch (and nicely handle) the possible   
    exceptions (that you are ignoring in the wait).
3. Use a loop to retry after exceptions but govern that by either time or
    # of tries.

代码:

public WebElement getElementByLocator( final By locator ) {
  LOGGER.info( "Get element by locator: " + locator.toString() );  
  final long startTime = System.currentTimeMillis();
  Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
    .withTimeout(30, TimeUnit.SECONDS)
    .pollingEvery(5, TimeUnit.SECONDS)
    .ignoring( NoSuchElementException.class ) 
    .ignoring( StaleElementReferenceException.class ) ;
  int tries = 0;
  boolean found = false;
  WebElement we = null;
  while ( (System.currentTimeMillis() - startTime) < 91000 ) {
   LOGGER.info( "Searching for element. Try number " + (tries++) ); 
   try {
    we = wait.until( ExpectedConditions.visibilityOfElementLocated( locator ) );
    found = true;
    break;
   } catch ( StaleElementReferenceException e ) {      
    LOGGER.info( "Stale element: \n" + e.getMessage() + "\n");
   } catch ( NoSuchElementException nse ) {      
    LOGGER.info( "No such element: \n" + nse.getMessage() + "\n");
   }
  }
  long endTime = System.currentTimeMillis();
  long totalTime = endTime - startTime;
  if ( found ) {
   LOGGER.info("Found element after waiting for " + totalTime + " mill." );
  } else {
   LOGGER.info( "Failed to find element after " + totalTime + " mill." );
  }
  return we;
}

答案 2 :(得分:1)

运行findElement时,如果找不到,则会出现错误。出现这种情况有三个原因之一:

您的选择器错误

如果选择器出错,最好先进行调试,直到到达那个位置并暂停测试执行。然后使用控制台找出正确的选择器以找到元素。

元素不存在

您可能会在第一次操作中注意到您正在寻找的元素实际上并不存在。在这种情况下,找出你处于错误状态的原因并修复它。如果你期望元素不在那里,Here is a great C# example关于如何扩展你的IWebElement对象以允许.Exists()方法。

元素迟到

确定元素是否迟到很容易。正常运行测试一次,然后以调试模式运行,逐步手动执行每个步骤。如果在手动步骤工作时您的正常测试运行失败,则表示您发现了问题。通常,问题是由于页面加载时没有发生AJAX加载。在这些情况下,一个好的webdev通常会添加一些您可以轻松搜索的微调器图像。我创建了一个名为WaitForPageLoad()的辅助方法,它首先等待页面加载,然后验证微调器不存在,然后再次等待页面加载完成。您希望2页加载等待,因为模式将旋转然后加载,而新的页面加载将加载然后旋转。最后,页面完成,您的元素将出现。

答案 3 :(得分:0)

您是否尝试逐个元素地捕获,而不是所有的等待和wait.until?

就像:WebElement username = driver.findelement(By.id("userName"));

顺便问一下你可以放弃你的HTML吗?

编辑:

我可以建议的是:

protected void sleep(int i) {
driver.manage().timeouts().implicitlyWait(i, TimeUnit.SECONDS); 
}

@test
void test(){
driver.findElement(By.id("linkLogIn")).click(); sleep(6);
driver.findElement(By.id("userName")).sendKeys("user"); sleep(1);
driver.findElement(By.id("inputEmail")).sendKeys("mail@gmail.com"); sleep(1);
driver.findElement(By.id("resetPassword")).click(); sleep(10);
Assert.assertTrue(isElementPresent(By.id("moduleMain")));
}

答案 4 :(得分:0)

我在使用WebDriver和C#时遇到了同样的问题。 关于如何在测试中避免(不完全,但最小化)NoSuchElementException,我可以提出两种不同的方法:

  1. 首先,您应该弄清楚您的应用程序是如何工作的 - 它是否使用了大量的Ajax和其他异步。请求/响应。然后,您可以使用显式等待每个元素,这些元素不能立即定位。

  2. 您可以基于Selenium WebDriver WebElement类编写自己的WebElement类实现。 主要想法 - 每次你将使用你的webelement它将重新定位 - 所以你不会担心NoSuchElement或StaleElementException。

答案 5 :(得分:-1)

那么你的代码告诉我你只是等到元素存在。

waitForElementPresent(driver, By.xpath("//*[@id='userName']")).sendKeys("name");
waitForElementPresent(driver, By.xpath("//*[@id='inputEmail']")).sendKeys("email@gmail.com");

它告诉我你没有点击该字段,然后使用sendkeys输入文本。 如何添加点击

 waitForElementPresent(driver, By.xpath("//*[@id='userName']"));
driver.findElement(by.id ="userName").click(); 
driver.findElement(by.id ="userName").sendKeys("name"); 

问题是鼠标专注于webdriver,需要关注适当的字段AFAIK