Skip to content

Commit 46eb4a4

Browse files
committed
HttpMock: Accept block arguments
Extend the `ActiveResource::HttpMock` declaration DSL to accept a block argument. When passed a block, the mock will yield an `ActiveResource::Request` instance to the block it handles a matching request. ```ruby 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.
1 parent 62b941e commit 46eb4a4

File tree

2 files changed

+122
-7
lines changed

2 files changed

+122
-7
lines changed

lib/active_resource/http_mock.rb

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,29 @@ class InvalidRequestError < StandardError; end # :nodoc:
5151
# assert_equal "Matz", person.name
5252
# end
5353
#
54+
# Each method can also accept a block. The mock will yield an
55+
# ActiveResource::Request instance to the block it handles a matching request.
56+
#
57+
# def setup
58+
# @matz = { person: { id: 1, name: "Matz" } }
59+
#
60+
# ActiveResource::HttpMock.respond_to do |mock|
61+
# mock.get "/people.json", omit_query_params: true do |request|
62+
# if request.path.split("?").includes?("name=Matz")
63+
# { people: [ @matz ] }.to_json
64+
# else
65+
# { people: [] }.to_json
66+
# end
67+
# end
68+
# end
69+
# end
70+
#
71+
# def test_get_matz
72+
# people = Person.where(name: "Matz")
73+
# assert_equal [ "Matz" ], people.map(&:name)
74+
# end
75+
#
76+
# When a block is passed to the mock, it ignores the +body+, +status+, and +response_headers+ arguments.
5477
class HttpMock
5578
class Responder # :nodoc:
5679
def initialize(responses)
@@ -62,9 +85,10 @@ def initialize(responses)
6285
# @responses[Request.new(:post, path, nil, request_headers, options)] = Response.new(body || "", status, response_headers)
6386
# end
6487
module_eval <<-EOE, __FILE__, __LINE__ + 1
65-
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {}, options = {})
88+
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {}, options = {}, &response)
89+
options = body if response
6690
request = Request.new(:#{method}, path, nil, request_headers, options)
67-
response = Response.new(body || "", status, response_headers)
91+
response = Response.new(body || "", status, response_headers) unless response
6892
6993
delete_duplicate_responses(request)
7094
@@ -154,12 +178,12 @@ def responses
154178
# === Example
155179
#
156180
# ActiveResource::HttpMock.respond_to do |mock|
157-
# mock.send(:get, "/people/1", {}, "JSON1")
181+
# mock.get("/people/1", {}, "JSON1")
158182
# end
159183
# ActiveResource::HttpMock.responses.length #=> 1
160184
#
161185
# ActiveResource::HttpMock.respond_to(false) do |mock|
162-
# mock.send(:get, "/people/2", {}, "JSON2")
186+
# mock.get("/people/2", {}, "JSON2")
163187
# end
164188
# ActiveResource::HttpMock.responses.length #=> 2
165189
#
@@ -169,7 +193,7 @@ def responses
169193
# === Example
170194
#
171195
# ActiveResource::HttpMock.respond_to do |mock|
172-
# mock.send(:get, "/people/1", {}, "JSON1")
196+
# mock.get("/people/1", {}, "JSON1")
173197
# end
174198
# ActiveResource::HttpMock.responses.length #=> 1
175199
#
@@ -248,7 +272,10 @@ def net_connection_disabled?
248272
# request = ActiveResource::Request.new(:post, path, body, headers, options)
249273
# self.class.requests << request
250274
# if response = self.class.responses.assoc(request)
251-
# response[1]
275+
# response = response[1]
276+
# response = response.call(request) if response.respond_to?(:call)
277+
#
278+
# Response.wrap(response)
252279
# else
253280
# raise InvalidRequestError.new("Could not find a response recorded for #{request.to_s} - Responses recorded are: - #{inspect_responses}")
254281
# end
@@ -258,7 +285,10 @@ def #{method}(path, #{'body, ' if has_body}headers, options = {})
258285
request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers, options)
259286
self.class.requests << request
260287
if response = self.class.responses.assoc(request)
261-
response[1]
288+
response = response[1]
289+
response = response.call(request) if response.respond_to?(:call)
290+
291+
Response.wrap(response)
262292
else
263293
raise InvalidRequestError.new("Could not find a response recorded for \#{request.to_s} - Responses recorded are: \#{inspect_responses}")
264294
end
@@ -321,6 +351,14 @@ def headers_match?(req)
321351
class Response
322352
attr_accessor :body, :message, :code, :headers
323353

354+
def self.wrap(response) # :nodoc:
355+
case response
356+
when self then response
357+
when String then new(response)
358+
else new(nil)
359+
end
360+
end
361+
324362
def initialize(body, message = 200, headers = {})
325363
@body, @message, @headers = body, message.to_s, headers
326364
@code = @message[0, 3].to_i

test/cases/http_mock_test.rb

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,30 @@ class HttpMockTest < ActiveSupport::TestCase
1010

1111
FORMAT_HEADER = ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES
1212

13+
test "Response.wrap returns the same Response instance" do
14+
response = ActiveResource::Response.new("hello")
15+
16+
assert_same response, ActiveResource::Response.wrap(response)
17+
end
18+
19+
test "Response.wrap returns a Response instance from a String" do
20+
response = ActiveResource::Response.wrap("hello")
21+
22+
assert_equal 200, response.code
23+
assert_equal "hello", response.body
24+
assert_equal "hello".size, response["Content-Length"].to_i
25+
end
26+
27+
test "Response.wrap returns a Response instance from other values" do
28+
[ nil, Object.new ].each do |value|
29+
response = ActiveResource::Response.wrap(value)
30+
31+
assert_equal 200, response.code
32+
assert_nil response.body
33+
assert_equal 0, response["Content-Length"].to_i
34+
end
35+
end
36+
1337
[ :post, :patch, :put, :get, :delete, :head ].each do |method|
1438
test "responds to simple #{method} request" do
1539
ActiveResource::HttpMock.respond_to do |mock|
@@ -72,6 +96,38 @@ class HttpMockTest < ActiveSupport::TestCase
7296
request(method, "/people/1", FORMAT_HEADER[method] => "application/xml")
7397
end
7498
end
99+
100+
test "responds to #{method} request with a block that returns a String" do
101+
ActiveResource::HttpMock.respond_to do |mock|
102+
mock.send(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }) do
103+
"Response"
104+
end
105+
end
106+
107+
assert_equal "Response", request(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }, "Request").body
108+
end
109+
110+
test "responds to #{method} request with a block that returns an ActiveResource::Response" do
111+
ActiveResource::HttpMock.respond_to do |mock|
112+
mock.send(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }) do
113+
ActiveResource::Response.new("Response")
114+
end
115+
end
116+
117+
assert_equal "Response", request(method, "/people/1", { FORMAT_HEADER[method] => "application/json" }, "Request").body
118+
end
119+
120+
test "yields request to the #{method} mock block" do
121+
ActiveResource::HttpMock.respond_to do |mock|
122+
mock.send(method, "/people/1") do |request|
123+
assert_kind_of ActiveResource::Request, request
124+
125+
ActiveResource::Response.new("Response")
126+
end
127+
end
128+
129+
assert_equal "Response", request(method, "/people/1").body
130+
end
75131
end
76132

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

213+
test "can ignore query params when yielding get request to the block" do
214+
ActiveResource::HttpMock.respond_to do |mock|
215+
mock.get "/people/1", {}, omit_query_in_path: true do |request|
216+
assert_kind_of ActiveResource::Request, request
217+
218+
ActiveResource::Response.new(request.path)
219+
end
220+
end
221+
222+
assert_equal "/people/1?key=value", request(:get, "/people/1?key=value").body
223+
end
224+
225+
test "can map a request to a block" do
226+
request = ActiveResource::Request.new(:get, "/people/1", nil, {}, omit_query_in_path: true)
227+
response = ->(req) { ActiveResource::Response.new(req.path) }
228+
229+
ActiveResource::HttpMock.respond_to(request => response)
230+
231+
assert_equal "/people/1?key=value", request(:get, "/people/1?key=value").body
232+
end
233+
157234
test "allows you to replace the existing response with the same request by passing pairs" do
158235
ActiveResource::HttpMock.respond_to do |mock|
159236
mock.send(:get, "/people/1", {}, "JSON1")

0 commit comments

Comments
 (0)