0

Dear all I am using Selenium EventFiringWebDriver to record the called web driver methods. I recognised that I often get a "StaleReferenceException" while when I just use the HtmlUnitDriver alone I don't have the issue.

I also recognised that the call i.e. "click()" has been performed in the browser although the "StaleElementReferenceException" got thrown.

Has anybody an idea while the EventFiringWebDriver runs into such problems while using the HtmlUnitDriver or FirefoxDriver standalone not? Could it be that the WebElements get updated by the origin drivers at runtime while the wrapped WebElements of the EventFiringWebDriver not? Or should we raise this as a bug for the EventFiringWebDriver implementation?

Example code with EventFiringWebDriver - throws StaleElementReferenceException

        HtmlUnitDriver driver = new HtmlUnitDriver();
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);

        ExtentReports extent = new ExtentReports ("report.html", true);
        ExtentTest logger = extent.startTest("test");

        EventFiringWebDriver eventDriver = new EventFiringWebDriver(driver);
        eventDriver.register(new MyWebDriverEventListener(logger));

        try {
            WebElement element  = null;
            eventDriver.get("https://www.google.com");
            element = eventDriver.findElement(By.xpath("//input[@type='text']"));
            element.sendKeys("Test");
            element.submit();
            Thread.sleep(2000);
            element = eventDriver.findElement(By.xpath("//div[@id='search']//a"));
            String title = element.getText();
            // HERE the StaleElementReferenceException get thrown ALTHOUGH the "click" event get processed by the browser, it loads already the page
            try {
                element.click();
            } catch(StaleElementReferenceException ex) {
            }
            Thread.sleep(2000);
            Assert.assertEquals(title, eventDriver.getTitle());
            logger.log(LogStatus.PASS,"end","Test passed");
         } catch(AssertionError error) {
            logger.log(LogStatus.FAIL,"end","Test failed:" + error.getMessage());
            throw error;
         }
        finally {
            extent.endTest(logger);
            extent.flush();
            extent.close();
            eventDriver.quit(); 
        }

The same code - just using the HtmlUnitDriver directly, works without any problems

        HtmlUnitDriver driver = new HtmlUnitDriver();
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);

        ExtentReports extent = new ExtentReports ("report.html", true);
        ExtentTest logger = extent.startTest("test");

        try {
            WebElement element  = null;
            driver.get("https://www.google.com");
            element = driver.findElement(By.xpath("//input[@type='text']"));
            element.sendKeys("Test");
            element.submit();
            Thread.sleep(2000);
            element = driver.findElement(By.xpath("//div[@id='search']//a"));
            String title = element.getText();
            element.click();
            Thread.sleep(2000);
            Assert.assertEquals(title, driver.getTitle());
            logger.log(LogStatus.PASS,"end","Test passed");
         } catch(AssertionError error) {
            logger.log(LogStatus.FAIL,"end","Test failed:" + error.getMessage());
            throw error;
         }
        finally {
            extent.endTest(logger);
            extent.flush();
            extent.close();
            driver.quit(); 
        }
megloff
  • 1,400
  • 4
  • 27
  • 44

1 Answers1

0

After studying the stack trace of the stale exception I recognised that the issue comes not directly from the EventFiringWebDriver. It gets thrown by my listener implementation of the WebDriverEventListener while I try to get the tag name of the element after the click has been performed.

For me it looks like that the design of the WebDriverEventListener is not optimal. With other words you may not able to use the passed WebElement in the "afterXXX" methods, otherwise you may risk a stale exception. Instead you should use the "beforeXXX" methods in order to retrieve the details of the elements.

Stacktrace of my StaleElementReferenceException

at org.openqa.selenium.htmlunit.HtmlUnitDriver.assertElementNotStale(HtmlUnitDriver.java:963)
at org.openqa.selenium.htmlunit.HtmlUnitWebElement.assertElementNotStale(HtmlUnitWebElement.java:734)
at org.openqa.selenium.htmlunit.HtmlUnitWebElement.getTagName(HtmlUnitWebElement.java:291)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement$1.invoke(EventFiringWebDriver.java:332)
at com.sun.proxy.$Proxy18.getTagName(Unknown Source)
at ch.megloff.test.SimpleExtentReportWebDriverEventListener.afterClickOn(SimpleExtentReportWebDriverEventListener.java:111)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.openqa.selenium.support.events.EventFiringWebDriver$1.invoke(EventFiringWebDriver.java:81)
at com.sun.proxy.$Proxy16.afterClickOn(Unknown Source)
at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement.click(EventFiringWebDriver.java:346)
..s

Java Code snippet of "getTagName()" of the underlying HtmlUnit Implementation

public String getTagName() {
   assertElementNotStale();
   return element.getNodeName();
} 

My "Error-prone" listener implementation for this "afterClickOn" Method - the "getTagName()" should not be called after a click has been performed

 public class MyWebDriverEventListener extends AbstractWebDriverEventListener {
    ...
    @Override
    public void afterClickOn(WebElement element, WebDriver driver) {
        // bad implementation, click has been already performed 
        // so you may risk to have a stale exception in case the 
        // browser  switched already to the other page (DOM got changed)
        logEvent("Clicked on tag: " + element.getTagName() + " with href: " + element.getAttribute("href"));
    }
 }
megloff
  • 1,400
  • 4
  • 27
  • 44