Можно ли повторно использовать объект браузера watir в более позднем процессе Ruby? - PullRequest
2 голосов
/ 26 декабря 2011

Итак, скажем, довольно часто запускается скрипт, который открывает браузер и выполняет веб-операции:

require 'watir-webdriver'

$browser = Watir::Browser.new(:firefox, :profile => "botmode")
 => #<Watir::Browser:0x7fc97b06f558 url="about:blank" title="about:blank"> 

Это может закончиться изящным завершением файла browser.close или может произойти сбой раньше и оставить незанятый процесс Firefox, требующий памяти, до тех пор, пока они не накопятся и не замедлят работу сервера.

У меня двоякий вопрос:

  • Хорошей практикой является обеспечение того, чтобы даже в случае сбоя скрипта в любом месте, приводящем к немедленному завершению ошибки, подпроцесс всегда очищался (у меня уже есть много коротких блоков begin-rescue-end, добавленных для других не связанных небольших тестов )
  • Что еще более важно, могу ли я просто запомнить этот Watir :: Browser: 0x7fc97b06f558 адрес объекта или PID каким-либо образом и переназначить его другой переменной $ browser в целом новом процессе Ruby, например, irb? То есть Можно ли позднее повторно подключить потерянный браузер к веб-драйверу в другой программе, используя watir-webdriver на том же компьютере? Затем из irb я мог войти и снова присоединиться к браузеру, оставленному сбойным скриптом Ruby, проверить сайт, на котором он работал, проверить, что пошло не так, какие элементы отличаются от ожидаемых и т. Д.

Другим чрезвычайно выгодным вариантом использования последнего было бы избежать ненужных затрат, связанных с сотнями запусков и отключений браузера в день ... лучше всего поддерживать один из них как своего рода демон. При первом запуске попытается повторно использовать предыдущий объект браузера, используя мой специально подготовленный профиль botmode , в противном случае создайте его. Тогда я бы намеренно не вызывал $ browser.close в конце моего скрипта. Если ничего другого, я запускаю на работе, чтобы убить Xvfb: 99, дисплей FF запускается внутри в конце дня, так или иначе (не давая FF никакого выбора, кроме как умереть с ним, если он все еще работает). Да, я знаю о Selenium jar, но также стараюсь не использовать этот java-сервис.

Извиняюсь, если это более простой вопрос Ruby. Я просто не был уверен, как это сформулировать и получать нерелевантные результаты поиска.

Ответы [ 4 ]

2 голосов
/ 26 декабря 2011

Я думаю, вы не можете просто запомнить переменную из другого процесса.Но решением может быть создание главного процесса и обработка вашего скрипта в цикле в потоке, периодически проверяя состояние работы браузера.Я использую нечто похожее в моих приемочных тестах на Cucumber + watir.Так что это будет что-то вроде этого:

require 'rubygems'
require 'firewatir' # or watir
@browser = FireWatir::Firefox.new

t = Thread.new do
    @browser.goto "http://google.com"
    #call more browser actions here
end
while not_exit?
  if t.stop?
      # error occurred in thread, restart or exit
  end
  if browser_live?
      # browser was killed for a some reason
      # restart or exit
  end
end
@browser.close

not_exit?- может быть более TRAP для Ctrl + C

browser_live?- вы можете проверить, работает ли браузер Firefox с списками процессов

Это довольно сложно, но может работать для вас

1 голос
/ 27 декабря 2011

Я почти уверен, что в момент выхода из ruby ​​любые дескрипторы или указатели на что-то вроде объекта браузера станут недействительными. Поэтому повторное использование чего-либо в более позднем процессе ruby, вероятно, не очень хороший подход. Кроме того, я могу ошибаться в этом, но кажется, что веб-драйвер не очень хорош для подключения к запущенному процессу браузера. Так что для вашего подхода к работе на самом деле все это нужно было бы обернуть каким-нибудь мастер-процессом, который вызывал все тесты и т. Д. И, эй, подождите секунду, это начинает звучать как фреймворк, который вы, возможно, уже (или, возможно, должны быть ) используя в первую очередь.

Таким образом, лучшее решение, вероятно, состоит в том, чтобы взглянуть на любую среду, которую вы используете для запуска своих тестов, и исследовать любую возможность действий 'setup / teardown' (которые могут иметь разные имена), которые выполняются до и после каждого теста, группы тестов или все тесты. Идти по этому пути хорошо, так как большинство фреймворков разработано так, чтобы вы могли запускать любой отдельный тест или набор тестов, которые вы хотите. И если ваши тесты хорошо спроектированы, их можно запускать по отдельности, не ожидая, что система была оставлена ​​в каком-то идеальном состоянии в результате предыдущего теста. Таким образом, эти виды действий по настройке / демонтажу также предназначены для такой работы.

