【问题标题】:Can a watir browser object be re-used in a later Ruby process?可以在以后的 Ruby 进程中重用 watir 浏览器对象吗?
【发布时间】:2012-01-27 18:16:04
【问题描述】:

因此,假设经常运行一个打开浏览器并执行 Web 操作的脚本:

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,并将其重新分配给全新 Ruby 进程中的另一个 $browser 变量,例如 irb? IE。可以稍后在同一台机器上使用 watir-webdriver 在另一个程序中重新附加 webdriver 上的孤立浏览器吗?然后我可以从 irb 进入并重新连接到崩溃的 Ruby 脚本留下的浏览器,检查它所在的网站,检查出了什么问题,哪些元素与预期不同,等等。

后者的另一个非常有利的用途是避免每天可能数百次浏览器启动和关闭的开销......最好让一个作为守护进程保持活力。第一次运行将尝试使用我特别准备的 botmode 配置文件重用以前的浏览器对象,否则创建一个。然后我会故意不在脚本末尾调用 $browser.close 。如果没有别的,我会运行一个 at 工作来杀死 Xvfb :99 显示 FF 无论如何都会在一天结束时运行(如果仍然在运行,让 FF 别无选择,只能和它一起死)。是的,我知道 Selenium 独立 jar,但也试图避免该 java 服务足迹。

如果这是一个基本的 Ruby 问题,我们深表歉意。我只是不知道如何表达它并不断获得不相关的搜索结果。

【问题讨论】:

  • 代码与 FF 等实际浏览器交互是否很重要?如果速度是问题,那么也许考虑使用无头选项而不是 FF?或者像 Chrome 或 Opera 这样启动速度更快的浏览器?如果我们对您想要完成的工作有更多了解,我们或许能够提供更好的建议和/或解决方案
  • 赞成速度和特别是。限制内存消耗,是的,我曾希望完全避免使用浏览器,但过去尝试启用 Htmlunit +Javascript 会导致灾难性故障或太多的编码差异......这是你的想法吗?如果不是强制 AJAX 支持,我会坚持使用 Mechanize 而不是 Watir,就像我为 TDAmeritrade.com 所做的那样,速度更快更轻
  • 是的,我正在考虑这篇博客帖子watirmelon.com/2010/12/14/… 中描述的 HtmlUnit 路由但是如果你有 Ajax 或其他需要真正浏览器的问题,那么是的,你会想要使用 FF 或 Chrome等等。为此,我认为我在下面提供的答案可能适用

标签: ruby serialization object watir watir-webdriver


【解决方案1】:

我猜,你不能只记住另一个进程的变量。但解决方案可能是创建一个主进程并在线程中循环处理您的脚本,定期检查浏览器的运行状态。我在 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? - 可以通过 ctrl+C 结束 TRAP

browser_live? - 你可以检查firefox浏览器是否与进程列表一起运行

这很棘手,但可能对你有用

【讨论】:

  • 谢谢,在某种程度上,我正在考虑守护我的低级 watir 脚本,但我担心这需要使用当前简单的命令行在更高级别进行大修API。
  • 认为创建一个separate question 来探索创建一个简单、独立的守护进程的策略可能不是一个坏主意,该守护进程的唯一目的是保持与我的主 Ruby 脚本共享的持久 Ruby 对象的活动
  • 我可以用那个来扩展我的答案
【解决方案2】:

我很确定,在 ruby​​ 退出时,任何指向浏览器对象之类的句柄或指针都会变得无效。因此,在以后的 ruby​​ 过程中重新使用某些东西可能不是一个好方法。另外我可能错了,但似乎 webdriver 并不擅长连接到正在运行的浏览器进程。因此,对于您的工作方法,它确实需要由调用所有测试等的某个主进程包装。嘿,等一下,这开始听起来像一个框架,您可能已经(或者应该是) 首先使用。

因此,更好的解决方案可能是查看您用于运行测试的任何框架,并调查在每个测试之前和之后运行的“设置/拆卸”操作(可以使用不同的名称)的任何功能,测试组或所有测试。这样做很好,因为大多数框架旨在允许您运行任何单个测试或您想要的一组测试。如果您的测试设计得很好,它们可以单独运行,而不必期望系统在之前的测试中处于某种完美状态。因此,这些类型的设置/拆卸操作也旨在以这种方式工作。

作为一个例子,Cucumber 在特性级别有这个,“背景”的想法基本上是为了通过定义在特性文件中的每个场景之前运行的通用步骤来干掉场景。 (例如导航到您的站点并登录)这可能包括调用一系列步骤,以查看浏览器对象是否存在,如果不存在则创建一个。但是,您需要将其放入每个开始变得相当不干燥的功能文件中。

幸运的是,cucumber 还允许通过使用Hooks 在一个地方执行此操作。您可以定义在步骤之前运行的钩子,在特定条件下,每个场景的“之前”和“之后”,以及在任何场景之前运行一次的代码,以及定义为运行“at_exit”的代码,您可以在其中关闭在所有场景都运行后浏览器。

