Написание фреймворка, используя Selenium. C чего начать?


(Виталий Коряков) #22

Согласен, но другого способа вытянуть этот поезд я не вижу, тем более, что сравнивать разные поезда смысла мало, так как на них кардинально разные цены. Можно выбирать конечно первый попавшийся поезд, но тут надо уточнять условие задачи.

Спасибо за отзыв, еще буду работать, конечно нужна практика ).
Было бы хорошо услышать еще задачи, на которых можно попрактиковаться,

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

Тут специфика сервиса в том, что нельзя выбрать станцию, если просто ввести ее в поле (даже правильно), ее обязательно надо выбрать из списка предлагаемых станций.

О, отличный способ, переделаю так же. Спасибо за наводку.


(sidelnikovmike) #23

Решил по поводу локаторов немного поисследовать. В отдельной теме напишу.


(Александр Таранков) #24

нормальный способ. Только не список элементов искать и брать первый, а искать конкретно один элемент по локатору.

P.S. для мультиязычных сайтов, для которых локаторы используют хардкод из различных наименований и т.п., имеет смысл выносить локаторы в отдельные файлы-ресурсы и брать из них локаторы независимо от локали по идентификатору типа departure.station.name, как предлагал @dzhariy


(vmaximv) #25

А не надо тут искать смысл, считайте что @dzhariy заказчик хД

Так вы эту еще не сделали :slight_smile:

Приложил задачу к собственному “велосипеду”.
Код специально не усложнял для простоты восприятия, мы же и так все знаем, что локаторы в тестах - это плохо :wink:


(Дмитрий Жарий) #26

@dzhariy аплодирует стоя

Да, это и есть решение. Осталось только на Пайтон переписать :smiley:


(Дмитрий Жарий) #27

@VitaliyKoryakov, вариант предложенный @vmaximv – это действительное решение задачи. По сути, такой сценарий можно запускать каждый день или час и собирать статистику по поездам.
Возьмите формат лог-файла, предложенного @vmaximv как “стандарт”.

В этой теме, мы много чего насоветовали, и боюсь, могли запутать вас кучей советов. Сейчас же, я хочу дать еще одну рекомендацию, при этом, расставив приоритеты для всего списка.

Рекомендации, в порядке убывания важности:

  1. Вынести тестовый код в методы шагов (функциональная декомпозиция)
  2. Добавить хэш locators
  3. Оптимизировать локаторы

Вынести тестовый код в методы шагов (функциональная декомпозиция)

Это очень простой подход, применяя который, вы можете превратить тесты из сценария в бот-стиле:
Click, Wait, SendKeys, Click, Click…

В язык бизнес-действий. Т.е. ваши тесты будут написаны на языке предметной области, DSL.
В таком стиле, тест выглядит как:

select_departure_city(“Киев”)
select_arival_city(“Москва”)
search_trains()
trains_list = get_trains_list()

for (train in trains_list)
{
    train_data = get_train_data(train)
    log(train_data)
}

в таком случае, вам будут не нужны комментарии по типу:
#choose station from Kyiv

давайте разберем этот пример

#choose station from Kyiv
WebDriverWait(driver, 10).until(lambda d: d.find_element_by_name("station_from"))
source_from = driver.find_element_by_name("station_from")
source_from.send_keys(u"Київ")
WebDriverWait(driver, 20).until(lambda d: d.find_element_by_xpath("//*[@id='stations_from']/div[1]"))
kyiv = driver.find_element_by_xpath("//*[@id='stations_from']/div[1]")
kyiv.click()

#choose station to Moscow
WebDriverWait(driver, 10).until(lambda d: d.find_element_by_name("station_till"))
source_to = driver.find_element_by_name("station_till")
source_to.send_keys(u"Москва")
WebDriverWait(driver, 20).until(lambda d: d.find_element_by_xpath("//*[@id='stations_till']/div[5]"))
moscow = driver.find_element_by_xpath("//*[@id='stations_till']/div[5]")
moscow.click()

Первое: берем комментарии, и создаем методы с тем же именем. Город выносим как параметр:


def choose_station_from(CITY)
    WebDriverWait(driver, 10).until(lambda d: d.find_element_by_name("station_from"))
    source_from = driver.find_element_by_name("station_from")
    source_from.send_keys( CITY )
    WebDriverWait(driver, 20).until(lambda d: d.find_element_by_xpath("//*[@id='stations_from']/div[1]"))
    first_item = driver.find_element_by_xpath("//*[@id='stations_from']/div[1]")
    first_item.click()

def choose_station_to(CITY)
	# реализация, аналогична первому. просто выносим City как параметр 

таким образом, просто разбивая простыню теста на логические блоки – отдельные методы/процедуры/функции – вы делаете тест более читабельным.

Добавить хэш locators

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

Оптимизировать локаторы

В данный момент, все работает. Поэтому, поставил этому пункту самый низкий приоритет. Найти минимальный и стабильный локатор – это все таки искусство, и тут требуется практика. Выше, я оставил ссылку на шпаргалку с XPath/CSS локаторами.

Вы можете попробовать и XPath и CSS, а может и ID.
Эта задача самая размытая. И тут важно непосредственно стабильность локатора.
Может быть, у вас уже в первоначальном варианте были стабильные локаторы.
Я не знаю – это покажет время (и будущие изменения).

Обычно, длинные локаторы и локаторы содержащие индексы (например div[5]; div[1] – это нормально) – ломаются чаще всего. Но, это всего-лишь статистика, и неизвестно, насколько это правда для текущей задачи.


(Виталий Коряков) #28

я к этому и хочу прийти, только не так быстро это получается ))

Я на будущее