В качестве примера, Cucumber имеет это на уровне функций, с идеей «фона», который в основном предназначен для просушки сценариев, определяя общие шаги, которые нужно выполнить перед каждым сценарием в файле объектов. (например, переход на ваш сайт и вход в него). Это может включать в себя вызов последовательности шагов, которые проверят, существует ли объект браузера, а если нет, то создайте его. Однако вам нужно будет добавить это в каждый файл функций, который начинает становиться довольно сухим.

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

Если бы я использовал огурец, я бы взглянул на идею некоторого кода в env.rb, который будет запускаться в начале, чтобы создать браузер, дополненный кодом at_exit, чтобы закрыть браузер. Затем, возможно, также код в хуке before, который может проверить, что браузер все еще там, и воссоздать его при необходимости, и, возможно, выйти из системы в хуке after. Оставьте такие вещи, как вход в систему для отдельных сценариев или блок background, если все сценарии в функции входят в систему с одним и тем же пользователем.

0 голосов
/ 16 января 2012

Просто обновление по части 2 моего вопроса.

Кажется, что МОЖЕТ сериализовать объект Watir: Browser с YAML, и, поскольку он основан на тексте, его содержание мне очень интересно (например, некоторые вещи, о которых я только мечтал настроить, скрытые внутри частных элементов частных классов ...) но это отдельная тема)

Десериализация из YAML все еще является проблемой. Несмотря на то, что я не тестировал после первой попытки, это дает мне какую-то ошибку синтаксического анализа reg exp ... не уверен, что это такое.

(подробнее об этом на , как сериализовать объект, используя TCPServer внутри? )

Между тем, даже попытка сериализации с Marshal, который также встроен в Ruby, но хранит в двоичном формате, приводит к очень разумно звучащей ошибке из-за невозможности выгрузить объект TCPServer (очевидно, содержащийся в моем Watir: Браузер, на который указывает $ browser)

В целом, я не удивлен этими результатами, но все еще довольно уверен, что есть способ, пока Watir не придет к чему-то более нативному (как PersistentWebdriver или как это было во времена jssh, когда вы можете просто подключиться к уже запущенному браузеру с правильным расширением)

До тех пор, если сериализация + десериализация для рабочего объекта станет слишком сложной, я прибегну к демонизации части моего Ruby, чтобы сохранить постоянство объектов и избавить от частых и дорогостоящих настроек / демонтажей. И я изучил некоторые устоявшиеся (модульное тестирование) фреймворки, но, похоже, ни одна из них еще не вписывается в общую структуру программного обеспечения - в конце концов, я не занимаюсь веб-тестированием.

0 голосов
/ 07 января 2012

Не столько решение, сколько обходной путь для первой части моего вопроса с использованием pkill.Публикация здесь, поскольку она оказалась намного менее тривиальной, чем я надеялся.

После выхода из сценария ruby ​​его порожденные процессы (которые могут больше не принадлежать одному и тому же дереву PID, как firefox-bin)) иметь предсказуемого «лидера сеанса», который оказался родительским оболочки bash, вызывающей rubyprogram.rb в моем случае.Доступно как $ PPID в Bash, для случаев, когда вам нужно подняться выше $$ .

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

#!/bin/bash
# This is the script that wraps on top of Ruby scripts

./ruby_program_using_watirwebdriver_browser.rb  myparams &  # spawn ruby in background but keep going below:

sleep 11 # give Ruby a chance to launch its web browser

pstree -panu $$  # prints out a process tree starting under Bash, the parent of Ruby. Firefox may not show!

wait  # now wait for Ruby to exit or crash

pkill -s $PPID firefox-bin  # should only kill firefox-bin's caused above, not elsewhere on the system

# Another way without pkill, will also print out what's getting killed if anything:
awk '$7=="firefox-bin" && $3=="'$PPID'" {print $1}' <(ps x -o pid,pgid,sess,ppid,tty,time,comm) | xargs -rt kill

ДОПОЛНИТЕЛЬНО И так как я использую выделенный сервер Xvfb Xwindows только для веб-привода на DISPLAY: 99, я также могу рассчитывать на xkill:

timeout 1s  xwininfo -display :99 -root -all |awk '/("Navigator" "Firefox")/ {print $1}' |xargs -rt  xkill -display :99 -id  
# the timeout is in case xkill decides to wait for user action, when window id was missing
...