【问题标题】:Selenium WebDriver reliable testsSelenium WebDriver 可靠测试
【发布时间】:2017-06-08 15:51:30
【问题描述】:

我知道这个问题以前被问过很多次,但我仍然找不到适合我的解决方案。当我大多数时候使用 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']")));

}

谢谢!

【问题讨论】:

  • 如果您正在搜索具有 Id 的元素,为什么要使用 xpath?这似乎很麻烦,因为您可以选择“By.id()”。
  • 是的,你是对的。我也尝试了“By.id()”(实际上,这是我现在使用的),但仍然没有明显的改进。
  • 页面上是否存在任何 JavaScript 错误?另外,您的应用程序中是否有任何动态更改的 id 或路径?
  • 没有 JavaScript 错误,也没有动态更改的 ID 或路径。

标签: selenium selenium-rc selenium-webdriver


【解决方案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(); 

【讨论】:

    【解决方案2】:

    如果您正确处理异常,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;
    }
    

    【讨论】:

      【解决方案3】:

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

      您的选择器错误

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

      元素不存在

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

      元素迟到了

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

      【讨论】:

        【解决方案4】:

        我在使用带有 C# 的 WebDriver 时遇到了相同类型的问题。 关于如何在测试中避免(不是完全,而是最小化)NoSuchElementException,我可以提出 2 种不同的方法:

        1. 首先你应该弄清楚你的应用程序是如何工作的——它是否使用了大量的 Ajax 和其他异步。请求/响应。然后,您可以对无法一次定位的每个元素使用显式等待。

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

        【讨论】:

          【解决方案5】:

          您是否尝试过逐个元素地捕捉,而无需所有这些等待和等待。

          只需喜欢: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")));
          }
          

          【讨论】:

          • 这是我尝试的第一件事。不幸的是,我无法删除我的 html。
          • 好的,您是否使用这种方式捕获了一些元素,或者每个元素都会引发异常?
          • 我设法以这种方式捕捉了一些元素。问题是,如果 5 次中有 3 次失败,则测试不可靠。
          • 你使用其他测试框架吗?像 Arquilian 无人机。您是否尝试通过 cssSelector 获取您的元素?
          • 不,我不使用其他框架。我还尝试使用“By.cssSelector()”获取我的元素。
          【解决方案6】:

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

          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

          【讨论】:

            猜你喜欢
            • 2012-12-21
            • 1970-01-01
            • 1970-01-01
            • 2020-05-24
            • 2014-07-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多