diff --git a/README.md b/README.md index 211995656..50b7b23a9 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ You can also use WebMock without RSpec or Test::Unit support: req = Net::HTTP::Get.new("/") Net::HTTP.start("www.example.com") { |http| http.request(req) }.message # ===> "Internal Server Error" - + ### Replaying raw responses recorded with `curl -is` `curl -is www.example.com > /tmp/example_curl_-is_output.txt` @@ -162,6 +162,18 @@ You can also use WebMock without RSpec or Test::Unit support: RestClient.post('www.example.net', 'abc') # ===> "abc\n" +### Raising errors + + stub_request(:any, 'www.example.net').to_raise(StandardError) + + RestClient.post('www.example.net', 'abc') # ===> StandardError + +### Raising timeout errors + + stub_request(:any, 'www.example.net').to_timeout + + RestClient.post('www.example.net', 'abc') # ===> RestClient::RequestTimeout + ### Multiple responses for repeated requests stub_request(:get, "www.example.com").to_return({:body => "abc"}, {:body => "def"}) @@ -172,7 +184,7 @@ You can also use WebMock without RSpec or Test::Unit support: Net::HTTP.get('www.example.com', '/') # ===> "def\n" -### Multiple responses using chained `to_return()` or `to_raise()` declarations +### Multiple responses using chained `to_return()`, `to_raise()` or `to_timeout` declarations stub_request(:get, "www.example.com"). to_return({:body => "abc"}).then. #then() is just a syntactic sugar diff --git a/lib/webmock/http_lib_adapters/httpclient.rb b/lib/webmock/http_lib_adapters/httpclient.rb index 865628ed6..ae354ffbc 100644 --- a/lib/webmock/http_lib_adapters/httpclient.rb +++ b/lib/webmock/http_lib_adapters/httpclient.rb @@ -59,6 +59,7 @@ def build_httpclient_response(webmock_response, stream = false, &block) response.reason=webmock_response.status[1] webmock_response.headers.to_a.each { |name, value| response.header.set(name, value) } + raise HTTPClient::TimeoutError if webmock_response.should_timeout webmock_response.raise_error_if_any block.call(nil, body) if block diff --git a/lib/webmock/http_lib_adapters/net_http.rb b/lib/webmock/http_lib_adapters/net_http.rb index d3334d0de..05c022887 100644 --- a/lib/webmock/http_lib_adapters/net_http.rb +++ b/lib/webmock/http_lib_adapters/net_http.rb @@ -122,6 +122,8 @@ def build_net_http_response(webmock_response, &block) response.extend StubResponse + raise Timeout::Error if webmock_response.should_timeout + webmock_response.raise_error_if_any yield response if block_given? diff --git a/lib/webmock/http_lib_adapters/patron.rb b/lib/webmock/http_lib_adapters/patron.rb index bae4f0728..a379cc0f1 100644 --- a/lib/webmock/http_lib_adapters/patron.rb +++ b/lib/webmock/http_lib_adapters/patron.rb @@ -66,6 +66,7 @@ def build_request_signature(req) end def build_patron_response(webmock_response) + raise Patron::TimeoutError if webmock_response.should_timeout webmock_response.raise_error_if_any res = Patron::Response.new res.instance_variable_set(:@body, webmock_response.body) diff --git a/lib/webmock/request_stub.rb b/lib/webmock/request_stub.rb index 8383e910b..63b1a309c 100644 --- a/lib/webmock/request_stub.rb +++ b/lib/webmock/request_stub.rb @@ -29,6 +29,11 @@ def to_raise(*exceptions) }) self end + + def to_timeout + @responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(:should_timeout => true)]) + self + end def response if @responses_sequences.empty? diff --git a/lib/webmock/response.rb b/lib/webmock/response.rb index 388bb7c08..d00c818de 100644 --- a/lib/webmock/response.rb +++ b/lib/webmock/response.rb @@ -60,17 +60,24 @@ def raise_error_if_any raise @exception.new('Exception from WebMock') if @exception end + def should_timeout + @should_timeout == true + end + def options=(options) self.headers = options[:headers] self.status = options[:status] self.body = options[:body] @exception = options[:exception] + @should_timeout = options[:should_timeout] end def evaluate!(request_signature) self.body = @body.call(request_signature) if @body.is_a?(Proc) self.headers = @headers.call(request_signature) if @headers.is_a?(Proc) self.status = @status.call(request_signature) if @status.is_a?(Proc) + @should_timeout = @should_timeout.call(request_signature) if @should_timeout.is_a?(Proc) + @exception = @exception.call(request_signature) if @exception.is_a?(Proc) self end @@ -78,7 +85,8 @@ def ==(other) self.body == other.body && self.headers === other.headers && self.status == other.status && - self.exception == other.exception + self.exception == other.exception && + self.should_timeout == other.should_timeout end private diff --git a/spec/httpclient_spec_helper.rb b/spec/httpclient_spec_helper.rb index 4665900af..74d26d19b 100644 --- a/spec/httpclient_spec_helper.rb +++ b/spec/httpclient_spec_helper.rb @@ -25,6 +25,10 @@ def http_request(method, uri, options = {}, &block) }) end + def client_timeout_exception_class + HTTPClient::TimeoutError + end + def default_client_request_headers(request_method = nil, has_body = false) {'Content-Type'=>'application/x-www-form-urlencoded'} if request_method == 'POST' && has_body end diff --git a/spec/net_http_spec_helper.rb b/spec/net_http_spec_helper.rb index 647fcb159..8e0d5cb21 100644 --- a/spec/net_http_spec_helper.rb +++ b/spec/net_http_spec_helper.rb @@ -32,6 +32,10 @@ def default_client_request_headers(request_method = nil, has_body = false) [k, v.flatten] }.flatten] end + + def client_timeout_exception_class + Timeout::Error + end # Sets several expectations that a real HTTP request makes it # past WebMock to the socket layer. You can use this when you need to check diff --git a/spec/patron_spec_helper.rb b/spec/patron_spec_helper.rb index 5dabdde88..35c282aaa 100644 --- a/spec/patron_spec_helper.rb +++ b/spec/patron_spec_helper.rb @@ -3,16 +3,16 @@ def http_request(method, uri, options = {}, &block) uri = Addressable::URI.heuristic_parse(uri) sess = Patron::Session.new sess.base_url = "#{uri.omit(:userinfo, :query).normalize.to_s}".gsub(/\/$/,"") - + sess.username = uri.user sess.password = uri.password - + sess.timeout = 10 - + response = sess.request(method, "#{uri.path}#{uri.query ? '?' : ''}#{uri.query}", options[:headers] || {}, { :data => options[:body] }) - + OpenStruct.new({ :body => response.body, :headers => WebMock::Util::Headers.normalize_headers(response.headers), @@ -20,13 +20,17 @@ def http_request(method, uri, options = {}, &block) :message => response.status_line }) end - + def default_client_request_headers(request_method = nil, has_body = false) nil end + def client_timeout_exception_class + Patron::TimeoutError + end + def setup_expectations_for_real_request(options = {}) #TODO - end + end end diff --git a/spec/request_stub_spec.rb b/spec/request_stub_spec.rb index 758d43238..de15b7c97 100644 --- a/spec/request_stub_spec.rb +++ b/spec/request_stub_spec.rb @@ -124,6 +124,28 @@ end + describe "to_timeout" do + + it "should assign response with timeout" do + @request_stub.to_timeout + @request_stub.response.should_timeout.should be_true + end + + it "should assign sequence of responses with response with timeout" do + @request_stub.to_return(:body => "abc").then.to_timeout + @request_stub.response.body.should == "abc" + @request_stub.response.should_timeout.should be_true + end + + it "should allow multiple timeouts to be declared" do + @request_stub.to_timeout.then.to_timeout.then.to_return(:body => "abc") + @request_stub.response.should_timeout.should be_true + @request_stub.response.should_timeout.should be_true + @request_stub.response.body.should == "abc" + end + + end + describe "times" do diff --git a/spec/response_spec.rb b/spec/response_spec.rb index f3bc93804..c82cb06a7 100644 --- a/spec/response_spec.rb +++ b/spec/response_spec.rb @@ -64,6 +64,20 @@ end end + + describe "timeout" do + + it "should know if it should timeout" do + @response = Response.new(:should_timeout => true) + @response.should_timeout.should be_true + end + + it "should not timeout by default" do + @response = Response.new + @response.should_timeout.should be_false + end + + end describe "body" do diff --git a/spec/webmock_spec.rb b/spec/webmock_spec.rb index 49584f781..616b0a2c7 100644 --- a/spec/webmock_spec.rb +++ b/spec/webmock_spec.rb @@ -291,6 +291,25 @@ class MyException < StandardError; end; end + describe "raising timeout errors" do + + it "should raise timeout exception if declared in a stubbed response" do + stub_http_request(:get, "www.example.com").to_timeout + lambda { + http_request(:get, "http://www.example.com/") + }.should raise_error(client_timeout_exception_class) + end + + it "should raise exception if declared in a stubbed response after returning declared response" do + stub_http_request(:get, "www.example.com").to_return(:body => "abc").then.to_timeout + http_request(:get, "http://www.example.com/").body.should == "abc" + lambda { + http_request(:get, "http://www.example.com/") + }.should raise_error(client_timeout_exception_class) + end + + end + describe "returning stubbed responses" do it "should return declared body" do