harper 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -43,11 +43,24 @@ Harper will silently replace it with the new mock.
43
43
  * *status*: The HTTP status code to provide. Optional. Defaults to 200.
44
44
  * *content_type*: What content-type header to set when responding with
45
45
  the mock. Required.
46
- * *body*: A string to be used as the HTTP response body. Required.
46
+ * *body*: Either a string to be used as the HTTP response body, or an
47
+ array of strings. If an array is provided Harper will cycle through
48
+ the array as the response body for requests. Required.
47
49
 
48
50
  * *delay*: A delay to include when responding to the mock,
49
51
  in milliseconds. Optional. Defaults to no delay.
50
52
 
53
+ * *request_body*: The params are used to compare with the actual request
54
+ body to find out whether the mock applies. This request_body is
55
+ optional and is required only in scenarios where you want multiple
56
+ requests to the same url return different response based on some
57
+ parameter sent in the request. The value passed in can actually be xml
58
+ read from a file, json read from a file or simply values in the request.
59
+ They need not be the whole request body - partial request params work.
60
+ However these must be conspicuously different across the requests i.e
61
+ they should not be so generic that they appear in all requests to
62
+ that url.
63
+
51
64
  ## Typical Use
52
65
 
53
66
  1. Start Harper at the very start of your test run.
@@ -69,6 +69,7 @@ Feature: Mock an HTTP RPC web service
69
69
  Given a defined response mock with a "status" of "<STATUS>"
70
70
  When the application issues a "GET" request to the mock
71
71
  Then the response code should be "<STATUS>"
72
+ When the application removes all registered mocks
72
73
  Examples:
73
74
  | STATUS |
74
75
  | 200 |
@@ -86,6 +87,7 @@ Feature: Mock an HTTP RPC web service
86
87
  Given a defined response mock with a "delay" of "<MILLISECONDS>"
87
88
  When the application issues a "GET" request to the mock
88
89
  Then the response time should be at least "<MILLISECONDS>"
90
+ When the application removes all registered mocks
89
91
  Examples:
90
92
  | MILLISECONDS |
91
93
  | 0 |