如果我使用 cucumber,我会考虑 env.rb 中的一些代码的想法,该代码将在开始时运行以创建浏览器,并辅以 at_exit 代码以关闭浏览器。然后也许还可以在before 钩子中编码,它可以检查浏览器是否仍然存在并在需要时重新创建它,并且可能在after 钩子中进行注销操作。如果功能中的所有场景都使用相同类型的用户登录,请留下诸如登录单个场景之类的内容,或者 background 块。

【讨论】:

  • ++ 当我的引擎的这个小部分成长以保证它自己的框架时,请考虑一下。不确定我是否还在那里,但是将设置和拆卸与许多测试/操作集分开似乎在结构上确实很合适。无论如何,我注意到PersistentWebdriver 上的讨论,但它似乎还没有准备好迎接黄金时段,或者我不清楚如何无缝地应用到我当前在 Ruby 中使用的 Watir-webdriver 浏览器实例。
  • 我建议尽早开始使用现有框架。在统一报告、设置/拆卸和许多其他轮子方面,您可以获得很多好处,否则您可能会花费大量时间重新发明。请参阅watir.com/frameworks 了解一些示例。然后,如果您发现现有的框架都不能满足您的需求,您可以考虑滚动您自己的框架,或者扩展一个最接近您想要的框架。很多优秀的工作已经投入其中,很遗憾没有利用它来让你的生活更轻松(并解决你当前的问题!)
  • 您指出的这些 Hooks 似乎是一个有用的功能。尽管当前的整体设计并不能证明为此召唤所有 Cucumber 是合理的,因为它不会真正提高可维护性。网络驱动组件已经接近最终的和简单的。确实,后来改装总是更痛苦。
【解决方案3】:

不是一个解决方案,而是我问题的第 1 部分的解决方法,使用 pkill。在这里发帖,因为事实证明它比我希望的要简单得多。

在 ruby​​ 脚本退出后,其衍生进程(可能不再属于同一个 PID 树,如 firefox-bin)有一个可预测的“会话领导者”,结果证明它是 父进程 em> 在我的例子中调用 ruby​​program.rb 的 bash shell。在 Bash 中以 $PPID 的形式提供,当您必须高于 $$ 时。

从而真正清理不需要的重量级进程,例如。红宝石崩溃后:

#!/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

【讨论】:

  • pkill -s $PPID firefox-bin 并不总是足够的。在其他环境(例如 CRON)中,我必须使用 sesspid=$(awk '$1=="'$$'" {print $3}' &lt;(ps x -o pid,pgid,sess,ppid,tty,time,comm)) 找到 $$ 的会话负责人,然后使用它而不是 $PPID,如 pkill -s $sesspid firefox-bin
【解决方案4】:

只是我问题第 2 部分的更新。

似乎可以使用 YAML 序列化 Watir:Browser 对象,并且因为它是基于文本的,所以内容对我来说非常有趣(例如,我梦寐以求的一些东西隐藏在私有类的私有元素中......但这是一个单独的主题)

从 YAML 反序列化仍然很麻烦。虽然我没有在第一次尝试之后进行测试,但它给了我某种 reg exp 解析错误......不知道那是什么。

(更多信息请访问how to serialize an object using TCPServer inside?

同时,即使尝试使用 Marshal 进行序列化,它也是 Ruby 内置的,但以二进制格式存储,会导致一个非常合理的错误,即无法转储 TCPServer 对象(显然包含在我的 Watir 中: $browser 指向的浏览器)

总而言之,我对这些结果并不感到惊讶,但仍然非常有信心有办法,直到 Watir 找到更原生的东西(比如 PersistentWebdriver 或者在 jssh 时代你可以只需附加到具有正确扩展名的已运行浏览器)

在那之前,如果对工作对象的序列化 + 反序列化变得过于棘手,我将诉诸守护程序的一部分 Ruby 以保持对象的持久性并避免频繁且昂贵的设置/拆卸。我确实对一些已建立的(单元测试)框架有所了解,但似乎没有一个框架适合我的整体软件结构——毕竟我不是网络测试。

【讨论】:

【解决方案5】:

您可以像这样使用DRb
浏览器池:

require 'drb'
require 'watir'

browser = Watir::Browser.new :chrome
DRb.start_service 'druby://127.0.0.1:9395', browser

gets

然后从测试脚本中使用这个浏览器:

require 'drb'
browser = DRbObject.new_with_uri 'druby://127.0.0.1:9395'
browser.goto 'stackoverflow.com'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-03
    • 1970-01-01
    • 2010-10-09
    • 2023-04-09
    • 1970-01-01
    • 2011-03-20
    • 2016-10-04
    • 1970-01-01
    相关资源
    最近更新 更多