ไม่พบองค์ประกอบข้อผิดพลาด StaleElementReference ในแคช

ฉันใช้ Capybara 2.1 กับ Ruby 1.9.3 โดยใช้ไดรเวอร์ซีลีเนียม (พร้อม Minitest และ Test Unit) เพื่อทดสอบเว็บแอป

ฉันกำลังดิ้นรนกับปัญหา StaleElementReferenceException ฉันได้เห็นการอภิปรายในหัวข้อนี้มาหลายครั้งแล้ว แต่ยังไม่สามารถหาวิธีแก้ไขปัญหาที่ฉันกำลังเผชิญอยู่ได้

โดยพื้นฐานแล้ว ฉันกำลังพยายามค้นหาองค์ประกอบการแบ่งหน้าทั้งหมดบนหน้าของฉันโดยใช้โค้ดนี้:

pagination_elements = page.all('.pagination a')

จากนั้นฉันก็ยืนยันองค์ประกอบเหล่านั้นเช่น:

pagination_elements.first.must_have_content('1')

หลังจากการยืนยันเหล่านั้น ฉันกำลังทดสอบต่อโดยคลิกที่ลิงก์หน้าถัดไป เพื่อให้แน่ใจว่าองค์ประกอบการแบ่งหน้าแรกในอนาคตของฉันจะเป็นหน้าก่อนหน้า เมื่อต้องการทำเช่นนั้น ฉันกำลังเรียกข้อมูลองค์ประกอบการแบ่งหน้าอีกครั้ง:

new_pagination_elements = page.all('.pagination a')

และ Stale Error ก็เกิดขึ้นที่นี่ เพราะฉันเข้าถึงองค์ประกอบที่ฉันได้เข้าถึงแล้ว ( นี่คือข้อผิดพลาด )

คุณสามารถดูสถานะลิงก์ได้ที่นี่

ฉันไม่รู้จริงๆ ว่าจะทำให้การทดสอบทั่วไปนี้ทำงานได้อย่างถูกต้องได้อย่างไร คุณมีเคล็ดลับสำหรับวิธีที่ดีกว่าในการเข้าถึงองค์ประกอบการแบ่งหน้าของฉันหรือไม่?


person Evers    schedule 27.08.2013    source แหล่งที่มา
comment
โปรดดู github.com/jnicklas/capybara/issues/843   -  person Rajarshi Das    schedule 27.08.2013
comment
@RajarshiDas ฉันได้อ่านหัวข้อนี้และหัวข้อที่เกี่ยวข้องแล้ว แต่เป็นปัญหาเก่ามากจาก Capybara เวอร์ชันก่อนหน้าและไม่ได้ช่วยแก้ไขปัญหาของฉัน   -  person Evers    schedule 27.08.2013


คำตอบ (5)


บางครั้งฉันมีปัญหากับเพจที่ใช้ AJAX มาก ในกรณีของฉันวิธีแก้ปัญหานี้สามารถแก้ไขได้:

begin
  ...
rescue Selenium::WebDriver::Error::StaleElementReferenceError
  sleep 1
  retry
end
person ejosafat    schedule 27.08.2013

ฉันเห็นข้อความหลักในส่วนสำคัญคือ:

Element not found in the cache - 
perhaps the page has changed since it was looked up

ฉันมีกรณีที่คล้ายกันมาก่อน มีสองวิธีแก้ไข:

  1. เพิ่ม page.reload ก่อนตรวจสอบสิ่งเดียวกันในหน้าใหม่ หากคุณตั้งค่า Capybara.automatic_reload = false ใน spec_helper

  2. find องค์ประกอบพิเศษในหน้าใหม่ซึ่งหน้าก่อนหน้าไม่มี เอฟเฟกต์นี้เทียบเท่ากับการรอ

อีกวิธีหนึ่งคือการใช้ตัวเลือกเฉพาะ ตัวอย่างเช่นแทนที่จะเป็น

pagination_elements = page.all('.pagination a')

ใช้

pagination_elements = page.all('#post_123 .pagination a')

เพิ่มพื้นที่รหัสเฉพาะต่อท้ายตัวเลือกและคุณไม่ควรประสบปัญหาดังกล่าว

