Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

Как правильно оформить ожидание, Webdriver + Python

webium
python
webdriver
Теги: #<Tag:0x00007f7b64b012b0> #<Tag:0x00007f7b64b01170> #<Tag:0x00007f7b64b01030>

(Maxim Aliseiko) #1

Продолжаю изучение webdriver+python, вопрос такой.

Есть страница, определённая как OperatorsPage, в ней определён список операторов вот так(использую webium):

operators_list = Finds(by=By.XPATH, value=’//a[@id=“js_tab_clicked”]’)

На этой странице есть метод

def check_operator_in_list(self, name):
return any(operator.text == name for operator in self.operators_list)

собственно в тесте после добавления нового оператора есть такой кусок:

self.current_page = OperatorsPage(self.driver)
assert self.current_page.check_operator_in_list(operator[‘name’])

Т.е. я возвращаю новый инстанс страницы и хочу проверить появился ли новый оператор в списке, однако ловлю exception:

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

Если руками прикрутить ожидание в секунд 5 типа так

sleep(5)
self.current_page = OperatorsPage(self.driver)
assert self.current_page.check_operator_in_list(operator[‘name’])

То всё проходит ок.

Вопрос - как правильно сделать ожидание страницы или поиск элемента заново?


(Dzmitry Ihnatsyeu) #2

Не совсем понятно, в какой момент вы ищете элемент.
Очевидно, что между этими событиями элемент успевает обновится через ajax. Я рекомендую вам искать элемент прямо перед взаимодействием с ним.
Например, ваш метод проверки можно обернуть в декоратор, который перед этим найдет элемент и отдаст его как параметр вашей фукнции.

Если и это не поможет, то можно реализовать explict_wait: который в случае StaleElementReferenceException будет выполнять поиск и взаимодействовать с элементом:
def explict_wait():
timeout = time.time() + timeout.
while time.time() < timeout:
try:
find_element
do action with element
except StaleElementReferenceException:
pass


(Goshko Nazar) #3

Dzmitry_Ihnatsyeu, зачем что то реализовывать, когда все есть из коробки?
maaxlee, если Вы изучаете driver+python, то и учите его, в первую очередь, а потом возьметесь за фреймворки.

Для того что бы решить вашу задачу, Вы должны перед каждым обращением к элементу, узнать есть ли он на странице. Проще всего это сделать, с помощью конструкции @property, которая будет перед отдачей элемента, искать его на странице. А вообще по хорошему, это делается в базовом классе, с идентификатором поиска (visible, clickable, present) и для всех элементов.
Что касается ожидания, то в selenium есть уже описанные методы поиска по критериям.

from selenium.webdriver.support import expected_conditions as ec
login_but = WebDriverWait(self.driver, 5).until(ec.element_to_be_clickable((By.CSS_SELECTOR, '#header > a')))
login_but.click()

Как реализован seg/get ищите в описаниях python


(rmerkushin) #4

Как уже написали выше, используйте встроенный wait_unitl. Добавлю только что возможно его будет не достаточно в случае ajax подгрузки информации. В таком случае необходимо вставлять топорный time.sleep() или дожидаться завершения ajax запроса. Для jQuery как то так:

def wait_for_ajax(self, xpath, timeout=5):
        wait = WebDriverWait(self.driver, timeout=int(timeout))
        message = "Element '%s' was not visible in %s second(s)." % (xpath, str(timeout))
        wait.until(lambda driver: driver.find_element_by_xpath(xpath).is_displayed()
                                  and driver.execute_script("return $.active") == 0, message=message)

(Maxim Aliseiko) #5

Спасибо за советы, единственный момент который смущает - в моём случает там не webelemet а список, и он может быть просто пустым


(rmerkushin) #6

Так элемент списка тоже веб-элемент. Дожидайтесь появления конкретного элемента и все.