web-dev-qa-db-ja.com

Selenium WebDriver古い要素の参照例外を解決する方法

Selenium 2 Web Driverテストには次のコードがあります。これはデバッグ時に機能しますが、ビルドで実行するとほとんどの場合失敗します。私はそれがページが更新されない方法に関係しているに違いないが、それを解決する方法がわからないので、私が間違ったことについてのポインタが高く評価されていることを知っています。 WebアプリケーションフレームワークとしてJSF primefacesを使用しています。 [新規追加]リンクをクリックすると、ポップアップダイアログボックスが表示され、入力ボックスに日付を入力して[保存]をクリックできます。入力要素を取得してテキストを入力すると、古い要素参照例外が発生します。

前もって感謝します

import static org.junit.Assert.assertEquals;

 import Java.util.HashMap;
import Java.util.List;
import Java.util.Map;

import org.junit.Test;
import org.openqa.Selenium.By;
import org.openqa.Selenium.StaleElementReferenceException;
import org.openqa.Selenium.WebDriver;
import org.openqa.Selenium.WebElement;
import org.openqa.Selenium.chrome.ChromeDriver;
import org.openqa.Selenium.support.ui.ExpectedCondition;
import org.openqa.Selenium.support.ui.WebDriverWait;


public class EnterActiveSubmissionIntegrationTest {
Map<String, Map<String, String>> tableData = new HashMap<String, Map<String, String>>();

@Test
public void testEnterActiveSubmission() throws Exception {
    // Create a new instance of the Firefox driver
    // Notice that the remainder of the code relies on the interface, 
    // not the implementation.
    System.setProperty("webdriver.chrome.driver", "C:/apps/chromedriver.exe");
    WebDriver driver = new ChromeDriver();

    // And now use this to visit Google
    driver.get("http://localhost:8080/strfingerprinting");
    // Alternatively the same thing can be done like this
    // driver.navigate().to("http://www.google.com");

    // Find the text input element by its name
    WebElement element = driver.findElement(By.linkText("Manage Submissions"));
    element.click();
    parseTableData(driver, "form:submissionDataTable_data", 1);
    assertEquals(tableData.get("form:submissionDataTable_data").get("12"), "Archived");

    WebElement newElement = driver.findElement(By.linkText("Add new"));
    newElement.click();

    WebDriverWait wait = new WebDriverWait(driver,10);
    wait.until(new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver driver) {
            WebElement button = driver.findElement(By
                    .name("createForm:dateInput_input"));

            if (button.isDisplayed())
                return true;
            else
                return false;

        }
    });

    WebElement textElement = driver.findElement(By.name("createForm:dateInput_input"));
    textElement.sendKeys("24/04/2013");
    WebElement saveElement = driver.findElement(By.name("createForm:saveButton"));
    saveElement.click();

    driver.navigate().refresh();

    parseTableData(driver, "form:submissionDataTable_data", 2);

    //Close the browser
    driver.quit();
}



private void parseTableData(WebDriver driver, String id, int expectedRows) {
    // Check the title of the page or expected element on page
    WebElement subTableElement = driver.findElement(By.id(id));
    List<WebElement> tr_collection=subTableElement.findElements(By.xpath("id('"+ id + "')/tr"));

    assertEquals("incorrect number of rows returned", expectedRows, tr_collection.size());
    int row_num,col_num;
    row_num=1;

    if(tableData.get(id) == null) {
        tableData.put(id, new HashMap<String, String>());
    }
    Map<String, String> subTable = tableData.get(id);
    for(WebElement trElement : tr_collection)
    {
        List<WebElement> td_collection=trElement.findElements(By.xpath("td"));
        col_num=1;
        for(WebElement tdElement : td_collection)
        {
            subTable.put(row_num + "" + col_num, tdElement.getText());
            col_num++;
        }
        row_num++;
    }
}
}

これを実行すると、次の例外が発生しますが、

WebElement textElement = driver.findElement(By.name("createForm:dateInput_input")); 

または

if (button.isDisplayed())

例外トレース

