2017-05-12 22 views
0

我有一系列使用Ruby和Watir gem開發的腳本。那些被Spinach包裹着,但那是我要問的。在超時的情況下向Watir添加重試機制

這些腳本的目的是做一些功能抽查或簡單地減輕一些非常重複的任務。

他們已經運行良好一段時間了,但最近,由於Chromedriver/Geckodriver(嘗試了兩種瀏覽器)和腳本之間的超時,我開始發現很多故障。當然,我可以簡單地重新啓動腳本,但是當成功率低於70%時,它確實開始惡化。

我最終做的是將所有打給Watir的電話都打包在一個Proc中,然後開始救援,在超時的情況下重試。

這是醜陋的,違反了很多規則,我幾乎慚愧不得不訴諸於此解決方案,但至少使用這個我的腳本現在正在完成。

這裏是我工作圍繞這一問題:

# takes a proc and wraps it around a series of rescue 
def execute_block_and_rety_if_needed 
    yield 
rescue Net::ReadTimeout 
    puts 'Read Timeout detected, retrying operation' 
    retry 
rescue Net::HTTPRequestTimeOut 
    puts 'Http Request Timeout detected, retrying operation' 
    retry 
rescue Errno::ETIMEDOUT 
    puts 'Errno::ETIMEDOUT detected, retrying operation' 
    retry 
end 

樣本使用是這樣的:

execute_block_and_rety_if_needed { @browser.link(name: 'OK').wait_until_present.click } # click the 'OK' button 

正如你所看到的,這明顯違反了DRY原則,因爲我需要每次調用這個過程。

我的問題是:如何將其作爲Watir的模塊/功能移動,以便它自動進行拾取。 (理想情況下,我會添加最大數量的重試來防止無限循環)。

版本信息: - Chromedriver => 2.29.461585 - GeckoDriver => 0.16.1 - 火狐=> ESR 52 - 鍍鉻=> 58 - 的Watir => 6.2.1

就作爲DRY評論,我提到了必須將所有Watir調用與proc包裝在一起的事實,如果不清楚,則表示抱歉。

execute_block_and_rety_if_needed { @browser.link(name: 'User').wait_until_present.click } # click the 'Edit' button 
execute_block_and_rety_if_needed { @browser.link(name: 'Cancel').wait_until_present.click } # click the 'Cancel' button 
execute_block_and_rety_if_needed { @browser.link(name: 'OK').wait_until_present.click } # click the 'OK' button 

以上只是一個例子,如果我想使用重試機制必須發生。

+0

請問您可以編輯您的問題,包括'chromedriver'和'geckodriver'版本以及各自的瀏覽器版本嗎?你運行最新的? – orde

回答

1

鑑於您希望重試發送到瀏覽器的每個命令,您可能需要考慮解決底層Selenium-WebDriver而不是Watir的問題。 Watir命令被髮送到Selenium-WebDriver,Selenium-WebDriver又將它們發送到瀏覽器/驅動程序。

每個命令(或至少大多數)當前通過Selenium::WebDriver::Remote::Http:Default#request發送。您可以修補該方法以重新包裝它。您的點擊不僅會重試超時,而且其他所有命令也會重試 - 例如導航,設置字段,獲取值等。

# Patch to retry timeouts during requests 
require 'watir' 
module Selenium 
    module WebDriver 
    module Remote 
     module Http 
     module DefaultExt 
      def request(*args) 
      tries ||= 3 
      super 
      rescue Net::ReadTimeout, Net::HTTPRequestTimeOut, Errno::ETIMEDOUT => ex 
      puts "#{ex.class} detected, retrying operation" 
      (tries -= 1).zero? ? raise : retry    
      end 
     end 
     end 
    end 
    end 
end 
Selenium::WebDriver::Remote::Http::Default.prepend(Selenium::WebDriver::Remote::Http::DefaultExt) 

# Then you can use Watir as usual 
browser = Watir::Browser.new :chrome # this will retry timeouts 
browser.goto('http://www.example.com') # this will also retry timeouts 
browser.link.click      # this will also retry timeouts 
+0

這正是我所需要的。這使我可以刪除所有用塊做的包裝,現在代碼處於更好的狀態。 非常感謝@Justin Ko! – Dave

1

您不需要爲此使用塊。您可以實現,做類似的方法:

def ensure_click(element, retries = 3) 
    @retries ||= retries 
    element.click 
rescue Net::ReadTimeout, Net::HTTPRequestTimeOut, Errno::ETIMEDOUT => ex 
    raise unless @retries > 0 
    @retries = @retries - 1 
    puts "#{ex.class} detected, retrying" 
    retry 
end 

... 
ensure_click(@browser.link(name: 'User')) 
... 

話雖這麼說,這些異常通常不是驅動程序錯誤,而是某種形式的網絡問題。這是不正常的。

+0

這確實比較乾淨。我會確保朝着這個方向前進。 子問題艱難:有沒有辦法讓Watir在任何地方都可以做到這一點,而無需重寫我的所有腳本? 就網絡問題而言,不幸的是,我無能爲力。這是我的影響範圍之外,必須處理它,因此是重試機制。 – Dave

+0

嗯,也許我們可以在救援塊內放置後掛鉤調用,並將該異常作爲參數傳遞給該方法。它會讓你設置一個Proc在每次潛在的DOM重置後運行。但這意味着將它放在多個地方,這也不是很優雅。在我們的列表中,我們可以使用Selenium Event監聽器實現後續處理,也許在此期間我們可以重新訪問。目前,做替換方法是最好的。 – titusfortner

+0

聽起來不錯。想讓我標記爲已解決,或者讓它開放直到你們完成重訪? – Dave

相關問題