134 votes

Comment éviter "StaleElementReferenceException" dans Selenium ?

J'implémente beaucoup de tests Selenium en utilisant Java. Parfois, mes tests échouent à cause d'un problème de sécurité. StaleElementReferenceException .

Pourriez-vous suggérer quelques approches pour rendre les tests plus stables ?

5voto

cezarypiatek Points 659

Dans mon projet, j'ai introduit une notion de StableWebElement. C'est un wrapper pour WebElement qui est capable de détecter si un élément est Stale et de trouver une nouvelle référence à l'élément original. J'ai ajouté une méthode d'aide à la localisation des éléments qui renvoie StableWebElement au lieu de WebElement et le problème de StaleElementReference a disparu.

public static IStableWebElement FindStableElement(this ISearchContext context, By by)
{
    var element = context.FindElement(by);
    return new StableWebElement(context, element, by, SearchApproachType.First);
} 

Le code en C# est disponible sur la page de mon projet mais il pourrait être facilement porté en java. https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/StableWebElement.cs

0voto

George Kargakis Points 968

Une solution en C# serait :

Classe d'aide :

internal class DriverHelper
{

    private IWebDriver Driver { get; set; }
    private WebDriverWait Wait { get; set; }

    public DriverHelper(string driverUrl, int timeoutInSeconds)
    {
        Driver = new ChromeDriver();
        Driver.Url = driverUrl;
        Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeoutInSeconds));
    }

    internal bool ClickElement(string cssSelector)
    {
        //Find the element
        IWebElement element = Wait.Until(d=>ExpectedConditions.ElementIsVisible(By.CssSelector(cssSelector)))(Driver);
        return Wait.Until(c => ClickElement(element, cssSelector));
    }

    private bool ClickElement(IWebElement element, string cssSelector)
    {
        try
        {
            //Check if element is still included in the dom
            //If the element has changed a the OpenQA.Selenium.StaleElementReferenceException is thrown.
            bool isDisplayed = element.Displayed;

            element.Click();
            return true;
        }
        catch (StaleElementReferenceException)
        {
            //wait until the element is visible again
            element = Wait.Until(d => ExpectedConditions.ElementIsVisible(By.CssSelector(cssSelector)))(Driver);
            return ClickElement(element, cssSelector);
        }
        catch (Exception)
        {
            return false;
        }
    }
}

Invocation :

        DriverHelper driverHelper = new DriverHelper("http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp", 10);
        driverHelper.ClickElement("input[value='csharp']:first-child");

De même, on peut l'utiliser pour Java.

0voto

vaibhavcool20 Points 464

La solution de Kenny est dépréciée, utilisez ceci, j'utilise la classe actions pour le double clic mais vous pouvez faire n'importe quoi.

new FluentWait<>(driver).withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
                    .ignoring(StaleElementReferenceException.class)
                    .until(new Function() {

                    @Override
                    public Object apply(Object arg0) {
                        WebElement e = driver.findelement(By.xpath(locatorKey));
                        Actions action = new Actions(driver);
                        action.moveToElement(e).doubleClick().perform();
                        return true;
                    }
                });

0voto

Gryu Points 1692

J'ai trouvé la solution aquí . Dans mon cas, l'élément devient inaccessible si l'on quitte la fenêtre, l'onglet ou la page en cours et que l'on y revient.

.ignoring(StaleElement...), .refreshed(...) et elementToBeClicable(...) n'ont pas aidé et je recevais une exception sur act.doubleClick(element).build().perform(); chaîne.

Utilisation de la fonction dans ma classe de test principale :

openForm(someXpath);

Ma fonction BaseTest :

int defaultTime = 15;

boolean openForm(String myXpath) throws Exception {
    int count = 0;
    boolean clicked = false;
    while (count < 4 || !clicked) {
        try {
            WebElement element = getWebElClickable(myXpath,defaultTime);
            act.doubleClick(element).build().perform();
            clicked = true;
            print("Element have been clicked!");
            break;
        } catch (StaleElementReferenceException sere) {
            sere.toString();
            print("Trying to recover from: "+sere.getMessage());
            count=count+1;
        }
    }

Ma fonction BaseClass :

protected WebElement getWebElClickable(String xpath, int waitSeconds) {
        wait = new WebDriverWait(driver, waitSeconds);
        return wait.ignoring(StaleElementReferenceException.class).until(
                ExpectedConditions.refreshed(ExpectedConditions.elementToBeClickable(By.xpath(xpath))));
    }

0voto

Josh Pinter Points 3814

Nettoyer findByAndroidId qui gère de manière élégante StaleElementReference .

C'est fortement basé sur La réponse de jspcal mais j'ai dû modifier cette réponse pour qu'elle fonctionne proprement avec notre installation et je voulais donc l'ajouter ici au cas où elle serait utile à d'autres. Si cette réponse vous a aidé, merci d'aller voter pour elle. La réponse de jspcal .

// This loops gracefully handles StateElementReference errors and retries up to 10 times. These can occur when an element, like a modal or notification, is no longer available.
export async function findByAndroidId( id, { assert = wd.asserters.isDisplayed, timeout = 10000, interval = 100 } = {} ) {
  MAX_ATTEMPTS = 10;
  let attempt = 0;

  while( attempt < MAX_ATTEMPTS ) {
    try {
      return await this.waitForElementById( `android:id/${ id }`, assert, timeout, interval );
    }
    catch ( error ) {
      if ( error.message.includes( "StaleElementReference" ) )
        attempt++;
      else
        throw error; // Re-throws the error so the test fails as normal if the assertion fails.
    }
  }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X