org.openqa.Selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
(Session info: chrome=26.0.1410.64)
  (Driver info: chromedriver=0.8,platform=Windows NT 6.0 SP2 x86) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 56 milliseconds
For documentation on this error, please visit:        http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.32.0', revision: '6c40c187d01409a5dc3b7f8251859150c8af0bcb', time: '2013-04-09 10:39:28'
System info: os.name: 'Windows Vista', os.Arch: 'x86', os.version: '6.0', Java.version: '1.6.0_10'
Session ID: 784c53b99ad83c44d089fd04e9a42904
Driver info: org.openqa.Selenium.chrome.ChromeDriver
Capabilities [{platform=XP, acceptSslCerts=true, javascriptEnabled=true,   browserName=chrome, rotatable=false, driverVersion=0.8, locationContextEnabled=true,  version=26.0.1410.64, cssSelectorsEnabled=true, databaseEnabled=true, handlesAlerts=true,  browserConnectionEnabled=false, nativeEvents=true, webStorageEnabled=true,   applicationCacheEnabled=false, takesScreenshot=true}]
at Sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at  Sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.Java:39)
at  Sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.Java:27)
at Java.lang.reflect.Constructor.newInstance(Constructor.Java:513)
at org.openqa.Selenium.remote.ErrorHandler.createThrowable(ErrorHandler.Java:187)
at org.openqa.Selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.Java:145)
at org.openqa.Selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.Java:554)
at org.openqa.Selenium.remote.RemoteWebElement.execute(RemoteWebElement.Java:268)
at org.openqa.Selenium.remote.RemoteWebElement.isDisplayed(RemoteWebElement.Java:320)
at com.integration.web.EnterActiveSubmissionIntegrationTest$1.apply(EnterActiveSubmissionIntegrationTest.Java:58)
at com.integration.web.EnterActiveSubmissionIntegrationTest$1.apply(EnterActiveSubmissionIntegrationTest.Java:1)
at org.openqa.Selenium.support.ui.FluentWait.until(FluentWait.Java:208)
at com.integration.web.EnterActiveSubmissionIntegrationTest.testEnterActiveSubmission(EnterActiveSubmissionIntegrationTest.Java:53)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
33
user1107753

まず、WebElementとは何かを明確にします。

WebElementは、DOM内の要素への参照です。

対話していた要素が破棄されてから再作成されると、StaleElementExceptionがスローされます。最近のほとんどの複雑なWebページは、ユーザーが操作するときにその場で動き回るので、DOM内の要素を破棄して再作成する必要があります。

これが発生すると、以前に持っていたDOM内の要素への参照は古くなり、この参照を使用してDOM内の要素とやり取りすることはできなくなります。これが発生した場合は、参照を更新する必要があります。または、実際には、要素を再度見つけます。

55
Ardesco

これは問題ではありません。 .findElement呼び出しをtry-catchブロックでラップしてStaleElementReferenceExceptionをキャッチすると、成功するまで必要な回数だけループして再試行できます。

私が書いたいくつかの例 です。

Selenide プロジェクトの別の例:

public static final Condition hidden = new Condition("hidden", true) {
    @Override
    public boolean apply(WebElement element) {
      try {
        return !element.isDisplayed();
      } catch (StaleElementReferenceException elementHasDisappeared) {
        return true;
      }
    }
  };
20
djangofan

私に起こっていたことは、webdriverがDOM要素への参照を見つけ、その参照が取得された後のある時点で、javascriptがその要素を削除して再追加することでした(基本的にページが再描画を行っていたため)。

これを試して。 DOMからdom要素を削除するアクションを見つけます。私の場合、これは非同期ajax呼び出しであり、ajax呼び出しが完了するとDOMから要素が削除されていました。そのアクションの直後に、要素が古くなるのを待ちます。

... do a thing, possibly async, that should remove the element from the DOM ...
wait.until(ExpectedConditions.stalenessOf(theElement));

この時点で、要素が古くなっていることを確認できます。そのため、次に要素を参照するときは、もう一度待機します。今回は、DOMに再び追加されるのを待機します。

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("whatever")))
17
eeeeaaii

