2012-10-11 86 views
1

在我的Rails項目中,我使用VCR和RSpec來測試HTTP對外部REST Web服務的交互,該服務僅允許每秒調用一次使用VCR和RSpec測試受限速的外部API調用

這是什麼意思到目前爲止,我最終運行我的測試套件,直到它失敗,由於從Web服務「超出呼叫次數」錯誤。然而,在那個階段,至少有一些錄像帶被錄製下來,所以我只是不斷地運行測試套件,直到最終我將它們全部記錄下來,套件只能使用盒式錄像帶(我的default_cassette_options = { record: :new_episodes })運行。這看起來並不是一種最佳的做事方式,特別是如果我發現我需要在未來經常重新錄製我的磁帶盒,並且我擔心不斷的調用可能會將我帶入Web服務的黑名單(沒有測試服務器他們有我知道的)。

所以,我結束了直接調用Web服務之前試圖把電話在我的Rspec的itsleep(1)製成,然後重構這些調用成錄像機配置:

規格/支持/ vcr.rb

VCR.configure do |c| 
    # ... 
    c.after_http_request do |request, response| 
    sleep(1) 
    end 
end 

儘管這似乎做工精細,有沒有更好的方式來做到這一點?目前,如果對已經沒有盒式磁帶的外部服務的呼叫是套件中的最終測試,則該套件不必要地睡1秒鐘。同樣,如果測試套件中兩次不帶磁帶的Web服務調用之間的時間超過一次,那麼還有一次不必要的暫停。有沒有人做過任何一種邏輯來測試這些條件,還是有辦法在VCR配置中優雅地做到這一點?

+1

鑑於積極的速率限制這樣的一個鏈接,我將建立節流權到客戶端。據推測,如果它炸燬測試套件,它也可能在生產中爆炸。 – willglynn

+0

+1好點;甚至沒有想到這一點。該計劃僅適用於每天一次的cron作業調用這些API,但由於在cron期間會有多個調用完成,所以此問題仍然存在。看起來我有一個客戶端設計問題來看待。 –

回答

3

首先,我建議不要使用:new_episodes作爲記錄模式。它有它的用途,但默認(:once)通常是你想要的。爲了保證準確性,您希望將錄像帶錄製爲一次性傳送的HTTP請求序列。使用:new_episodes,您可以結束包含幾個月內記錄但現在一起播放的HTTP交互的磁帶,而真正的HTTP服務器可能不會以同樣的方式響應。其次,我鼓勵你聽聽你的測試所暴露的痛苦,並且找到從這些HTTP請求中分離大部分測試套件的方法。你能找到一種方法來實現:只有測試集中在客戶端,而端到端的驗收測試才能提出請求?如果將HTTP內容封裝在一個簡單的界面中,應該很容易用所有其他測試代替測試雙重內容,並且更容易控制您的輸入。

雖然這是一個較長期的修復。在短期內,你可以調整你的VCR的配置,如下所示:

VCR.configure do |vcr| 
    allow_next_request_at = nil 
    filters = [:real?, lambda { |r| URI(r.uri).host == 'my-throttled-api.com' }] 

    vcr.after_http_request(*filters) do |request, response| 
    allow_next_request_at = Time.now + 1 
    end 

    vcr.before_http_request(*filters) do |request| 
    if allow_next_request_at && Time.now < allow_next_request_at 
     sleep(allow_next_request_at - Time.now) 
    end 
    end 
end 

它使用鉤過濾器(as documented)只在真正的API請求來主機運行掛鉤。 allow_next_request_at用於睡眠所需的最短時間。

+0

非常感謝您的回覆。我同意你和@willglyn的說法,這些問題暴露了我的服務客戶端類中的一些設計問題,並且我需要將大部分測試套件與這些請求分離開來,所以我會仔細研究一下。與此同時,你的短期解決方案爲我工作,非常感謝。然而,我確實得到了':real?'符號的錯誤,並且當我從'filters'中刪除它時,我得到了一個'未定義的方法'uri'for nil:NilClass'(將'r'改爲'req'後)。由於這只是一個短期修復,所以我現在不做引用「過濾器」。 –

+0

很高興幫助。我更新了一些代碼......我認爲這應該適用於你(儘管,我對':real?'錯誤感到困惑 - 這是經過測試的,它的工作原理 - 你得到了什麼錯誤?) –

+0

感謝編輯。代碼現在按預期工作。我最初得到的錯誤是'未定義的方法'是真的嗎?'對於nil:NilClass',但現在'lambda'塊中的參數數量從2變爲1,似乎解決了這個問題。非常感謝您的幫助! –

3

另一種方法可能是使用API​​Cache作爲HTTP庫的代理,因爲它將代表您處理速率限制。

APICache.get("my_albums", period => 1) do 
    FlickrRb.get_all_sets 
end 

當您嘗試調用API的次數超過您的限制時,將會提升APICache::CannotFetch

這裏給APICache Github repo

+0

不錯,謝謝。我會檢查出來的。 –