2

I have a Selenium WebDriver Page Object Model in C# 6. I am experiencing StaleElementReferenceExceptions while trying to click on ajax-loaded nav bar elements. This is confusing, because I'm using PageFactory, without any [CacheLookup]s.

Here's the code in question. I've tried to simplify to just the important parts. (I actually pass around a Driver, a wrapper around IWebDriver.) MenuBar.SelectEnglish<T>() throws the exception.

public class Tests
{
    [Test]
    public void SelectEnglishTest()
    {
        homePage
            .MenuBar.SelectEnglish<HomePage>();
    }

    // ...
}

public class MenuBar : PageObject
{
    [FindsBy(How = How.CssSelector, Using = "...")]
    private IWebElement Language { get; set; }

    [FindsBy(How = How.CssSelector, Using = "...")]
    private IWebElement English { get; set; }

    public T SelectEnglish<T>() where T : Page
    {
        Language.Click();
        IWait<IWebDriver> wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(5));
        wait.Until(ExpectedConditions.ElementToBeClickable(English));
        English.Click();
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
        return (T)Activator.CreateInstance(typeof(T), Driver);
    }

    // ...
}

public class HomePage : PageObject
{
    public MenuBar MenuBar { get; private set; }

    // ...
}

public class PageObject
{
    protected IWebDriver Driver { get; }

    protected PageObject(IWebDriver driver)
    {
        Driver = driver;
        PageFactory.InitElements(this, new RetryingElementLocator(Driver, TimeSpan.FromSeconds(20)));
    }

    // ...
}

What is causing this error? What can I do about it?

MartinRosenberg
  • 189
  • 2
  • 10
  • You have to "refind" (find again) the `Language` element before using since the DOM changed since the element was initially found. See more at: http://stackoverflow.com/questions/16166261/selenium-webdriver-stale-element-reference-exception. – alecxe Aug 25 '15 at 16:26
  • I know it's an old question but still it's interesting... Which line exactly throws the exception? Can you share `HomePage`s constructor? perhaps you fixed it somehow..? – Moshisho Jan 18 '17 at 13:43

1 Answers1

2

From the docs, Stale Element Reference Exception

A stale element reference exception is thrown in one of two cases, the first being more common than the second:
 - The element has been deleted entirely.
 - The element is no longer attached to the DOM.

Since you mentioned the element is loaded using Ajax, most likely the element changed after your page object fetched it. Just fetch it again or wait for the Ajax to complete before fetching the affected elements.


EDIT 1

Here's some sample code to show how you can fetch an element using a method even while using PageFactory.

using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.PageObjects;
using System;

namespace C_Sharp_Selenium_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            FirefoxDriver driver = new FirefoxDriver();
            driver.Navigate().GoToUrl("http://www.google.com");
            HomePage homePage = new HomePage(driver);
            PageFactory.InitElements(driver, homePage);
            homePage.search("stack overflow");
            homePage.getSearchBox().Clear();
            homePage.getSearchBox().SendKeys("c# pagefactory");
            homePage.getSearchButton().Click();
        }
    }

    public class HomePage
    {
        private By searchBox = By.Id("lst-ib");
        private By searchButton = By.Name("btnG");

        // add other elements in here that use FindsBy() to be loaded using PageFactory.InitElements()

        private IWebDriver driver;

        public void search(String s)
        {
            getSearchBox().SendKeys(s);
            getSearchButton().Click();
        }

        public IWebElement getSearchBox()
        {
            return driver.FindElement(searchBox);
        }

        public IWebElement getSearchButton()
        {
            return driver.FindElement(searchButton);
        }

        public HomePage(IWebDriver driver)
        {
            this.driver = driver;
        }
    }
}
JeffC
  • 22,180
  • 5
  • 32
  • 55
  • I'm using `PageFactory`. I couldn't re-fetch elements manually if I wanted to. – MartinRosenberg Aug 25 '15 at 19:56
  • Sure you can... you just have to create a method to fetch each Ajax element. – JeffC Aug 25 '15 at 21:00
  • How might I do that within `PageFactory`? – MartinRosenberg Aug 25 '15 at 21:01
  • Added some sample code to demonstrate how to create a method that scrapes an element off the page. This is not ideal but, in your case, it sounds like it's one of the only options since you have Ajax driven elements. I'm not super strong in C# so there may be a better way to accomplish this same task. – JeffC Aug 25 '15 at 22:39
  • 3
    Having the locators in multiple places defeats the purpose of the Page Object Model, though. – MartinRosenberg Aug 25 '15 at 22:41
  • You are correct... I was playing around with the PageFactory to remember how it works and meant to remove the `FindsBy()`s. Fixed now. – JeffC Aug 25 '15 at 22:47
  • 1
    I'd like to avoid using multiple design patterns for the same things. – MartinRosenberg Aug 26 '15 at 15:26