From d378933565074ad8684648b2aa66b41ac4d508a7 Mon Sep 17 00:00:00 2001 From: Ryan Kinderman Date: Wed, 16 Jan 2013 15:27:41 -0600 Subject: [PATCH 1/2] Adding support for lazy construction of responses from stubbed requests --- lib/typhoeus/expectation.rb | 54 +++++++++++++++++++++---- lib/typhoeus/hydra/stubbable.rb | 4 +- lib/typhoeus/request/stubbable.rb | 4 +- spec/typhoeus/expectation_spec.rb | 51 +++++++++++++++++++---- spec/typhoeus/hydra/before_spec.rb | 12 +++--- spec/typhoeus/hydra/stubbable_spec.rb | 2 +- spec/typhoeus/request/before_spec.rb | 12 +++--- spec/typhoeus/request/stubbable_spec.rb | 4 +- 8 files changed, 109 insertions(+), 34 deletions(-) diff --git a/lib/typhoeus/expectation.rb b/lib/typhoeus/expectation.rb index 32f1ffa4..6053c3d2 100644 --- a/lib/typhoeus/expectation.rb +++ b/lib/typhoeus/expectation.rb @@ -14,6 +14,30 @@ module Typhoeus # actual = Typhoeus.get("www.example.com") # expected == actual # #=> true + # + # @example Stub a request and get a lazily-constructed response containing data from actual widgets that exist in the system when the stubbed request is made. + # Typhoeus.stub("www.example.com/widgets").and_return do + # actual_widgets = Widget.all + # Typhoeus::Response.new( + # :body => actual_widgets.inject([]) do |ids, widget| + # ids << widget.id + # end.join(",") + # ) + # end + # + # @example Stub a request and get a lazily-constructed response in the format requested. + # Typhoeus.stub("www.example.com").and_return do |request| + # accept = (request.options[:headers]||{})['Accept'] || "application/json" + # format = accept.split(",").first + # body_obj = { 'things' => [ { 'id' => 'foo' } ] } + # + # Typhoeus::Response.new( + # :headers => { + # 'Content-Type' => format + # }, + # :body => SERIALIZERS[format].serialize(body_obj) + # ) + # end class Expectation # @api private @@ -47,15 +71,26 @@ def clear all.clear end - # Returns expecation matching the provided - # request. + # Returns stubbed response matching the + # provided request # - # @example Find expectation. - # Typhoeus::Expectation.find_by(request) + # @example Find response + # Typhoeus::Expectation.response_for(request) # - # @return [ Expectation ] The matching expectation. + # @return [ Typhoeus::Response ] The stubbed response from a + # matching expectation, or nil if no matching expectation + # is found. # # @api private + def response_for(request) + expectation = find_by(request) + return nil if expectation.nil? + + expectation.response(request) + end + + private + def find_by(request) all.find do |expectation| expectation.matches?(request) @@ -101,8 +136,8 @@ def stubbed_from(value) # expectation.and_return(response) # # @return [ void ] - def and_return(response) - responses << response + def and_return(response=nil, &block) + responses << (response.nil? ? block : response) end # Checks wether this expectation matches @@ -142,8 +177,11 @@ def responses # @return [ Response ] The response. # # @api private - def response + def response(request) response = responses.fetch(@response_counter, responses.last) + if response.respond_to?(:call) + response = response.call(request) + end @response_counter += 1 response.mock = @from || true response diff --git a/lib/typhoeus/hydra/stubbable.rb b/lib/typhoeus/hydra/stubbable.rb index 47e2aee5..626d311e 100644 --- a/lib/typhoeus/hydra/stubbable.rb +++ b/lib/typhoeus/hydra/stubbable.rb @@ -15,8 +15,8 @@ module Stubbable # @example Add the request. # hydra.add(request) def add(request) - if expectation = Expectation.find_by(request) - request.finish(expectation.response) + if response = Expectation.response_for(request) + request.finish(response) else super end diff --git a/lib/typhoeus/request/stubbable.rb b/lib/typhoeus/request/stubbable.rb index ace5330c..c44a294d 100644 --- a/lib/typhoeus/request/stubbable.rb +++ b/lib/typhoeus/request/stubbable.rb @@ -17,8 +17,8 @@ module Stubbable # # @return [ Response ] The response. def run - if expectation = Expectation.find_by(self) - finish(expectation.response) + if response = Expectation.response_for(self) + finish(response) else super end diff --git a/spec/typhoeus/expectation_spec.rb b/spec/typhoeus/expectation_spec.rb index 28d0cc1a..aa77507b 100644 --- a/spec/typhoeus/expectation_spec.rb +++ b/spec/typhoeus/expectation_spec.rb @@ -47,13 +47,23 @@ end end - describe ".find_by" do + describe ".response_for" do let(:request) { Typhoeus::Request.new("") } + let(:stubbed_response) { Typhoeus::Response.new } - it "returns a dummy when expectations not empty" do + it "finds a matching expectation and returns its next response" do Typhoeus::Expectation.all << expectation expectation.should_receive(:matches?).with(request).and_return(true) - expect(Typhoeus::Expectation.find_by(request)).to eq(expectation) + expectation.should_receive(:response).with(request).and_return(stubbed_response) + + response = Typhoeus::Expectation.response_for(request) + + expect(response).to be(stubbed_response) + end + + it "returns nil if no matching expectation is found" do + response = Typhoeus::Expectation.response_for(request) + expect(response).to be(nil) end end @@ -83,6 +93,14 @@ expect(expectation.responses).to eq([1, 2]) end end + + context "when block" do + it "adds to responses" do + block = Proc.new {} + expectation.and_return &block + expect(expectation.responses).to eq([block]) + end + end end describe "#responses" do @@ -92,13 +110,32 @@ end describe "#response" do + let(:request) { Typhoeus::Request.new("") } + before { expectation.instance_variable_set(:@responses, responses) } context "when one response" do - let(:responses) { [Typhoeus::Response.new] } + context "is pre-constructed" do + let(:responses) { [Typhoeus::Response.new] } + + it "returns response" do + expect(expectation.response(request)).to be(responses[0]) + end + end - it "returns response" do - expect(expectation.response).to be(responses[0]) + context "is lazily-constructed" do + def construct_response(request) + @request_from_response_construction = request + lazily_constructed_response + end + + let(:lazily_constructed_response) { Typhoeus::Response.new } + let(:responses) { [ Proc.new { |request| construct_response(request) } ] } + + it "returns response" do + expect(expectation.response(request)).to be(lazily_constructed_response) + expect(@request_from_response_construction).to be(request) + end end end @@ -107,7 +144,7 @@ it "returns one by one" do 3.times do |i| - expect(expectation.response).to eq(responses[i]) + expect(expectation.response(request)).to be(responses[i]) end end end diff --git a/spec/typhoeus/hydra/before_spec.rb b/spec/typhoeus/hydra/before_spec.rb index 308fc87f..fa8faf44 100644 --- a/spec/typhoeus/hydra/before_spec.rb +++ b/spec/typhoeus/hydra/before_spec.rb @@ -16,7 +16,7 @@ context "when true" do it "calls super" do Typhoeus.before { true } - Typhoeus::Expectation.should_receive(:find_by) + Typhoeus::Expectation.should_receive(:response_for) hydra.add(request) end end @@ -24,7 +24,7 @@ context "when false" do it "doesn't call super" do Typhoeus.before { false } - Typhoeus::Expectation.should_receive(:find_by).never + Typhoeus::Expectation.should_receive(:response_for).never hydra.add(request) end end @@ -32,7 +32,7 @@ context "when response" do it "doesn't call super" do Typhoeus.before { Typhoeus::Response.new } - Typhoeus::Expectation.should_receive(:find_by).never + Typhoeus::Expectation.should_receive(:response_for).never hydra.add(request) end end @@ -43,7 +43,7 @@ before { 3.times { Typhoeus.before { |r| String.new(r.base_url) } } } it "calls super" do - Typhoeus::Expectation.should_receive(:find_by) + Typhoeus::Expectation.should_receive(:response_for) hydra.add(request) end @@ -61,7 +61,7 @@ end it "doesn't call super" do - Typhoeus::Expectation.should_receive(:find_by).never + Typhoeus::Expectation.should_receive(:response_for).never hydra.add(request) end @@ -75,7 +75,7 @@ context "when no before" do it "calls super" do - Typhoeus::Expectation.should_receive(:find_by) + Typhoeus::Expectation.should_receive(:response_for) hydra.add(request) end end diff --git a/spec/typhoeus/hydra/stubbable_spec.rb b/spec/typhoeus/hydra/stubbable_spec.rb index 159e6674..00bccbe0 100644 --- a/spec/typhoeus/hydra/stubbable_spec.rb +++ b/spec/typhoeus/hydra/stubbable_spec.rb @@ -9,7 +9,7 @@ before { Typhoeus.stub(base_url).and_return(response) } describe "#add" do - it "checks expactations" do + it "checks expectations" do hydra.add(request) end diff --git a/spec/typhoeus/request/before_spec.rb b/spec/typhoeus/request/before_spec.rb index ba502128..8b7b4309 100644 --- a/spec/typhoeus/request/before_spec.rb +++ b/spec/typhoeus/request/before_spec.rb @@ -15,7 +15,7 @@ context "when true" do it "calls super" do Typhoeus.before { true } - Typhoeus::Expectation.should_receive(:find_by) + Typhoeus::Expectation.should_receive(:response_for) request.run end end @@ -23,7 +23,7 @@ context "when false" do it "doesn't call super" do Typhoeus.before { false } - Typhoeus::Expectation.should_receive(:find_by).never + Typhoeus::Expectation.should_receive(:response_for).never request.run end @@ -36,7 +36,7 @@ context "when a response" do it "doesn't call super" do Typhoeus.before { Typhoeus::Response.new } - Typhoeus::Expectation.should_receive(:find_by).never + Typhoeus::Expectation.should_receive(:response_for).never request.run end @@ -52,7 +52,7 @@ before { 3.times { Typhoeus.before { |r| String.new(r.base_url) } } } it "calls super" do - Typhoeus::Expectation.should_receive(:find_by) + Typhoeus::Expectation.should_receive(:response_for) request.run end @@ -70,7 +70,7 @@ end it "doesn't call super" do - Typhoeus::Expectation.should_receive(:find_by).never + Typhoeus::Expectation.should_receive(:response_for).never request.run end @@ -84,7 +84,7 @@ context "when no before" do it "calls super" do - Typhoeus::Expectation.should_receive(:find_by) + Typhoeus::Expectation.should_receive(:response_for) request.run end end diff --git a/spec/typhoeus/request/stubbable_spec.rb b/spec/typhoeus/request/stubbable_spec.rb index c1b83f1f..bf057b49 100644 --- a/spec/typhoeus/request/stubbable_spec.rb +++ b/spec/typhoeus/request/stubbable_spec.rb @@ -7,8 +7,8 @@ before { Typhoeus.stub(base_url).and_return(response) } - describe "#queue" do - it "checks expactations" do + describe "#run" do + it "checks expectations" do request.run end From ac18808dc60de86b8ba7118e4f7140b5bd17cb23 Mon Sep 17 00:00:00 2001 From: Ryan Kinderman Date: Sat, 9 Mar 2013 15:02:36 -0600 Subject: [PATCH 2/2] Accepting the lazy response construction block of a stubbed request on the call to Typhoeus.stub --- lib/typhoeus.rb | 12 +++++++----- lib/typhoeus/expectation.rb | 4 ++-- spec/typhoeus_spec.rb | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/typhoeus.rb b/lib/typhoeus.rb index 6b17f3c7..fc919036 100644 --- a/lib/typhoeus.rb +++ b/lib/typhoeus.rb @@ -73,13 +73,15 @@ def configure # @return [ Typhoeus::Expectation ] The expecatation. # # @see Typhoeus::Expectation - def stub(base_url, options = {}) + def stub(base_url, options = {}, &block) expectation = Expectation.all.find{ |e| e.base_url == base_url && e.options == options } - return expectation if expectation - - Expectation.new(base_url, options).tap do |new_expectation| - Expectation.all << new_expectation + if expectation.nil? + expectation = Expectation.new(base_url, options) + Expectation.all << expectation end + + expectation.and_return &block unless block.nil? + expectation end # Add before callbacks. diff --git a/lib/typhoeus/expectation.rb b/lib/typhoeus/expectation.rb index 6053c3d2..71075f20 100644 --- a/lib/typhoeus/expectation.rb +++ b/lib/typhoeus/expectation.rb @@ -16,7 +16,7 @@ module Typhoeus # #=> true # # @example Stub a request and get a lazily-constructed response containing data from actual widgets that exist in the system when the stubbed request is made. - # Typhoeus.stub("www.example.com/widgets").and_return do + # Typhoeus.stub("www.example.com/widgets") do # actual_widgets = Widget.all # Typhoeus::Response.new( # :body => actual_widgets.inject([]) do |ids, widget| @@ -26,7 +26,7 @@ module Typhoeus # end # # @example Stub a request and get a lazily-constructed response in the format requested. - # Typhoeus.stub("www.example.com").and_return do |request| + # Typhoeus.stub("www.example.com") do |request| # accept = (request.options[:headers]||{})['Accept'] || "application/json" # format = accept.split(",").first # body_obj = { 'things' => [ { 'id' => 'foo' } ] } diff --git a/spec/typhoeus_spec.rb b/spec/typhoeus_spec.rb index 6e1877d9..56045c12 100644 --- a/spec/typhoeus_spec.rb +++ b/spec/typhoeus_spec.rb @@ -21,7 +21,22 @@ describe ".stub" do let(:base_url) { "www.example.com" } + shared_examples "lazy response construction" do + it "calls the block to construct a response when a request matches the stub" do + expected_response = Typhoeus::Response.new + Typhoeus.stub(base_url) do |request| + expected_response + end + + response = Typhoeus.get(base_url) + + expect(response).to be(expected_response) + end + end + context "when no similar expectation exists" do + include_examples "lazy response construction" + it "returns expectation" do expect(Typhoeus.stub(base_url)).to be_a(Typhoeus::Expectation) end @@ -33,6 +48,8 @@ end context "when similar expectation exists" do + include_examples "lazy response construction" + let(:expectation) { Typhoeus::Expectation.new(base_url) } before { Typhoeus::Expectation.all << expectation }