Selenium 2 StaleElementReferenceException When Usi

2019-03-30 10:34发布

问题:

I am using Selenium 2 to test an asp.net web forms page using the InternetExplorerDriver and am encountering a StaleElementReferenceException. The page contains a (auto-postback) drop down list, which I am selecting different values from.

Example code:

Page:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication.WebForm1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
  <head runat="server">
    <title></title>
  </head>
  <body>
    <form id="form1" runat="server">
    <div>
      <asp:DropDownList ID="ddl" runat="server" AutoPostBack="true">
        <asp:ListItem Text="one"></asp:ListItem>
        <asp:ListItem Text="two"></asp:ListItem>
      </asp:DropDownList>
    </div>
    </form>
  </body>
</html>

(The code-behind file contains nothing more than the Visual Studio auto-created stuff.)

Test fixture code:

using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;

namespace IntegrationTests
{
  [TestFixture]
  public class WebForm1TestFixture
  {
    [Test]
    public void ShouldSelectItemOneThenItemTwo()
    {
      IWebDriver driver = new InternetExplorerDriver(); // Using ChromeDriver causes test to pass...
      driver.Navigate().GoToUrl("http://localhost/<my-virtual-directory-name>/WebForm1.aspx");
      IWebElement list = driver.FindElement(By.Id("ddl"));
      IWebElement itemOne = list.FindElement(By.XPath("option[1]"));
      itemOne.Select();
      list = driver.FindElement(By.Id("ddl"));
      IWebElement itemTwo = list.FindElement(By.XPath("option[2]"));
      itemTwo.Select();
      list = driver.FindElement(By.Id("ddl"));
      itemOne = list.FindElement(By.XPath("option[1]"));// This line causes the StaleElementReferenceException to occur
      itemOne.Select();

      // Some assertion would go here
    }
  }
}

When I run the test I get the following error:

OpenQA.Selenium.StaleElementReferenceException: Element is no longer valid
at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse) in e:\Projects\WebDriver\trunk\remote\client\src\csharp\webdriver-remote-client\RemoteWebDriver.cs: line 883
at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(DriverCommand driverCommandToExecute, Dictionary`2 parameters) in e:\Projects\WebDriver\trunk\remote\client\src\csharp\webdriver-remote-client\RemoteWebDriver.cs: line 727
at OpenQA.Selenium.Remote.RemoteWebElement.FindElement(String mechanism, String value) in e:\Projects\WebDriver\trunk\remote\client\src\csharp\webdriver-remote-client\RemoteWebElement.cs: line 570
at OpenQA.Selenium.Remote.RemoteWebElement.FindElementByXPath(String xpath) in e:\Projects\WebDriver\trunk\remote\client\src\csharp\webdriver-remote-client\RemoteWebElement.cs: line 458
at OpenQA.Selenium.By.<>c__DisplayClasse.<XPath>b__c(ISearchContext context) in e:\Projects\WebDriver\trunk\common\src\csharp\webdriver-common\By.cs: line 119
at OpenQA.Selenium.By.FindElement(ISearchContext context) in e:\Projects\WebDriver\trunk\common\src\csharp\webdriver-common\By.cs: line 227
at OpenQA.Selenium.Remote.RemoteWebElement.FindElement(By by) in e:\Projects\WebDriver\trunk\remote\client\src\csharp\webdriver-remote-client\RemoteWebElement.cs: line 267
at IntegrationTests.WebForm1TestFixture.ShouldSelectItemOneThenItemTwo() in WebForm1TestFixture.cs: line 25 

If I change the test to use a ChromeDriver then the test passes. It seems to me like this means it is either a problem with the InternetExplorerDriver or the Internet Explorer browser itself. Does anybody know which and if there's anything I can do to get round this (the site will be used in IE by the end users, so changing browsers isn't possible, unfortunately)?


EDIT: The current work-around that I am using is to put a Thread.Sleep() after the list has been selected; this works but obviously isn't an ideal solution.

回答1:

Below is the pattern I ended up implementing.

After each item*.Select() I've implemented a wait:

IWait<IWebDriver> wait = new MyCustomWebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(driver => <some-condition-to-wait-for>);

Where <some-condition-to-wait-for> is a way of determining that the item selection has finished (for example, by checking that another control on the page has been updated, e.g.

wait.Until(driver => driver.FindElement(By.Id("someLabelId")).Text == "expectedValue")`.

MyCustomWebDriverWait is a class that implements IWait<IWebDriver> and is almost identical to the WebDriverWait class, only it catches StaleElementReferenceException as well as NotFoundException (which meant changing the type of lastException from NotFoundException to WebDriverException.

You can read how I was pointed in this direction by Daniel Wagner-Hall on the Selenium-users google group here.



回答2:

The list element could be changing in the DOM due to the autopostback. Try refinding the list element each time you select an option. E.g.

IWebElement itemOne = driver.FindElement(By.Id("ddl")).FindElement(By.XPath("option[1]"));
  itemOne.Select();
  IWebElement itemTwo = driver.FindElement(By.Id("ddl")).FindElement(By.XPath("option[1]"));
  itemTwo.Select();


回答3:

I found calling driver.refresh() after going to the page that it worked fine, my code is:

        Pages.Login.Goto();
        Browser.Refresh(); <-- refresh the webdriver after going to the page
        Pages.Login.LogInAsRegularUser();