このような要素を待ってみてください:

// Waiting 30 seconds for an element to be present on the page, checking
// for its presence once every 5 seconds.
Wait<WebDriver> stubbornWait = new FluentWait<WebDriver>(driver)
    .withTimeout(30, SECONDS)
    .pollingEvery(5, SECONDS)
    .ignoring(NoSuchElementException.class)
    .ignoring(StaleElementReferenceException.class);

WebElement foo = stubbornWait.until(new Function<WebDriver, WebElement>() {
    public WebElement apply(WebDriver driver) {
        return driver.findElement(By.id("foo"));
    }
});
9

古い要素の2つの理由

  1. WebDriverでWebElementとして参照されるWebページで見つかった要素は、WebElementが古くなるDOMが(おそらくJavaScript関数のために)変更されます。

  2. 要素は完全に削除されました。

停止したWebElement [上記のいずれか]と対話しようとすると、StaleElementExceptionがスローされます。

古い例外を回避/解決する方法?

  1. 参照ではなく要素へのロケーターの保存
driver = webdriver.Firefox();
driver.get("http://www.github.com");
search_input = lambda: driver.find_element_by_name('q');
search_input().send_keys('hello world\n'); 
time.sleep(5);


search_input().send_keys('hello frank\n') // no stale element exception
  1. 使用されるJSライブラリのフックを利用する
   # Using Jquery queue to get animation queue length.
    animationQueueIs = """
    return $.queue( $("#%s")[0], "fx").length;
    """ % element_id
    wait_until(lambda: self.driver.execute_script(animationQueueIs)==0)
  1. アクションをJavaScriptインジェクションに移動する
 self.driver.execute_script("$(\"li:contains('Narendra')\").click()");
  1. 要素が古くなるのを積極的に待つ
  # Wait till the element goes stale, this means the list has updated
  wait_until(lambda: is_element_stale(old_link_reference))

私のために働いたこのソリューションは、あなたがあなたのために働いた追加のシナリオがあればここで言及しました、そして以下にコメントします

8
NarendraC

StaleElementReferenceExceptionは、findelementメソッドによってアクセスされる要素が利用できないためです。

要素で操作を実行する前に確認する必要があります(その要素の可用性に疑問がある場合)

要素の可視性を待っている

(new WebDriverWait(driver, 10)).until(new ExpectedCondition()
    {
           public Boolean apply(WebDriver d) {
              return d.findElement(By.name("createForm:dateInput_input")).isDisplayed();
     }});

または、 this ロジックを使用して、要素が存在するかどうかを確認します。

4
Santoshsarma

Seleniumが提供する期待条件を使用して、WebElementを待ちます。

デバッグ中、クライアントは単体テストまたはMavenビルドを実行する場合ほど高速ではありません。これは、デバッグモードでは、クライアントが要素を準備する時間があることを意味しますが、ビルドが同じコードを実行している場合、彼ははるかに速く、探しているWebElementはページのDOMに表示されない可能性があります。

これで私を信じて、私は同じ問題を抱えていた。

例えば:

inClient.waitUntil(ExpectedConditions.visibilityOf(YourElement,2000))

この簡単なメソッド呼び出しは、DOM上のWebElementの可視性に関する2秒間の呼び出しの後に待機します。

2
user2440872

次のコードでこの問題を解決しました。

public WebElement waitForElement(final By findBy, final int waitTime) {
    Wait<AppiumDriver> wait = new FluentWait<>((AppiumDriver) driver)
            .withTimeout(waitTime, TimeUnit.SECONDS)
            .pollingEvery(POLL_TIME, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class,StaleElementReferenceException.class);

    WebElement webElement = wait.until(new Function<AppiumDriver, WebElement>() {
        @Override
        public WebElement apply(AppiumDriver driver) {
            System.out.println("Trying to find element " + findBy.toString());                
            WebElement element = driver.findElement(findBy);
            return element;
        }
    });
    return webElement;
}
1
Raghu K Nair

