一個有意思的Ruby Webdriver逾時問題的解決過程

來源:互聯網
上載者:User

標籤:webdriver   ruby   wait   timeout   monkey patch   

rescue in receive

因為寫ruby的時候感覺混身上下都拽起來了,所以比較喜歡用ruby寫代碼。今天遇到了一個webdriver timeout的問題,問題本身還是因為我對webdriver不瞭解以及破文檔導致的。首先我們把問題簡化一下:


driver = Selenium::WebDriver.for :safari
driver.navigate.to "http://www.faraway.com"

wait = Selenium::WebDriver::Wait.new(:timeout => 1000) # seconds
wait.until { driver.find_element(:css, ‘input[name="username"]‘) }

因為考慮到網站太遠了,速度比較慢,於是timeout設的值比較大:timeout => 1000

這裡我們訪問:faraway這個網站,然後等待出現輸入框輸入使用者名稱。得到了如下錯誤資訊:

/Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/ selenium/webdriver/safari/server.rb:41:in`rescue in receive‘: timed out waiting for Safari to respond (Selenium::WebDriver::Error::TimeOutError)
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/safari/server.rb:36:in `receive‘
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/safari/bridge.rb:68:in `raw_execute‘
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/remote/bridge.rb:612:in `execute‘
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/remote/bridge.rb:110:in `get‘
from /Users/twer/.rvm/gems/[email protected]/gems/selenium-webdriver-2.42.0/lib/selenium/webdriver/common/navigation.rb:14:in `to‘
from faraway.rb:26:in `<main>‘


看了出錯的地方就是我wait的地方調用的。我心想我這不是設了:timeout => 1000,wait的等待時間為1000秒嗎,怎麼還搞不定啊?


Monkey Patch來拯救我了

搞不定啊,上面有人在吹啊,咋辦嘛。暴力解決。我找到了上面紅色部分標明的檔案/selenium/webdriver/safari/server.rb。(https://code.google.com/p/selenium/source/browse/rb/lib/selenium/webdriver/safari/server.rb)

找到了出錯的地方41行,是在receive函數裡:

def receive
   @frame ||= WebSocket::Frame::Incoming::Server.new(:version => @version)
   until msg = @frame.next

   end_time = Time.now + @command_timeout

   begin
      data = @ws.read_nonblock(1)
      rescue Errno::EWOULDBLOCK, Errno::EAGAIN
         now = Time.now
         if now >= end_time
            raise Error::TimeOutError, "timed out waiting for Safari to respond"   #第41行
         end

         IO.select([@ws], nil, nil, end_time - now)
         retry
      end

      @frame << data
   end

   puts "<<< #{msg}" if $DEBUG

   WebDriver.json_load msg.to_s
end


可以看出,是now >= end_time用來計算是否逾時,在看前面end_time是由Time.now + @command_timeout得到的,然後進入begin/rescue/retry的。奇怪,我的:timeout => 1000不應該是work的嗎?


第一個monkey patch

我把上面那部分代碼直接拷到了我的源檔案faraway.rb中,做了如下修改:

if now >= end_time
   puts @command_timeout
   raise Error::TimeOutError, "timed out waiting for Safari to respond"
end

發現@command_timeout不是1000,而是60,也就是一分鐘


第二個monkey patch

until msg = @frame.next
   end_time = Time.now + 1000
這樣強制讓@command_timeout設為1000


這樣運行,沒有問題了


探個究竟

雖然monkey patch可以解決我串連faraway的問題。但是,作為快速實驗得到反饋還行,真這樣用還是不太愉快。於是我就開始讀原始碼,webdriver本身的代碼還是挺簡單的。很容易看懂,具體怎麼看我就不講了。分分鐘發現了問題其實是在這裡:


driver = Selenium::WebDriver.for :safari,timeout: 1000
driver.navigate.to "http://www.faraway.com"


紅色的部分在文檔上是沒有交代的,那這裡的timeout是什麼意思呢?我的wait不是已經有timeout了嗎,怎麼在Driver的建立時還需要呢?

原因是這是兩種不同的timeout。wait的timeout表示的是,我們的driver會等待多久知道這個元素出現。而我們出錯的地方是driver接收伺服器返回資訊的timeout。還不清楚?

這樣說,在wait的時候,driver會周期輪詢的去執行until中的代碼,看看until中的情況是否得到了滿足。每次執行的時候Driver對會發一個請求,這個請求其實也是有timeout時間的,所以我們最開始遇到的問題並不是wait本身timeout了。而是driver發送命令後接收的時候timeout了。

明了了吧,都是faraway.com搞得怪,想重現這個問題。把command timeout改為1試試


如果我再次不幸。。。

我一隻攪得自己是運氣比較背的那種人。不過解決問題的過程還是很開心。ruby這種動態語言,可以打monkey patch,對於快速定位問題,解決問題還是很有協助的。開心。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.