Тоесть, насколько я понял ищется первое свободное место в первом же поезде?
Ну это проще на самом деле, и мне кажется, что актуальнее собирать не просто статистику, а статистику конкретного поезда с конкретным типом места, тогда будет просматриваться разница в цене. А то сегодня купе - завтра люкс. )))
Мне, как пользователю не совсем удобно.

Если же задача стоит собрать статистику по всем актуальным поездам на текущий момент - да, это уже другая задача )
Более сложная, но более информативная.


(Дмитрий Жарий) #30

Задача тут в том, чтобы создать скрипт, который будет запускатся каждый час и собирать статистику по всем поездам заданного направления.
Таким образом, формируется статистический лог, по которому можно будет определить:

  1. Когда поезд появился в списке
  2. Насколько различается цена на один и тот-же поезд в разные дни недели
  3. Разницу в цене на разные поезда по одному и тому-же направлению
  4. Определить, когда «выбрасываются» не проданные билеты (Если поезд следует по направлению София – Москва через Киев, то вначале по направлению Киев–Москва будет выброшено меньше билетов, за неизвестное время до отправления, будут добавлены дополнительные)

Для того, чтобы создать такой лог, нужно:

  1. Получить список всех поездов
  2. Для каждого поезда из списка, получить все «классы»: Люкс, Купе, Плацкарт. Если мест на Люкс будет 0, то в момент сбора этого пункта не будет в списке. А за 2 часа до отправления, люкс может появится: может быть сдан билет, либо выброшены не проданные.
  3. Для каждого класса – выбрать первой свободное место и добыть цену.

Таким образом, соберется статистический лог, который в дальнейшем можно будет проанализировать специальным парсером. Но, это уже другая задача.
А данная задача – сбор информации по всем поездам заданного направления в момент запуска скрипта


(Виталий Коряков) #31

Вот только дошли руки для продолжения этой темы ). Смена работы и обязанностей не дали уделить внимание данной задаче. Но я про нее не забыл.

Взялся переписывать задачу - и сразу возникла проблема.
Город вводится вебрдайвером в поле настолько быстро, что поиск в pop-up окне не успевает сработать корректно, что затрудняет реализовать правильный выбор города из pop-up, а другого способа выбрать город на сайте нет. Если вводить город руками - срабатывает ок.
Вот скрины:

Ввод Вебдрайвером:

Ввод вручную:

Как видим - разная отработка pop-up-а. Как решить проблему с вводом в вебрдайвере, что бы окно успевало сработать корректно?


(Виталий Коряков) #32

В примере, данным @vmaximv реализован интересный подход работы со ячейками таблицы:

String trainN = results.getCell("№ поезда", i).getText();
String direction = results.getCell("Откуда / Куда", i).getText();

Это готовые методы для работы с ячейкаи под Джаву, или собственная реализация, и есть ли что то подобное для Питона?


(vmaximv) #33

Конечно собственная.


(Виталий Коряков) #34

Тогда как вы вытаскиваете саму таблицу для удобного парсинга - непонятно.


(vmaximv) #35

Конкретно в этом случае все ищется через локаторы


(Виталий Коряков) #36

Это понятно, что через локаторы. Достучаться до каждого элемента отдельно - не проблема, типа номер поезда:

"//*[@id='ts_res_tbl']/tbody/tr[1]/td[1]"

Но таблица меняется постоянно, и как вытащить элементы, не зная количество поездов в ней - пока не пойму как.


(Дмитрий Жарий) #37

Тут секрет в том, что у WebElement тоже есть метод findElements

Так что сначала находите все элементы tr, а потом для каждого tr’а – td


(Виталий Коряков) #38

Да, я уже сам разобрался, спасибо )
Уже на финишной прямой.


(Виталий Коряков) #39

В общем вариант решения есть, но с кодировкой загвоздка. Получаю отчет в током виде:

2014-01-14 11:17:02.472000	[u'042 \u041a', u'Kyiv-Pasazhyrsky - Moskva Kievskaia', u'\u041b - 1 495.24 UAH', u'\u041a - 776.99 UAH']
2014-01-14 11:17:02.474000	[u'006 \u041a', u'Kyiv-Pasazhyrsky - Moskva Kievskaia', u'\u041b - 1 957.47 UAH', u'\u041a - 1 007.11 UAH']

encode(‘utf-8’) - не помогает.
Что может быть не так?


(Anna_tigris) #40

Всем привет. Тоже заинтересовалась этой задачей. Возникла проблема с формированием локаторов при поиске свободного места в вагоне. Если вагон “Люкс” проблем нет (driver.findElement(By.xpath("//a[@class=‘free’]")) работает отлично, а вот если вагон “Купе” локатор ни как не могу правильно составить. Вот такой пробовала driver.findElement(By.xpath("//a[@class=‘lower free’]")), но он сработает только если в первом купе встретит свободное нижнее место, если свободное нижнее место будет в другом купе он не срабатывает. Подскажите, что я делаю не так.


(Дмитрий Жарий) #41

Там структура страницы с двумя подвохами.

  1. Первый в том, что есть 2 пустых элемента, у которых есть класс free. При этом, они всегда есть на странице.
    Предлагаю, вот эти варианты локаторов:
XPath: //a[contains(@class, 'free') and @title]
CSS: a.free[title]

Второй подвох в том, что когда открываются некоторые вагоны купе – идет “графический эффект” пересчета мест. Т.е. сразу после открытия, нужно подождать пока места “пересчитаются”


(Дмитрий Жарий) #42

вот тут я не силен в кодировках для Java, но мне кажется, что стоит попробовать не encode, а decode.
И кодировка эта больше похожа на чистый Юникод, чем на UTF8.

Вот, нагуглил вопрос, может быть поможет: http://stackoverflow.com/questions/13700333/convert-escaped-unicode-character-back-to-actual-character