古い要素の例外が発生した場合!!

それらのテキストボックス/ボタン/リンクをサポートするライブラリが変更された場合、古い要素の例外が発生する可能性があります。したがって、ライブラリ参照を含むキャッシュに保存された参照は、更新されたライブラリでページが更新されたため、現在古くなっています。

for(int j=0; j<5;j++)
try {
    WebElement elementName=driver.findElement(By.name(“createForm:dateInput_input”));
    break;
} catch(StaleElementReferenceException e){
e.toString();
System.out.println(“Stale element error, trying ::  ” + e.getMessage());
}
elementName.sendKeys(“20/06/2018”);
1
Nag Raj

Selenium WebDriver for StaleElementReferenceExceptionには@CachelookUpを使用しないことをお勧めします。

@FindByアノテーションを使用しており、@CacheLookUpを持っている場合は、コメントアウトしてチェックしてください。

1
Ashish Sharda

これは私のために働いた(ソース ここ ):

 /**
     * Attempts to click on an element multiple times (to avoid stale element
     * exceptions caused by rapid DOM refreshes)
     *
     * @param d
     *            The WebDriver
     * @param by
     *            By element locator
     */
    public static void dependableClick(WebDriver d, By by)
    {
        final int MAXIMUM_WAIT_TIME = 10;
        final int MAX_STALE_ELEMENT_RETRIES = 5;

        WebDriverWait wait = new WebDriverWait(d, MAXIMUM_WAIT_TIME);
        int retries = 0;
        while (true)
        {
            try
            {
                wait.until(ExpectedConditions.elementToBeClickable(by)).click();

                return;
            }
            catch (StaleElementReferenceException e)
            {
                if (retries < MAX_STALE_ELEMENT_RETRIES)
                {
                    retries++;
                    continue;
                }
                else
                {
                    throw e;
                }
            }
        }
    }
0
Curt

このソリューションは私にとってはうまくいきました:

エラー処理関数を追加して再試行

var pollLoop = function () {
      element(by.id('spinnerElem')).getAttribute('class').then(function (cls) {
                if (cls.indexOf('spinner-active') > -1) {
                    // wait for the spinner
                } else {
                    //do your logic
                    promise.defer().fulfill();
                }
            }, function () {
                // This err handling function is to handle the {StaleElementReferenceError} and makes sure we find the element always.
                pollLoop();
            });
        };
0
Ashok M A

@djangofanの回答を参照すると、実行可能な解決策は、コードがtry catchブロック内に保持されている可能性があります。以下のコードを使用すると、いつでも問題が発生しませんでした。

public void inputName(String name)
{
    try {
        waitForVisibilityElement(name);//My own visibility function
        findElement(By.name("customerName")).sendKeys(name);
    }
    catch (StaleElementReferenceException e)
    {
        e.getMessage();
    }
}

ExpectedConditions.presenceOfElementLocated(By)を使用しようとしましたが、古い例外が断続的にスローされます。

このソリューションが役立つことを願っています。

0
vkrams

新しいchrome拡張機能をダウンロードし、Seleniumサーバー3を使用するだけで正常に機能します。

0
virender rana

私の場合、このエラーは、ActionChains要素を

def parse(self, response):

seleniumとScrapyの組み合わせを使用する場合の方法、例:

動作しません:

class MySpider(scrapy.Spider):
     action_chains = ActionChains(self.driver)

action_chains = ActionChains(self.driver)内でdef parse(self, response):を移動すると、問題が解決しました。例:

作品:

def parse(self, response):
     self.driver.get(response.url)
     action_chains = ActionChains(self.driver)
0
Ben Wilson

ForループEXのtry catchブロックで、ExpectedConditionでwebdriverwaitを使用します:for python

for i in range(4):
    try:
        element = WebDriverWait(driver, 120).until( \
                EC.presence_of_element_located((By.XPATH, 'xpath')))
        element.click()    
        break
    except StaleElementReferenceException:
        print "exception "
0
rajkrish06

