Skip to content

Commit

Permalink
Merge pull request #275 from ryankinderman/master-stubbed-lazy-response
Browse files Browse the repository at this point in the history
Adding support for lazy construction of responses from stubbed requests
  • Loading branch information
hanshasselberg committed Mar 29, 2013
2 parents e3222c4 + ac18808 commit c53f026
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 39 deletions.
12 changes: 7 additions & 5 deletions lib/typhoeus.rb
Expand Up @@ -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.
Expand Down
54 changes: 46 additions & 8 deletions lib/typhoeus/expectation.rb
Expand Up @@ -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") 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") 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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions lib/typhoeus/hydra/stubbable.rb
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions lib/typhoeus/request/stubbable.rb
Expand Up @@ -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
Expand Down
51 changes: 44 additions & 7 deletions spec/typhoeus/expectation_spec.rb
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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
Expand Down
12 changes: 6 additions & 6 deletions spec/typhoeus/hydra/before_spec.rb
Expand Up @@ -16,23 +16,23 @@
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

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

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
Expand All @@ -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

Expand All @@ -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

Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion spec/typhoeus/hydra/stubbable_spec.rb
Expand Up @@ -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

Expand Down
12 changes: 6 additions & 6 deletions spec/typhoeus/request/before_spec.rb
Expand Up @@ -15,15 +15,15 @@
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

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

Expand All @@ -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

Expand All @@ -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

Expand All @@ -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

Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions spec/typhoeus/request/stubbable_spec.rb
Expand Up @@ -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

Expand Down
17 changes: 17 additions & 0 deletions spec/typhoeus_spec.rb
Expand Up @@ -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
Expand All @@ -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 }

Expand Down

0 comments on commit c53f026

Please sign in to comment.