@@ -24,3 +24,7 @@ Then /^the "([^"]*)" mock is available at the URL in the "([^"]*)" header$/ do |
24
24
  get :from => id_url
25
25
  response.code.should == 200
26
26
  end
27
+
28
+ When /^the application removes all registered mocks$/ do
29
+ delete_all_mocks
30
+ end
@@ -31,6 +31,10 @@ class ClientApplication
31
31
  @response = self.class.delete @known_mocks[name][:url]
32
32
  end
33
33
 
34
+ def delete_all_mocks
35
+ self.class.delete "/h/mocks"
36
+ end
37
+
34
38
  def get(options)
35
39
  timed { @response = self.class.get options[:from] }
36
40
  end
@@ -27,6 +27,15 @@ module Harper
27
27
  def mock_id(url)
28
28
  [url].pack('m').tr("+/=", "-_.").gsub("\n", '')
29
29
  end
30
+
31
+ def retrieve_mock(mock_id, http_method, request_body)
32
+ if @@mocks[mock_id]
33
+ return @@mocks[mock_id].first if @@mocks[mock_id].length == 1
34
+ mocks_for_requested_http_method = @@mocks[mock_id].select { |m| m['method'] == http_method}
35
+ mock = mocks_for_requested_http_method.detect{|m| m["request_body"] && request_body =~ /#{m["request_body"]}/} if request_body
36
+ mock ||= mocks_for_requested_http_method.detect{|m| m["request_body"].nil?}
37
+ end
38
+ end
30
39
  end
31
40
 
32
41
  post '/h/mocks' do
@@ -37,10 +46,11 @@ module Harper
37
46
  mock['id'] = mock_id(mock['url'])
38
47
  mock['method'].upcase!
39
48
  mock['delay'] = mock['delay'].to_f / 1000.0
40
- @@mocks[mock['id']] = mock
49
+ @@mocks[mock['id']] ||= []
50
+ @@mocks[mock['id']] << mock
41
51
 
42
52
  logger.info("Created mock for endpoint: '#{mock['url']}'")
43
-
53
+
44
54
  headers['location'] = "/h/mocks/#{mock['id']}"
45
55
  status "201"
46
56
  end
@@ -76,12 +86,14 @@ module Harper
76
86
 
77
87
  logger.debug("#{request.request_method} request for a mock: '#{request.path}'")
78
88
 
79
- mock = @@mocks[mock_id]
80
- if mock && request.request_method == mock['method']
89
+ request_body = request.body.read if request.body
90
+ mock = retrieve_mock(mock_id, request.request_method, request_body)
91
+
92
+ if mock
81
93
  content_type mock['content-type']
82
94
  status mock['status'] || "200"
83
95
  sleep mock['delay']
84
-
96
+
85
97
  logger.info("Serving mocked body for endpoint: '#{mock['url']}'")
86
98
 
87
99
  case mock['body']
@@ -1,3 +1,3 @@
1
1
  module Harper
2
- Version = "1.0.0"
2
+ Version = "1.1.0"
3
3
  end
@@ -5,7 +5,6 @@ require 'harper'
5
5
 
6
6
  describe Harper::App do
7
7
  include Rack::Test::Methods
8
-
9
8
  supported_verbs = ["GET", "POST", "PUT", "DELETE"]
10
9
 
11
10
  let(:app) { Harper::App.new }
@@ -68,7 +67,7 @@ describe Harper::App do
68
67
  before(:each) do
69
68
  post '/h/mocks', mock_def
70
69
  end
71
-
70
+
72
71
  it "should respond with a 201 created" do
73
72
  last_response.status.should == 201
74
73
  end
@@ -180,4 +179,119 @@ describe Harper::App do
180
179
 
181
180
  end
182
181
 
182
+ context "supporting multiple mocks for same request url" do
183
+ it "should return the mock response corresponding to the request body for xml requests" do
184
+ url = "/service-url"
185
+
186
+ xml_string_for_request_one = <<-EOF
187
+ <mydoc>
188
+ <someelement attribute="nanoo">first request</someelement>
189
+ </mydoc>
190
+ EOF
191
+ body_one = "response body for request one"
192
+ mock_def_for_first_request =
193
+ { :method => "POST",
194
+ :url => url,
195
+ :'content-type' => "application/xml",
196
+ :body => body_one,
197
+ :request_body => xml_string_for_request_one
198
+ }.to_json
199
+
200
+ post '/h/mocks', mock_def_for_first_request
201
+
202
+ xml_string_for_request_two = <<-EOF
203
+ <mydoc>
204
+ <someelement attribute="nanoo">Other request</someelement>
205
+ </mydoc>
206
+ EOF
207
+ body_two = "response body for request two"
208
+ mock_def_for_second_request =
209
+ { :method => "POST",
210
+ :url => url,
211
+ :'content-type' => "application/xml",
212
+ :body => body_two,
213
+ :request_body => xml_string_for_request_two
214
+ }.to_json
215
+
216
+ post '/h/mocks', mock_def_for_second_request
217
+
218
+ post url, xml_string_for_request_one
219
+ last_response.body.should == body_one
220
+
221
+ post url, xml_string_for_request_two
222
+ last_response.body.should == body_two
223
+ end
224
+
225
+ it "should return the mock response corresponding to the request body for json requests" do
226
+ url = "/service-url"
227
+
228
+ request_json_one = {:param => "param 1"}.to_json
229
+ body_one = "response body for request one"
230
+ mock_def_for_first_request =
231
+ { :method => "POST",
232
+ :url => url,
233
+ :'content-type' => "application/json",
234
+ :body => body_one,
235
+ :request_body => request_json_one
236
+ }.to_json
237
+
238
+ post '/h/mocks', mock_def_for_first_request
239
+
240
+ request_json_two = {:param => "param 2"}.to_json
241
+ body_two = "response body for request two"
242
+ mock_def_for_second_request =
243
+ { :method => "POST",
244
+ :url => url,
245
+ :'content-type' => "application/json",
246
+ :body => body_two,
247
+ :request_body => request_json_two
248
+ }.to_json
249
+
250
+ post '/h/mocks', mock_def_for_second_request
251
+
252
+ post url, request_json_one
253
+ last_response.body.should == body_one
254
+
255
+ post url, request_json_two
256
+ last_response.body.should == body_two
257
+ end
258
+
259
+ it "should return the correct response for mocks registered without any request body" do
260
+ url = "/service-url"
261
+
262
+ xml_string_for_request_one = <<-EOF
263
+ <mydoc>
264
+ <someelement attribute="nanoo">first request</someelement>
265
+ </mydoc>
266
+ EOF
267
+
268
+ body_one = "response body for request one"
269
+ mock_def_for_first_request =
270
+ { :method => "POST",
271
+ :url => url,
272
+ :'content-type' => "application/xml",
273
+ :body => body_one,
274
+ :request_body => xml_string_for_request_one
275
+ }.to_json
276
+
277
+ post '/h/mocks', mock_def_for_first_request
278
+
279
+ body_two = "response body for request two"
280
+ mock_def_for_second_request_without_request_body =
281
+ { :method => "POST",
282
+ :url => url,
283
+ :'content-type' => "application/xml",
284
+ :body => body_two
285
+ }.to_json
286
+
287
+ post '/h/mocks', mock_def_for_second_request_without_request_body
288
+
289
+ post url, xml_string_for_request_one
290
+ last_response.body.should == body_one
291
+
292
+ post url
293
+ last_response.body.should == body_two
294
+ end
295
+ end
296
+
183
297
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-06 00:00:00.000000000 Z
12
+ date: 2012-07-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
16
- requirement: &70133087544280 !ruby/object:Gem::Requirement
16
+ requirement: &70183758661480 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70133087544280
24
+ version_requirements: *70183758661480
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: httparty
27
- requirement: &70133087543880 !ruby/object:Gem::Requirement
27
+ requirement: &70183758661020 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70133087543880
35
+ version_requirements: *70183758661020
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: json
38
- requirement: &70133087543060 !ruby/object:Gem::Requirement
38
+ requirement: &70183758676260 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.4.6
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70133087543060
46
+ version_requirements: *70183758676260
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: trollop
49
- requirement: &70133087542500 !ruby/object:Gem::Requirement
49
+ requirement: &70183758674400 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70133087542500
57
+ version_requirements: *70183758674400
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rack-test
60
- requirement: &70133087541880 !ruby/object:Gem::Requirement
60
+ requirement: &70183758673380 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70133087541880
68
+ version_requirements: *70183758673380
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: sham_rack
71
- requirement: &70133087541260 !ruby/object:Gem::Requirement
71
+ requirement: &70183758672900 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70133087541260
79
+ version_requirements: *70183758672900
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec
82
- requirement: &70133087540700 !ruby/object:Gem::Requirement
82
+ requirement: &70183758672340 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70133087540700
90
+ version_requirements: *70183758672340
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: cucumber
93
- requirement: &70133087540100 !ruby/object:Gem::Requirement
93
+ requirement: &70183758671800 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0.10'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70133087540100
101
+ version_requirements: *70183758671800
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: cucumber-sinatra
104
- requirement: &70133087535840 !ruby/object:Gem::Requirement
104
+ requirement: &70183758670580 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,7 +109,7 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70133087535840
112
+ version_requirements: *70183758670580
113
113
  description: Dead simple mocking for HTTP services, a Sinatra app
114
114
  email:
115
115
  - giles.alexander@gmail.com