2013-01-08 27 views
16

Tôi có một khối retryRSpec thử lại ném ngoại lệ và sau đó trở về giá trị

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 

nơi fetch_and_rescan_app_instances truy cập mạng như vậy có thể ném một ngoại lệ.

Tôi muốn viết một thử nghiệm rspec nó ném một ngoại lệ lần đầu tiên và không ném một ngoại lệ thứ hai thời gian nó được gọi, vì vậy tôi có thể kiểm tra nếu lần thứ hai nó không ném một ngoại lệ, my_method thắng không ném một ngoại lệ.

Tôi biết tôi có thể làm stub(:fetch_and_rescan_app_instances).and_return(1,3) và lần đầu tiên nó trả về 1 và lần thứ hai 3, nhưng tôi không biết cách ném một ngoại lệ lần đầu tiên và trả lại thứ gì đó lần thứ hai.

Trả lời

21

Bạn có thể tính toán giá trị trả về trong một khối:

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 

Lưu ý rằng bạn nên thực sự không được giải cứu từ Exception, sử dụng một cái gì đó cụ thể hơn. Xem: Why is it a bad style to `rescue Exception => e` in Ruby?

+0

Đã thêm 'my_instance' vào đó để bạn nhận' my_instance.stub (: fetch_and_rescan_app_instances) 'vv. Gọi' stub (: fetch_and_rescan_app_instances) 'giống như vậy sẽ không hoạt động. –

+0

Xin cảm ơn! @shioyama, tôi hiểu tại sao không giải cứu ngoại lệ, nhưng nếu tôi không biết loại ngoại lệ chính xác nào thì nó sẽ ném? Tôi biết đó là một cuộc gọi mạng để nó có thể có vấn đề kết nối hoặc bất cứ điều gì khác, nhưng không chắc chắn loại ngoại lệ nó sẽ được. – Matilda

+1

@Meena, bạn có thể thử giải cứu thường xuyên, chỉ giải cứu được từ StandardError. – Jenn

12

Những gì bạn cần làm là hạn chế thời gian thông điệp nên được nhận (nhận tội), tức là trong trường hợp của bạn, bạn có thể

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') 

Calling instance.fetch_and_rescan_app_instances lần đầu tiên sẽ tăng RuntimeError, và lần thứ hai sẽ trở lại 'một số giá trị trả lại'.

PS. Gọi nhiều hơn điều đó sẽ dẫn đến lỗi, bạn có thể xem xét sử dụng các đặc điểm kỹ thuật số nhận khác nhau https://www.relishapp.com/rspec/rspec-mocks/docs/message-expectations/receive-counts

2

Điều này đã thay đổi một chút trong RSpec3.x. Có vẻ như cách tiếp cận tốt nhất là chuyển một khối tới receive xác định loại hành vi này.

Sau đây là từ các tài liệu cho thấy làm thế nào để tạo kiểu này transit failure:

(lỗi này mỗi lần khác nó được gọi là ... Nhưng rất dễ dàng để thích nghi.)

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 
Các vấn đề liên quan