5

我知道这个问题以前被问过很多次,但我仍然找不到适合我的解决方案。当我大多数时候使用 Selenium WebDriver 运行测试时,它们会因“NoSuchElementException”而失败。我尝试使用显式和隐式等待,但似乎没有任何效果。那么,除了使用 Waits 之外,还有其他方法可以让我的测试更可靠吗?

我将 selenium-java-2.31.0 与 FirefoxDriver 一起使用。下面是一些我试图让我的测试更可靠的代码示例:

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']")));

}

谢谢!

4

6 回答 6

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(); 
于 2013-04-05T14:57:50.620 回答
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;
}
于 2013-04-05T16:25:48.533 回答
1

当你运行 findElement 时,如果找不到它,你会得到一个错误。发生这种情况的三个原因之一:

您的选择器错误

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

元素不存在

在您的第一个操作中,您可能会注意到您正在寻找的元素实际上并不存在。在这种情况下,找出你处于错误状态的原因并修复它。如果您希望该元素不存在,这里有一个很棒的 C# 示例,说明如何扩展您的 IWebElement 对象以允许使用 .Exists() 方法。

元素晚了

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

于 2013-08-07T20:15:44.920 回答
0

I have faced with the same type of problem, while using WebDriver with C#. I can propose 2 different ways on how you can avoid(not completely, but minimize) NoSuchElementException in your tests:

  1. First of all you should figure out how your application works - does it use a lot of Ajax and other asynch. requests/responses. Then you can use explicit wait for every element, which can not be located at once.

  2. You can write your own implementation of WebElement class based on Selenium WebDriver WebElement class. Main idea - everytime you will use your webelement it will relocated - so you will not be worry about NoSuchElement or StaleElementException.

于 2013-04-04T13:48:49.770 回答
0

您是否尝试在没有所有这些等待和等待的情况下逐个元素地捕捉元素?

就像: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")));
}
于 2013-04-04T13:45:59.827 回答
-1

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

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

它没有告诉我您单击该字段,然后使用发送键输入文本。如何添加点击

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

问题是鼠标焦点在webdriver上,它需要集中在适当的字段AFAIK

于 2013-04-08T10:08:40.320 回答