2013-01-08 19 views
16

我有一個重試塊RSpec的重試拋出異常,然後返回值

def my_method 
    app_instances = [] 
    attempts = 0 
    begin 
     app_instances = fetch_and_rescan_app_instances(page_n, policy_id, policy_cpath) 
    rescue Exception 
     attempts += 1 
     retry unless attempts > 2 
     raise Exception 
    end 
    page_n += 1 
    end 

其中fetch_and_rescan_app_instances接入網絡,以便能夠拋出異常。

我想寫一個rspec測試,它第一次拋出一個異常,第二次被調用時不會拋出異常,所以我可以測試第二次拋出異常時my_method是否贏得不會拋出任何豁免。

我知道我可以做stub(:fetch_and_rescan_app_instances).and_return(1,3)和第一次它返回1和第二次3,但我不知道如何做第一次拋出異常和第二次返回的東西。

回答

21

你可以在一個塊計算的返回值:

describe "my_method" do 
    before do 
    my_instance = ... 
    @times_called = 0 
    my_instance.stub(:fetch_and_rescan_app_instances).and_return do 
     @times_called += 1 
     raise Exception if @times_called == 1 
    end 
    end 

    it "raises exception first time method is called" do 
    my_instance.my_method().should raise_exception 
    end 

    it "does not raise an exception the second time method is called" do 
    begin 
     my_instance.my_method() 
    rescue Exception 
    end 
    my_instance.my_method().should_not raise_exception 
    end 
end 

請注意,您應該真的不從Exception搶救,使用更具體的東西。請參閱:Why is it a bad style to `rescue Exception => e` in Ruby?

+0

在那裏添加了'my_instance',這樣你就可以得到'my_instance.stub(:fetch_and_rescan_app_instances)'等等。調用'stub(:fetch_and_rescan_app_instances)'就像不行。 –

+0

非常感謝! @shioyama,我明白了爲什麼不做救援異常,但如果我不知道它會拋出什麼樣的確切例外呢?我知道這是一個網絡調用,所以它可能有連接問題或其他任何事情,但不知道它會是什麼類型的異常。 – Matilda

+1

@Meena,你可以嘗試一個定期的救援,它只是從StandardError救出。 – Jenn

12

你要做的就是限制了次數,應收到消息(接收數),即你的情況,你可以

instance.stub(:fetch_and_rescan_app_instances).once.and_raise(RuntimeError, 'fail') 
instance.stub(:fetch_and_rescan_app_instances).once.and_return('some return value') 

調用instance.fetch_and_rescan_app_instances第一次將提高RuntimeError,並第二次將返回'一些回報價值'。

PS。調用比這更會導致錯誤,您可能會考慮使用不同的接收計數規範https://www.relishapp.com/rspec/rspec-mocks/docs/message-expectations/receive-counts

2

這在RSpec3.x中稍有變化。看起來最好的方法是將一個塊傳遞給定義這種行爲的receive

以下是從文檔提示如何創建該類型的transit failure

(此錯誤它被稱爲每隔一次 ...但很容易適應。)

RSpec.describe "An HTTP API client" do 
    it "can simulate transient network failures" do 
    client = double("MyHTTPClient") 

    call_count = 0 
    allow(client).to receive(:fetch_data) do 
     call_count += 1 
     call_count.odd? ? raise("timeout") : { :count => 15 } 
    end 

    expect { client.fetch_data }.to raise_error("timeout") 
    expect(client.fetch_data).to eq(:count => 15) 
    expect { client.fetch_data }.to raise_error("timeout") 
    expect(client.fetch_data).to eq(:count => 15) 
    end 
end