मैं जावा का उपयोग करके बहुत सारे सेलेनियम परीक्षण लागू कर रहा हूं। कभी-कभी, मेरे परीक्षण असफल हो जाते हैं StaleElementReferenceException
। क्या आप परीक्षणों को अधिक स्थिर बनाने के लिए कुछ दृष्टिकोण सुझा सकते हैं?
मैं जावा का उपयोग करके बहुत सारे सेलेनियम परीक्षण लागू कर रहा हूं। कभी-कभी, मेरे परीक्षण असफल हो जाते हैं StaleElementReferenceException
। क्या आप परीक्षणों को अधिक स्थिर बनाने के लिए कुछ दृष्टिकोण सुझा सकते हैं?
जवाबों:
यह तब हो सकता है जब पृष्ठ पर हो रहा एक DOM ऑपरेशन अस्थायी रूप से तत्व को अप्राप्य बना रहा हो। उन मामलों के लिए अनुमति देने के लिए, आप एक अपवाद को फेंकने से पहले तत्व को कई बार लूप में एक्सेस करने का प्रयास कर सकते हैं।
Darrelgrainger.blogspot.com से इस उत्कृष्ट समाधान की कोशिश करें :
public boolean retryingFindClick(By by) {
boolean result = false;
int attempts = 0;
while(attempts < 2) {
try {
driver.findElement(by).click();
result = true;
break;
} catch(StaleElementException e) {
}
attempts++;
}
return result;
}
मैं इस मुद्दे पर रुक-रुक कर चल रहा था। मेरे लिए जाने-माने, BackboneJS पृष्ठ पर चल रहा था और उस तत्व को प्रतिस्थापित कर रहा था जिस पर मैं क्लिक करने का प्रयास कर रहा था। मेरा कोड इस तरह दिखता था।
driver.findElement(By.id("checkoutLink")).click();
जो बेशक कार्यात्मक रूप से इस के समान है।
WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
checkoutLink.click();
कभी-कभी ऐसा होता था कि जावास्क्रिप्ट चेकआउट तत्व को खोजने और उसे क्लिक करने के बीच में प्रतिस्थापित कर देता था, अर्थात।
WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
// javascript replaces checkoutLink
checkoutLink.click();
लिंक पर क्लिक करने का प्रयास करते समय किसने सही ढंग से StaleElementReferenceException का नेतृत्व किया। मैं वेबड्राइवर को यह बताने के लिए कोई विश्वसनीय तरीका नहीं दे पाया कि जब तक कि जावास्क्रिप्ट चलना शुरू न हो जाए, तब तक यहाँ कैसे मैंने इसे हल किया।
new WebDriverWait(driver, timeout)
.ignoring(StaleElementReferenceException.class)
.until(new Predicate<WebDriver>() {
@Override
public boolean apply(@Nullable WebDriver driver) {
driver.findElement(By.id("checkoutLink")).click();
return true;
}
});
यह कोड लगातार लिंक पर क्लिक करने का प्रयास करेगा, StaleElementReferenceException को अनदेखा करते हुए जब तक कि क्लिक सफल न हो जाए या टाइमआउट न हो जाए। मुझे यह समाधान पसंद है क्योंकि यह आपको किसी भी रिट तर्क को लिखने से बचाता है, और वेबड्राइवर के केवल अंतर्निहित निर्माण का उपयोग करता है।
आम तौर पर यह DOM अपडेटेड होने के कारण होता है और आप अपडेटेड / नए एलिमेंट को एक्सेस करने की कोशिश करते हैं - लेकिन DOM का रिफ्रेशमेंट होता है इसलिए यह एक अमान्य रेफरेंस है ..
अद्यतन पूरा होने के लिए पहले तत्व पर एक स्पष्ट प्रतीक्षा का उपयोग करके इसके चारों ओर प्राप्त करें, फिर तत्व के लिए एक नया संदर्भ फिर से पकड़ो।
यहाँ कुछ छद्म वर्णन करने के लिए कोड (मैं के लिए उपयोग कुछ सी # कोड से अनुकूलित है बिल्कुल इस मुद्दे):
WebDriverWait wait = new WebDriverWait(browser, TimeSpan.FromSeconds(10));
IWebElement aRow = browser.FindElement(By.XPath(SOME XPATH HERE);
IWebElement editLink = aRow.FindElement(By.LinkText("Edit"));
//this Click causes an AJAX call
editLink.Click();
//must first wait for the call to complete
wait.Until(ExpectedConditions.ElementExists(By.XPath(SOME XPATH HERE));
//you've lost the reference to the row; you must grab it again.
aRow = browser.FindElement(By.XPath(SOME XPATH HERE);
//now proceed with asserts or other actions.
उम्मीद है की यह मदद करेगा!
केनी का समाधान अच्छा है, हालांकि इसे अधिक सुरुचिपूर्ण तरीके से लिखा जा सकता है
new WebDriverWait(driver, timeout)
.ignoring(StaleElementReferenceException.class)
.until((WebDriver d) -> {
d.findElement(By.id("checkoutLink")).click();
return true;
});
या यह भी:
new WebDriverWait(driver, timeout).ignoring(StaleElementReferenceException.class).until(ExpectedConditions.elementToBeClickable(By.id("checkoutLink")));
driver.findElement(By.id("checkoutLink")).click();
लेकिन वैसे भी, सबसे अच्छा समाधान सेलेनाइड पुस्तकालय पर भरोसा करना है, यह इस तरह की चीजों को संभालता है और अधिक। (तत्व संदर्भों के बजाय यह परदे के पीछे संभालता है ताकि आपको कभी भी बासी तत्वों से निपटना न पड़े, जो काफी मुश्किल हो सकता है)। selenide
ऐसा क्यों StaleElementReferenceException
होता है इसका कारण पहले से ही निर्धारित किया गया है: तत्व के साथ कुछ खोजने और करने के बीच डोम को अपडेट।
क्लिक-प्रॉब्लम के लिए मैंने हाल ही में इस तरह से एक समाधान का उपयोग किया है:
public void clickOn(By locator, WebDriver driver, int timeout)
{
final WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.until(ExpectedConditions.refreshed(
ExpectedConditions.elementToBeClickable(locator)));
driver.findElement(locator).click();
}
महत्वपूर्ण हिस्सा सेलेनियम के खुद के ExpectedConditions
माध्यम से "चेनिंग" है ExpectedConditions.refreshed()
। यह वास्तव में प्रतीक्षा करता है और जांचता है कि क्या प्रश्न में तत्व निर्दिष्ट समय के दौरान ताज़ा किया गया है और अतिरिक्त रूप से क्लिक करने योग्य तत्व के लिए प्रतीक्षा करता है।
ताज़ा विधि के लिए प्रलेखन पर एक नज़र डालें ।
अपने प्रोजेक्ट में मैंने StableWebElement की एक धारणा पेश की। यह WebElement के लिए एक आवरण है जो यह पता लगाने में सक्षम है कि क्या तत्व बासी है और मूल तत्व का एक नया संदर्भ ढूंढता है। मैंने उन सहायक तत्वों को खोजने के लिए एक सहायक विधि जोड़ी है, जो WebElement के बजाय StableWebElement लौटाते हैं और StaleElementReference के साथ समस्या गायब हो जाती है।
public static IStableWebElement FindStableElement(this ISearchContext context, By by)
{
var element = context.FindElement(by);
return new StableWebElement(context, element, by, SearchApproachType.First);
}
C # का कोड मेरे प्रोजेक्ट के पेज पर उपलब्ध है, लेकिन इसे आसानी से java https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SelenbUtils/StableWebElement.cs पर पोर्ट किया जा सकता है
C # में एक समाधान होगा:
सहायक वर्ग:
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;
}
}
}
मंगलाचरण:
DriverHelper driverHelper = new DriverHelper("http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp", 10);
driverHelper.ClickElement("input[value='csharp']:first-child");
इसी तरह जावा के लिए इस्तेमाल किया जा सकता है।
केनी के समाधान का उपयोग नहीं किया गया है, मैं एक्शन क्लास का उपयोग डबल क्लिक करने के लिए कर रहा हूं लेकिन आप कुछ भी कर सकते हैं।
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;
}
});
findByAndroidId
विधि जो इनायत संभालती है StaleElementReference
।यह भारी रूप से jspcal के उत्तर पर आधारित है, लेकिन मुझे इसे अपने सेटअप के साथ सफाई से काम करने के लिए उस उत्तर को संशोधित करना पड़ा और इसलिए मैं इसे यहाँ जोड़ना चाहता था, यदि यह दूसरों के लिए उपयोगी हो। यदि इस उत्तर ने आपकी मदद की, तो कृपया 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.
}
}
}
यह मेरे लिए काम करता है (100% काम) C # का उपयोग करके
public Boolean RetryingFindClick(IWebElement webElement)
{
Boolean result = false;
int attempts = 0;
while (attempts < 2)
{
try
{
webElement.Click();
result = true;
break;
}
catch (StaleElementReferenceException e)
{
Logging.Text(e.Message);
}
attempts++;
}
return result;
}
समस्या यह है कि जब तक आप जावास्क्रिप्ट से जावा में तत्व को वापस करते हैं, तो यह जावास्क्रिप्ट को वापस डोम को छोड़ सकता है।
जावास्क्रिप्ट में पूरी बात करने की कोशिश करें:
driver.executeScript("document.querySelector('#my_id').click()")
इसे इस्तेमाल करे
while (true) { // loops forever until break
try { // checks code for exceptions
WebElement ele=
(WebElement)wait.until(ExpectedConditions.elementToBeClickable((By.xpath(Xpath))));
break; // if no exceptions breaks out of loop
}
catch (org.openqa.selenium.StaleElementReferenceException e1) {
Thread.sleep(3000); // you can set your value here maybe 2 secs
continue; // continues to loop if exception is found
}
}
मुझे यहाँ समाधान मिल गया है । वर्तमान विंडो, टैब या पृष्ठ छोड़ने और फिर से वापस आने के मामले में मेरे मामले में तत्व अप्राप्य हो जाता है।
.ignoring (StaleElement ...), .refreshed (...) और elementToBeClicable (...) ने मदद नहीं की और मुझे act.doubleClick(element).build().perform();
स्ट्रिंग पर अपवाद मिल रहा था ।
मेरे मुख्य परीक्षण वर्ग में फ़ंक्शन का उपयोग करना:
openForm(someXpath);
मेरा आधार फ़ंक्शन:
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;
}
}
मेरा बेसक्लास फ़ंक्शन:
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))));
}
एक संभावित समस्या हो सकती है जो StaleElementReferenceException की ओर ले जाती है जिसका अब तक किसी ने उल्लेख नहीं किया है (क्रियाओं के संबंध में)।
मैं इसे जावास्क्रिप्ट में समझाता हूं, लेकिन यह जावा में समान है।
यह काम नहीं करेगा:
let actions = driver.actions({ bridge: true })
let a = await driver.findElement(By.css('#a'))
await actions.click(a).perform() // this leads to a DOM change, #b will be removed and added again to the DOM.
let b = await driver.findElement(By.css('#b'))
await actions.click(b).perform()
लेकिन फिर से कार्रवाई शुरू करने से इसे हल हो जाएगा:
let actions = driver.actions({ bridge: true })
let a = await driver.findElement(By.css('#a'))
await actions.click(a).perform() // this leads to a DOM change, #b will be removed and added again to the DOM.
actions = driver.actions({ bridge: true }) // new
let b = await driver.findElement(By.css('#b'))
await actions.click(b).perform()
आमतौर पर StaleElementReferenceException जब हम जिस तत्व तक पहुंचने का प्रयास करते हैं वह प्रकट हुआ है, लेकिन अन्य तत्व उस तत्व की स्थिति को प्रभावित कर सकते हैं, जिससे हम अंतर्विरोधित होते हैं, इसलिए जब हम क्लिक करने या गेटटेक् करने का प्रयास करते हैं या वेबलेमेंट पर कुछ कार्रवाई करने का प्रयास करते हैं तो हमें अपवाद मिलता है: आमतौर पर कहते हैं कि तत्व DOM के साथ संलग्न नहीं है ।
समाधान मैंने कोशिश की इस प्रकार है:
protected void clickOnElement(By by) {
try {
waitForElementToBeClickableBy(by).click();
} catch (StaleElementReferenceException e) {
for (int attempts = 1; attempts < 100; attempts++) {
try {
waitFor(500);
logger.info("Stale element found retrying:" + attempts);
waitForElementToBeClickableBy(by).click();
break;
} catch (StaleElementReferenceException e1) {
logger.info("Stale element found retrying:" + attempts);
}
}
}
protected WebElement waitForElementToBeClickableBy(By by) {
WebDriverWait wait = new WebDriverWait(getDriver(), 10);
return wait.until(ExpectedConditions.elementToBeClickable(by));
}
उपरोक्त कोड में मैं पहले प्रतीक्षा करने की कोशिश करता हूं और फिर तत्व पर क्लिक करता हूं यदि अपवाद होता है तो मैं इसे पकड़ लेता हूं और इसे लूप करने की कोशिश करता हूं क्योंकि ऐसी संभावना है कि अभी भी सभी तत्व लोड नहीं हो सकते हैं और फिर से अपवाद हो सकता है।
हो सकता है कि इसे और अधिक हाल ही में जोड़ा गया था, लेकिन अन्य उत्तर सेलेनियम के अंतर्निहित प्रतीक्षा सुविधा का उल्लेख करने में विफल होते हैं, जो आपके लिए उपरोक्त सभी करता है, और सेलेनियम में बनाया गया है।
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
findElement()
जब तक तत्व नहीं मिला है, या 10 सेकंड के लिए यह फिर से कॉल करेगा ।
स्रोत - http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp