Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 45 additions & 7 deletions lib/active_resource/http_mock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,29 @@ class InvalidRequestError < StandardError; end # :nodoc:
# assert_equal "Matz", person.name
# end
#
# Each method can also accept a block. The mock will yield an
# ActiveResource::Request instance to the block it handles a matching request.
#
# def setup
# @matz = { person: { id: 1, name: "Matz" } }
#
# ActiveResource::HttpMock.respond_to do |mock|
# mock.get "/people.json", omit_query_params: true do |request|
# if request.path.split("?").includes?("name=Matz")
# { people: [ @matz ] }.to_json
# else
# { people: [] }.to_json
# end
# end
# end
# end
#
# def test_get_matz
# people = Person.where(name: "Matz")
# assert_equal [ "Matz" ], people.map(&:name)
# end
#
# When a block is passed to the mock, it ignores the +body+, +status+, and +response_headers+ arguments.
class HttpMock
class Responder # :nodoc:
def initialize(responses)
Expand All @@ -62,9 +85,10 @@ def initialize(responses)
# @responses[Request.new(:post, path, nil, request_headers, options)] = Response.new(body || "", status, response_headers)
# end
module_eval <<-EOE, __FILE__, __LINE__ + 1
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {}, options = {})
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {}, options = {}, &response)
options = body if response
request = Request.new(:#{method}, path, nil, request_headers, options)
response = Response.new(body || "", status, response_headers)
response = Response.new(body || "", status, response_headers) unless response

delete_duplicate_responses(request)

Expand Down Expand Up @@ -154,12 +178,12 @@ def responses
# === Example
#
# ActiveResource::HttpMock.respond_to do |mock|
# mock.send(:get, "/people/1", {}, "JSON1")
# mock.get("/people/1", {}, "JSON1")
# end
# ActiveResource::HttpMock.responses.length #=> 1
#
# ActiveResource::HttpMock.respond_to(false) do |mock|
# mock.send(:get, "/people/2", {}, "JSON2")
# mock.get("/people/2", {}, "JSON2")
# end
# ActiveResource::HttpMock.responses.length #=> 2
#
Expand All @@ -169,7 +193,7 @@ def responses
# === Example
#
# ActiveResource::HttpMock.respond_to do |mock|
# mock.send(:get, "/people/1", {}, "JSON1")
# mock.get("/people/1", {}, "JSON1")
# end
# ActiveResource::HttpMock.responses.length #=> 1
#
Expand Down Expand Up @@ -248,7 +272,10 @@ def net_connection_disabled?
# request = ActiveResource::Request.new(:post, path, body, headers, options)
# self.class.requests << request
# if response = self.class.responses.assoc(request)
# response[1]
# response = response[1]
# response = response.call(request) if response.respond_to?(:call)
#
# Response.wrap(response)
# else
# raise InvalidRequestError.new("Could not find a response recorded for #{request.to_s} - Responses recorded are: - #{inspect_responses}")
# end
Expand All @@ -258,7 +285,10 @@ def #{method}(path, #{'body, ' if has_body}headers, options = {})
request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers, options)
self.class.requests << request
if response = self.class.responses.assoc(request)
response[1]
response = response[1]
response = response.call(request) if response.respond_to?(:call)

Response.wrap(response)
else
raise InvalidRequestError.new("Could not find a response recorded for \#{request.to_s} - Responses recorded are: \#{inspect_responses}")
end
Expand Down Expand Up @@ -321,6 +351,14 @@ def headers_match?(req)
class Response
attr_accessor :body, :message, :code, :headers

def self.wrap(response) # :nodoc:
case response
when self then response
when String then new(response)
else new(nil)
end
end

def initialize(body, message = 200, headers = {})
@body, @message, @headers = body, message.to_s, headers
@code = @message[0, 3].to_i
Expand Down
77 changes: 77 additions & 0 deletions test/cases/http_mock_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,30 @@ class HttpMockTest < ActiveSupport::TestCase

FORMAT_HEADER = ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES

test "Response.wrap returns the same Response instance" do
response = ActiveResource::Response.new("hello")

assert_same response, ActiveResource::Response.wrap(response)
end

test "Response.wrap returns a Response instance from a String" do
response = ActiveResource::Response.wrap("hello")

assert_equal 200, response.code
assert_equal "hello", response.body
assert_equal "hello".size, response["Content-Length"].to_i
end

test "Response.wrap returns a Response instance from other values" do
[ nil, Object.new ].each do |value|
response = ActiveResource::Response.wrap(value)

assert_equal 200, response.code
assert_nil response.body
assert_equal 0, response["Content-Length"].to_i
end
end

[ :post, :patch, :put, :get, :delete, :head ].each do |method|
test "responds to simple #{method} request" do
ActiveResource::HttpMock.respond_to do |mock|
Expand Down Expand Up @@ -72,6 +96,38 @@ class HttpMockTest < ActiveSupport::TestCase
request(method, "/people/1", FORMAT_HEADER[method] => "application/xml")
end
end

test "responds to #{method} request with a block that returns a String" do
ActiveResource::HttpMock.respond_to do |mock|
mock.send(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }) do
"Response"
end
end

assert_equal "Response", request(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }, "Request").body
end

test "responds to #{method} request with a block that returns an ActiveResource::Response" do
ActiveResource::HttpMock.respond_to do |mock|
mock.send(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }) do
ActiveResource::Response.new("Response")
end
end

assert_equal "Response", request(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }, "Request").body
end

test "yields request to the #{method} mock block" do
ActiveResource::HttpMock.respond_to do |mock|
mock.send(method, "/people/1") do |request|
assert_kind_of ActiveResource::Request, request

ActiveResource::Response.new("Response")
end
end

assert_equal "Response", request(method, "/people/1").body
end
end

test "allows you to send in pairs directly to the respond_to method" do
Expand Down Expand Up @@ -154,6 +210,27 @@ class HttpMockTest < ActiveSupport::TestCase
assert_equal 1, ActiveResource::HttpMock.responses.length
end

test "can ignore query params when yielding get request to the block" do
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/1", {}, omit_query_in_path: true do |request|
assert_kind_of ActiveResource::Request, request

ActiveResource::Response.new(request.path)
end
end

assert_equal "/people/1?key=value", request(:get, "/people/1?key=value").body
end

test "can map a request to a block" do
request = ActiveResource::Request.new(:get, "/people/1", nil, {}, omit_query_in_path: true)
response = ->(req) { ActiveResource::Response.new(req.path) }

ActiveResource::HttpMock.respond_to(request => response)

assert_equal "/people/1?key=value", request(:get, "/people/1?key=value").body
end

test "allows you to replace the existing response with the same request by passing pairs" do
ActiveResource::HttpMock.respond_to do |mock|
mock.send(:get, "/people/1", {}, "JSON1")
Expand Down