上記の提案の多くを試しましたが、最も簡単なものが機能しました。私の場合、Web要素に@CachelookUpを使用すると、古い要素の例外が発生しました。ページを更新した後、要素参照がリロードされず、要素を見つけることができなかったと思います。要素の@CachelookUp行を無効にすると機能しました。

    //Search button
    @FindBy(how=How.XPATH, using =".//input[@value='Search']")  
    //@CachelookUp
    WebElement BtnSearch;
0
Silverbullet

答えがわからない場合は、他人を混同しないでください。エンドユーザーにとっては非常にイライラします。簡単で短い答えは、webdriverで@CacheLookupアノテーションを使用することです。以下のリンクを参照してください。 WebDriverで@CacheLookupはどのように機能しますか?

0
Kishor Gaur

WebDriverは、エレメントが特定され、タイムアウトが10秒後になるまで待機する必要があります。

WebElement myDynamicElement1 = new WebDriverWait(driver, 10).until(
    ExpectedConditions.presenceOfElementLocated(
        By.name("createForm:dateInput_input")
    )
);
0
Wanping Qu

問題を詳細に調査した結果、Bootstrapのみに追加されたDIV要素を選択するとエラーが発生することがわかりました。 ChromeブラウザーはそのようなDIVSを削除し、エラーが発生します。ステップダウンして、エラーを修正するための実際の要素を選択するだけで十分です。たとえば、私のモーダルダイアログの構造は次のとおりです。

<div class="modal-content" uib-modal-transclude="">
    <div class="modal-header">
        ...
    </div>
    <div class="modal-body">
        <form class="form-horizontal ...">
            ...
        </form>
    <div>
<div>

div class = "modal-body"を選択するとエラーが生成され、form ...を選択すると正常に機能します。

0
user1920925

古い要素参照を回避するために私が見つけた最良の方法は、PageFactoryを使用せず、代わりにロケーター(つまり要素ごと)を保存することです。

public class WebDriverFactory {

    // if you want to multithread tests, use a ThreadLocal<WebDriver> 
    // instead.
    // This also makes it so you don't have to pass around WebDriver objects
    // when instantiating new Page classes
    private static WebDriver driver = null;

    public static WebDriver getDriver() {
       return driver;
    }
    public static void setDriver(WebDriver browser)  {
       driver = browser;
    }       
}

// class to let me avoid typing out the lengthy driver.findElement(s) so 
// much
public Abstract class PageBase {
    private WebDriver driver = WebDriverFactory.getDriver();

    // using var args to let you easily chain locators
    protected By getBy(By... locator) {
      return new ByChained(locator);
    }

    protected WebElement find(By... locators) {
      return driver.findElement(getBy(locators));
    }

    protected List<WebElement> findEm(By... locators) {
      return driver.findElements(getBy(locators));
    }

    protected Select select(By... locators) {
      return new Select(getBy(locators));
    }
}

public class somePage extends PageBase {
  private static WebDriver driver = WebDriverFactory.getDriver();
  private static final By buttonBy = By.cssSelector(".btn-primary");

  public void clickButton() {
     WebDriverWait wait = new WebDriverWait(driver, 10);
     wait.until(ExpectedConditions.elementToBeClickable(buttonBy));
     find(buttonBy).click();
  }

}

使用する静的なWebDriverWaitメソッドでいっぱいのクラスがあります。そして、上記のWebDriver waitの使用がStaleElement例外を処理するかどうかを覚えていません。そうでない場合は、DjangoFanの答えのように、代わりに流waitな待機を使用できます。しかし、私が表示している原則は機能します(WebDriverWaitの特定の行が破裂した場合でも)。

Tldr;

  1. ロケーター、およびWebDriverWait/Fluent wait /要素の組み合わせを使用して自分で要素を再配置します。したがって、要素が古くなった場合、@FindBy(pagefactoryで初期化された要素)でロケーターを複製することなく要素を再配置できますWebElement.relocate()メソッドはありません。
  2. 生活を簡素化するために、要素/要素のリストを見つけるための便利なメソッドを備えたAbstract BasePageクラスを用意します。
0
James Affleck