person Billy Chan    schedule 27.08.2013
comment
ฉันต้องการหลีกเลี่ยงการใช้ page.reload เนื่องจากการทดสอบการรวมระบบของฉันเป็นไปตามโฟลว์ของผู้ใช้ และการโหลดหน้าเว็บซ้ำไม่ได้เป็นส่วนหนึ่งของการทดสอบ - person Evers; 27.08.2013
comment
@Evers ไม่มีอะไรผิดปกติที่นี่ การตั้งค่าเริ่มต้นคือ true ดังนั้นทุกการกระทำจะโหลดเนื้อหาซ้ำโดยที่คุณไม่ได้สังเกตเห็น ฉันตั้งค่าเป็น false เสมอเพราะฉันใช้ Javascript เพื่อโต้ตอบบ่อยครั้ง ลืมส่วนนี้ไปได้เลยหากคุณไม่มีการตั้งค่าดังกล่าวใน spec_helper - person Billy Chan; 27.08.2013
comment
แต่การบังคับ reload อาจช่วยได้หากคุณตรวจสอบสิ่งเดียวกันสองครั้ง คุ้มค่าที่จะลอง - person Billy Chan; 27.08.2013
comment
ขอบคุณ! มันใช้งานได้ค่อนข้างดีตอนนี้! การต่อท้าย ID ที่ไม่ซ้ำกันเป็นวิธีที่ดีกว่าในการดำเนินการ การเปิดตัวการทดสอบเฉพาะนี้ 10 ครั้งจะส่งผลให้มีการทดสอบที่ล้มเหลวเพียงครั้งเดียว มันจึงยังไม่แม่นยำ 100% - person Evers; 27.08.2013

ลิงก์ที่น่าสนใจเกี่ยวกับข้อผิดพลาดนี้และวิธีแก้ไข: http://stefan.haflidason.com/testing-with-rails-and-capybara-methods-that-wait-method-that-wont/

person Ben Colon    schedule 19.03.2015

เห็นได้ชัดว่า นอกเหนือจากสภาพการแข่งขันแล้ว ข้อผิดพลาดนี้ยังปรากฏขึ้นเนื่องจากมีการใช้บล็อก within ในทางที่ผิด ตัวอย่างเช่น:

within '.edit_form' do
  click '.edit_button'
  # The error will appear here if the 'edit_button' is not a
  # descendant of the 'edit_form'
end
person art-solopov    schedule 12.04.2016
comment
นั่นเป็นกรณีของฉัน! ฉันถูกขังอยู่ในบล็อก within ซึ่งก่อให้เกิดข้อผิดพลาดมารยาทกับวัตถุเก่าที่อยู่ภายใน - สิ่งที่เหม็นอับไม่ใช่สิ่งที่ฉันเป็น #find'ing แต่เป็นบล็อกนั่นเอง! - person igorsantos07; 26.04.2017

คุณเคยลองใช้ WebDriver โดยตรงแทนที่จะใช้ Capybara หรือไม่? สิ่งนี้อาจทำให้คุณควบคุมได้มากขึ้นว่าเมื่อใดควรและไม่แคชวัตถุ

เช่น. (ขออภัยสำหรับไวยากรณ์ของ Java แต่ควรเข้าใจ)

WebElement searchField = driver.findElement(By.CssSelector("input.foo"));

searchField.click();

searchField.sendKeys("foo foo");

System.out.println(searchField.getText());

//Do something elsewhere on the page which causes html to change (e.g. submit form)

.....
....

//This next line would throw stale object

System.out.println(searchField.getText());

//This line will not throw exception

searchField = driver.findElement(By.CssSelector("input.foo"));

System.out.println(searchField.getText());

การกำหนด "findElement" อีกครั้งให้กับ "searchField" หมายความว่าเราค้นหาองค์ประกอบนั้นอีกครั้ง การรู้ว่าเมื่อใดและเมื่อใดที่จะไม่มอบหมายใหม่เป็นกุญแจสำคัญในการตัดสินใจว่าจะแคชองค์ประกอบเว็บของคุณอย่างไร

ฉันไม่ได้ใช้ Capybara แต่ฉันคิดว่ามันจะซ่อนกลยุทธ์การแคชจากคุณใช่ไหม

person Robbie Wareham    schedule 27.08.2013
comment
เรากำลังใช้ Capybara กับโปรเจ็กต์อื่นๆ ของเราอยู่แล้ว และเราไม่ได้พิจารณาที่จะย้อนกลับไปใช้ Webdriver เลย Capybara ช่วยเราประหยัดเวลาได้มาก แต่คุณพูดถูก capybara มีกลยุทธ์การแคช และอื่นๆ อีกมากมาย :) - person Evers; 27.08.2013
comment
ฉันกำลังจะย้ายไปที่ Ruby และกำลังตัดสินใจว่าจะใช้ไลบรารีใด สิ่งเหล่านี้ทำให้คิดว่าจะใช้ Webdriver โดยตรง ฉันชอบที่จะควบคุมมากเกินไปที่จะใช้บางอย่างเช่น Capybara - person Robbie Wareham; 27.08.2013
comment
ฉันเป็นผู้พิทักษ์ Selenium Webdrivers ที่ยอดเยี่ยม แต่ฉันต้องยอมรับว่า Capybara นั้นยอดเยี่ยมในการรอองค์ประกอบต่างๆ ฯลฯ ... ให้คุณมุ่งเน้นไปที่การทดสอบของคุณ - person Evers; 28.08.2013