t.me/atinfo_chat Telegram группа по автоматизации тестирования

Ошибка StaleElementReferenceException при проверке элемента

Позаимствовал следующий код для проверки логина:

@BeforeClass
	public static void init(){
		app = new ApplicationManager();		
	}
	
	@Test
	public void testA() throws Exception{
		User user = new User().setLogin("admin").setPassword("admin");
		app.getUserHelper().loginAs(user);	
		assertTrue(!app.getUserHelper().isLoggin());		
		app.getNavigationHelper().openKaraoke();
		
	}

Выдается ошибка при выполнении метода isElementPresent

public boolean isLoggin(){	
		return isElementPresent(pageManager.loginPage.getButtonAutorization());
	}
***
protected boolean isElementPresent(By by) {	
		driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
	    try {	    	
	    	driver.findElement(by).isEnabled(); //Ошибка
	      driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
	      return true;
	    } catch (NoSuchElementException e) {
	    	driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
	      return false;
	    }
	 }
***

Сама ошибка выдается следующая:

org.openqa.selenium.StaleElementReferenceException: Element not found in the cache - perhaps the page has changed since it was looked up
Command duration or timeout: 1.13 seconds
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.44.0', revision: '76d78cf323ce037c5f92db6c1bba601c2ac43ad8', time: '2014-10-23 13:11:40'
System info: host: 'sml-pc-017', ip: '10.65.5.147', os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.8.0_20'
Driver info: org.openqa.selenium.firefox.FirefoxDriver
Capabilities [{applicationCacheEnabled=true, rotatable=false, handlesAlerts=true, databaseEnabled=true, version=34.0.5, platform=WINDOWS, nativeEvents=false, acceptSslCerts=true, webStorageEnabled=true, locationContextEnabled=true, browserName=firefox, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true}]
Session ID: 98d17d7f-d3ff-4d4c-bf77-769d6f7b36db
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
	at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:204)
	at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:156)
	at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:599)
	at org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:268)
	at org.openqa.selenium.remote.RemoteWebElement.isEnabled(RemoteWebElement.java:142)
	at com.sl.applogic.DriverBasedHelper.isElementPresent(DriverBasedHelper.java:28)
	at com.sl.applogic.UserHelper.isLoggin(UserHelper.java:21)
	at com.sl.test.ExampleTest.testA(ExampleTest.java:33)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.openqa.selenium.StaleElementReferenceException: Element not found in the cache - perhaps the page has changed since it was looked up
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.44.0', revision: '76d78cf323ce037c5f92db6c1bba601c2ac43ad8', time: '2014-10-23 13:11:40'
System info: host: 'sml-pc-017', ip: '10.65.5.147', os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.8.0_20'
Driver info: driver.version: unknown
	at <anonymous class>.fxdriver.cache.getElementAt(resource://fxdriver/modules/web-element-cache.js:8329:1)
	at <anonymous class>.Utils.getElementAt(file:///C:/Users/DGRIGO~1/AppData/Local/Temp/anonymous1485809659092331065webdriver-profile/extensions/fxdriver@googlecode.com/components/command-processor.js:7922:10)
	at <anonymous class>.WebElement.isElementEnabled(file:///C:/Users/DGRIGO~1/AppData/Local/Temp/anonymous1485809659092331065webdriver-profile/extensions/fxdriver@googlecode.com/components/command-processor.js:11112:11)
	at <anonymous class>.DelayedCommand.prototype.executeInternal_/h(file:///C:/Users/DGRIGO~1/AppData/Local/Temp/anonymous1485809659092331065webdriver-profile/extensions/fxdriver@googlecode.com/components/command-processor.js:11635:16)
	at <anonymous class>.fxdriver.Timer.prototype.setTimeout/<.notify(file:///C:/Users/DGRIGO~1/AppData/Local/Temp/anonymous1485809659092331065webdriver-profile/extensions/fxdriver@googlecode.com/components/command-processor.js:548:5)

Что происходит в этом месте и как с этим справится? Спасибо.

The element has been deleted entirely.The element is no longer attached to the DOM.

Вы что хотите получить то ? Если падающий тест то добавьте
добавить еще catch (StaleElementReferenceException e) {
return false
}

и это driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); лучше в finally вынести

Я проверяю существует ли кнопка логина на странице.

У вас случаем не меняеться локатор этой кнопки при наведение скажем на неё ?

Нет

Попробуйте вызывать isElementPresent метод рекурсивно при поимке StaleElementReferenceException:

catch (StaleElementReferenceException e) {
isElementPresent(by)
}

Название isElementPresent не совсем соответствует содержимому метода. Так все же, задача - проверить присутствие элемента, либо активность? В случае присутствия нужно убрать isEnabled. Причем, иногда полезно еще чуть подождать элемент, особенно динамический:

wait.until(ExpectedConditions.presenceOfElementLocated(locator));

где wait - объект WebDriverWait.

Если бросит timeout exception - элемент так и не появился за выделенный интервал времени.

П.С. Неявные ожидания, как правило, задаются лишь раз, после инициализации драйвера. Смысла туда-сюда их гонять нет никакого (за редким исключением). Подобные проблемы хорошо решаются при помощи явных ожиданий.

Изначально была задумка проверять по кнопке авторизации где находится пользователь. То есть если локатор находится в DOM, то залогиниться, если нет, то продолжить работу. Попробую без isEnabled

Вы можете еще воспользоваться вот таким подходом . Я использовал его как то на проекте где джаваскрипт иногда долго отрабатывал и по этому приходилось по несколько раз перепроверять есть ли элемент или его нету.

Почему не воспользоваться кастомным матчером при помощи уже готовых решений селениума? Достаточно создать класс, реализующий ExpectedCondition интерфейс. К слову, это можно сделать и при помощи анонимного класса прямо при вызове метода until у WebDriverWait. Что-то типа следующего:

wait.until(new ExpectedCondition<Boolean>() {
    public Boolean apply(final WebDriver dirver) {
        return (Boolean) executeJS("return jQuery.active == 0");
    }
});

где executeJS - обертка над стандартным JSExecutor’ом, а wait - инстанс WebDriverWait.

Да можно так. Мне еще рекомендовали вот такое подход . Я посоветовал свой первый подход так как его использовал на проекте и для меня это работало. Оно не только для джаваскрипта годится но и вообще для случаев когда приложение может “тупить” и отдавать элемент быстрее чем у findElement бросает exception.

Ну так представленный выше подход тоже не только для JS годится. :wink: Уже есть много готовых ExpectedConditions на все случаи жизни. Если же чего-то не хватает, то вам достаточно переопределить метод apply и задать кастомное условие. :blush:

П.С. Я к тому, что подобные проблемы можно решить гораздо быстрее, значительно сократив объем кода.

Вопрос : почему final - dirver может где-то измениться ?

Потому что нам привили эту привычку. :blush:

У нас на проекте в code style rules входные final аргументы - обязательное условие.
В основном, это избавляет программиста от случайных перезатираний ссылок, забытых this + использование параметров для анонимных классов предполагает наличие final keyword.

В данном конкретном примере final не обязателен. Just to follow code style policies. :smile:

StaleElementReferenceException из-за того что после логина инстанс app стал недействительным. Перед проверкой просто обновите app.Примерно так:

app.getUserHelper().loginAs(user);
init();
assertTrue(!app.getUserHelper().isLoggin());

Думаю должно помочь в этой ситуации.

1 Симпатия