mock_server 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.md +101 -54
  2. data/lib/mock_server/spec/helpers.rb +160 -15
  3. metadata +45 -60
data/README.md CHANGED
@@ -1,84 +1,131 @@
1
1
  # MockServer
2
2
 
3
- You're building a neat javascript application that consume JSON...
3
+ __MockServer let you record interactions with Rack-apps and provide playback with advanced request matching for your tests.__
4
4
 
5
- So you have spec for your models and controller for your API, you do?
5
+ [![Build Status](https://secure.travis-ci.org/unixcharles/mock_server.png?branch=master)](http://travis-ci.org/unixcharles/mock_server)
6
6
 
7
- And you're going to test it with something like capybara?
7
+ MockServer is an answer from a real world problem at [Teambox](http://teambox.com) and we use it extensively for our entire acceptance test suites.
8
8
 
9
- ... The thing is that its going to be slow, and you're testing testing all you're application logic again!
9
+ When building javascript application that communicate with the backend over an API, you will find yourself the entire stack again and again, waiting after the database while you only want ensure that your javascript applicatin is correctly communicating with the server.
10
10
 
11
- MockServer allow you to record your interaction with the server and give you a way to match
12
- the record request with the real request.
11
+ Our solution was to considere our own backend as it was an external API and completely mock the interaction with the API.
13
12
 
14
- Its like a rack [VCR](https://github.com/myronmarston/vcr) for your own API!
13
+ ### Speed. Its fast.
15
14
 
16
- # Recording mode
15
+ Run test against a completely fake server, don't hit you application stack.
17
16
 
18
- Create mount the rack application, in rails
17
+ ### Test isolation.
19
18
 
20
- require 'mock_server/record'
21
- config.middleware.use MockServer::Record,
22
- { :path => 'fixtures/records', :filename => 'uploads'
23
- :routes => [ '/api/*/**', '/api/*/**' ] }
19
+ Avoid duplicated testing. Test your API in its own test suite and let the frontend perform request against fixtures to avoid testing the entire stack (again).
24
20
 
25
- And you're ready to record. Run the test, build so seed by clicking around. whatever.
21
+ MockServer is a very light solution divided in three parts.
26
22
 
27
- # Playback mode
23
+ * a recording Rack middleware
24
+ * a playback Rack middleware
25
+ * an helper module to use inside your tests
28
26
 
29
- require 'mock_server/playback'
30
- config.middleware.use MockServer::Playback,
31
- { :path => 'fixtures/records' }
27
+ # Getting started
32
28
 
33
- Recording your own playback? Noes, don't use the two Rack middleware at the same time.
29
+ ## Installation
34
30
 
35
- # Rspec
31
+ ```bash
32
+ gem install mock_server
33
+ ```
36
34
 
37
- require 'mock_server/spec/helpers'
38
- RSpec.configure do |config|
39
- config.include MockServer::Spec::Helpers
40
- end
35
+ ## Recording mode
41
36
 
42
- In your spec
37
+ Mounting the rack middleware, in rails
43
38
 
44
- before :each do
39
+ ```ruby
40
+ require 'mock_server/record'
41
+ config.middleware.use MockServer::Record,
42
+ { :path => 'fixtures/records', :filename => 'api'
43
+ :routes => [ '/api/*/**', '/api_v2/**' ] }
44
+ ```
45
45
 
46
- # Set the filename where you store the records
47
- mock_server_use_record 'uploads'
46
+ At this point, the `MockServer::Record` middleware will record all the intraction that match the given routes into the `fixtures/records/api.yml` file.
48
47
 
49
- # From now on, those path belong to MockServer.
50
- #
51
- # if we can't match to a record, the server return a 404 and populate the errors stack.
52
- mock_server_enable_routes '**/uploads/*', '**/uploads', '**/folders'
48
+ You can record from your test or just boot the app and click around, be creative.
53
49
 
54
- end
50
+ ## Playback mode
55
51
 
56
- after :each do
57
- mock_server_reset!
58
- end
52
+ Once you are done recording, disable the `MockServer::Record`, and you are ready to use the `MockServer::Playback` middleware.
59
53
 
60
- scenario "Some json api fun" do
61
- mock_server_get('/api/2/projects')
62
- mock_server_post('/api/2/projects') do |request, recorded_request|
54
+ ```ruby
55
+ require 'mock_server/playback'
56
+ config.middleware.use MockServer::Playback,
57
+ { :path => 'fixtures/records' }
58
+ ```
63
59
 
64
- # I use the should helpers because it return the method
65
- # but it won't raise a failure in the test, since it happen
66
- # inside a proc ouside the scenario. But It will populate the
67
- # error stack if can't match anything.
60
+ You are now ready to test.
68
61
 
69
- recorded_request.body.name.should == recorded_request.body.name and
70
- recorded_request.body.name.should == 'MockServer'
62
+ ## Rspec
71
63
 
72
- # Its a normal detect block, so it has to return true to match
73
- # the record response.
74
- }
64
+ MockServer is just two record/playback Rack middleware, but it also come with an helper module to maniplate its settings.
75
65
 
76
- ... fun stuff ...
66
+ You just need to include the module in your test.
77
67
 
78
- mock_server_success_stack.should include('/api/2/projects')
79
- mock_server_errors_stack.should be_empty
80
- end
68
+ For exemple, in Rspec, you could do like this:
81
69
 
82
- # Pull request?
70
+ ```ruby
71
+ require 'mock_server/spec/helpers'
72
+ RSpec.configure do |config|
73
+ config.include MockServer::Spec::Helpers
74
+ end
75
+ ```
83
76
 
84
- Yes.
77
+ Inside your test, basic usage exemple:
78
+
79
+ ```ruby
80
+ before :each do
81
+
82
+ # Set the filename where you store the records
83
+ # in this exemple, it will load `fixtures/records/bootsrap.yml`
84
+ # and `fixtures/records/uploads.yml`
85
+ mock_server_use_record 'bootsrap', 'uploads'
86
+
87
+ # From now on, those path belong to MockServer.
88
+ # if we can't match to a record, the server return a 404 and populate the errors stack.
89
+ mock_server_enable_routes '**/uploads/*', '**/uploads', '**/folders'
90
+
91
+ end
92
+
93
+ after :each do
94
+ mock_server_reset!
95
+ end
96
+
97
+ scenario "Some JSON api fun" do
98
+ mock_server_get('/api/2/projects')
99
+ # If no block is given, it will return the first recorded response that
100
+ # match the verb and path.
101
+
102
+ # You can be more specific by using a block
103
+ mock_server_post('/api/2/projects') do |request, recorded_request|
104
+
105
+ recorded_request.body.name == recorded_request.body.name and
106
+ recorded_request.body.name == 'MockServer'
107
+
108
+ # Internally, the Playback middlware will perform a Array#detect against
109
+ # all the recorded request loaded from the fixture files and will
110
+ # return the response when it return true
111
+ }
112
+
113
+ # ...fun stuff... fight with selenium selectors or whatever you would normally do!
114
+ end
115
+ ```
116
+
117
+ Have a look at the [helpers](http://rubydoc.info/github/unixcharles/mock_server/master/MockServer/Spec/Helpers).
118
+
119
+ ## Pull request?
120
+
121
+ Yes.
122
+
123
+ ## Documentation
124
+
125
+ [rubydoc](http://rubydoc.info/github/unixcharles/mock_server/master)
126
+
127
+ ## Credits
128
+
129
+ MockServer borrow idea of the awesome [VCR](https://github.com/myronmarston/vcr) from [Myron Marston](https://github.com/myronmarston) that does similar work with HTTP interactions to external services. Give it a look!
130
+
131
+ MockServer is developped and improved for our internal use at [Teambox](http://teambox.com/). Kudos to my work mates for their insightful feedback and pull requests.
@@ -1,5 +1,5 @@
1
1
  unless defined? MockServer::Store
2
- require_relative 'Store/global'
2
+ require 'mock_server/store/global'
3
3
  end
4
4
 
5
5
  module MockServer
@@ -7,100 +7,226 @@ module MockServer
7
7
  module Helpers
8
8
  include MockServer::Store
9
9
 
10
- # Inspect
10
+
11
+ # Public: Inspect mock server options
12
+ #
13
+ # Returns a String.
11
14
  def mock_server_inspect
12
15
  mock_server_options.inspect
13
16
  end
14
17
 
15
- # Configuration
18
+ # Public: Overwrite or initialize the list of fixtures
19
+ #
20
+ # *arguments - Filename...
21
+ #
22
+ # Examples
23
+ #
24
+ # mock_server_use_record(:users, :comments)
25
+ #
16
26
  def mock_server_use_record(*arguments)
17
27
  mock_server_options_set(:record_filenames, arguments)
18
28
  end
19
29
 
30
+ # Public: Add fixtures to the list of fixtures
31
+ #
32
+ # *arguments - Filename...
33
+ #
34
+ # Examples
35
+ #
36
+ # mock_server_add_record(:users, :comments)
37
+ #
20
38
  def mock_server_add_record(*arguments)
21
39
  config = (mock_server_options_fetch(:record_filenames, []) + arguments)
22
40
  mock_server_options_set(:record_filenames, config)
23
41
  end
24
42
 
43
+ # Public: Set the path of fixtures files
44
+ #
45
+ # path - Sting of the fixtures path
46
+ #
47
+ # Examples
48
+ #
49
+ # mock_server_set_fixture_path('fixtures/records/')
50
+ #
25
51
  def mock_server_set_fixture_path(path)
26
52
  mock_server_options_set(:path, path)
27
53
  end
28
54
 
29
- # Active path config
30
- def mock_server_enable_routes(*arguments)
31
- routes = mock_server_options_fetch(:routes, []) + arguments
55
+ # Public: Enabled MockServer on a given routes.
56
+ # Accept unix-directory like */** catch all.
57
+ #
58
+ # *arguments - Sting of the fixtures path
59
+ #
60
+ # Examples
61
+ #
62
+ # mock_server_enable_routes('/api/2/**', '/api/verify')
63
+ #
64
+ def mock_server_enable_routes(*paths)
65
+ routes = mock_server_options_fetch(:routes, []) + paths
32
66
  mock_server_options_set(:routes, routes)
33
67
  end
34
68
 
35
- def mock_server_disable_path(path)
36
- routes = mock_server_options_fetch(:routes, []) - [path]
37
- mock_server_options_set(:routes, routes.flatten!)
69
+ # Public: Disable MockServer on a given routes.
70
+ #
71
+ # *paths - Sting of the fixtures path
72
+ #
73
+ # Examples
74
+ #
75
+ # mock_server_disable_path('/api/2/**', '/api/verify')
76
+ #
77
+ def mock_server_disable_path(*paths)
78
+ routes = mock_server_options_fetch(:routes, []) - paths
79
+ mock_server_options_set(:routes, routes.flatten)
38
80
  end
39
81
 
82
+ # Public: Disable all routes being server by MockServer
83
+ #
84
+ #
85
+ # Examples
86
+ #
87
+ # mock_server_disable_all_routes!
88
+ #
40
89
  def mock_server_disable_all_routes!
41
90
  mock_server_options_set(:routes, [])
42
91
  end
43
92
 
44
- # Matchers helpers
93
+ # Public: Register a matcher on a GET request for a given route.
94
+ #
95
+ # path - Relative HTTP path to match
96
+ # &block - Optional block for complex matching on the request
97
+ #
98
+ # Examples
99
+ #
100
+ # mock_server_get('/api/2/account')
101
+ #
102
+ # mock_server_get('/api/2/account') { |request, recorded_request| request.body == recorded_request.body }
103
+ #
45
104
  def mock_server_get(path, &block)
46
105
  mock_server_request :get, path, block
47
106
  end
48
107
 
108
+ # Public: Register a matcher on a POST request for a given route.
109
+ #
110
+ # path - Relative HTTP path to match
111
+ # &block - Optional block for complex matching on the request
112
+ #
113
+ # Examples
114
+ #
115
+ # mock_server_post('/api/2/account')
116
+ #
117
+ # mock_server_post('/api/2/account') { |request, recorded_request| request.body == recorded_request.body }
118
+ #
49
119
  def mock_server_post(path, &block)
50
120
  mock_server_request :post, path, block
51
121
  end
52
122
 
123
+ # Public: Register a matcher on a PUT request for a given route.
124
+ #
125
+ # path - Relative HTTP path to match
126
+ # &block - Optional block for complex matching on the request
127
+ #
128
+ # Examples
129
+ #
130
+ # mock_server_put('/api/2/account')
131
+ #
132
+ # mock_server_put('/api/2/account') { |request, recorded_request| request.body == recorded_request.body }
133
+ #
53
134
  def mock_server_put(path, &block)
54
135
  mock_server_request :put, path, block
55
136
  end
56
137
 
138
+ # Public: Register a matcher on a DELETE request for a given route.
139
+ #
140
+ # path - Relative HTTP path to match
141
+ # &block - Optional block for complex matching on the request
142
+ #
143
+ # Examples
144
+ #
145
+ # mock_server_delete('/api/2/account')
146
+ #
147
+ # mock_server_delete('/api/2/account') { |request, recorded_request| request.body == recorded_request.body }
148
+ #
57
149
  def mock_server_delete(path, &block)
58
150
  mock_server_request :delete, path, block
59
151
  end
60
152
 
153
+ # Public: Clear all the matchers
154
+ #
61
155
  def mock_server_clear_matchers!
62
156
  mock_server_options_set(:matchers, [])
63
157
  end
64
158
 
65
- # Errors / Success stack
159
+ # Public: Retrive the MockServer request stack.
160
+ #
161
+ # Return array of request path.
66
162
  def mock_server_requests_stack
67
163
  mock_server_options_fetch(:requests_stack, [])
68
164
  end
69
165
 
166
+ # Public: Clear the MockServer request stack.
70
167
  def mock_server_requests_stack_clear!
71
168
  mock_server_options_set(:requests_stack, [])
72
169
  end
73
170
 
171
+ # Public: Retrive the MockServer request stack.
172
+ #
173
+ # Return array of request path.
74
174
  def mock_server_errors_stack
75
175
  mock_server_options_fetch(:errors_stack, [])
76
176
  end
77
177
 
178
+ # Public: Retrive the MockServer errors request stack.
179
+ # i.e.: path being register for being serve by MockServer, but
180
+ # no suitable matcher was found to serve the request will be
181
+ # added to the error stack
182
+ #
183
+ # Return array of errors.
78
184
  def mock_server_errors_stack_clear!
79
185
  mock_server_options_set(:errors_stack, [])
80
186
  end
81
187
 
188
+ # Public: Retrive the MockServer successful request stack.
189
+ #
190
+ # Return array of successful response stack.
82
191
  def mock_server_success_stack
83
192
  mock_server_options_fetch(:success_stack, [])
84
193
  end
85
194
 
195
+ # Public: Clear the MockServer successful request stack.
86
196
  def mock_server_success_stack_clear!
87
197
  mock_server_options_set(:success_stack, [])
88
198
  end
89
199
 
200
+ # Public: Clear the MockServer response stack.
201
+ #
202
+ # alias:
203
+ #
204
+ # mock_server_requests_stack_clear!
205
+ # mock_server_success_stack_clear!
206
+ # mock_server_errors_stack_clear!
207
+ #
90
208
  def mock_server_response_stack_clear!
91
209
  mock_server_requests_stack_clear!
92
210
  mock_server_success_stack_clear!
93
211
  mock_server_errors_stack_clear!
94
212
  end
95
213
 
96
- # Clear all the settings
97
-
214
+ # Public: Clear the MockServer state.
215
+ #
216
+ # alias:
217
+ #
218
+ # mock_server_response_stack_clear!
219
+ # mock_server_clear_matchers!
220
+ # mock_server_disable_all_routes!
221
+ #
98
222
  def mock_server_reset!
99
223
  mock_server_response_stack_clear!
100
224
  mock_server_clear_matchers!
101
225
  mock_server_disable_all_routes!
102
226
  end
103
227
 
228
+ # Public: Utility helper to reraise errors catch inside the matchers block
229
+ #
104
230
  def mock_server_reraise_matcher_exceptions
105
231
  mock_server_options_fetch(:matcher_exceptions, []).each do |exception|
106
232
  raise exception
@@ -109,17 +235,32 @@ module MockServer
109
235
 
110
236
  protected
111
237
 
112
- # Matchers helpers
238
+ # Internal: Register a matcher on a given route for playback
239
+ #
240
+ # method - HTTP verb to register
241
+ # path - Relative HTTP path to match
242
+ # matcher - Optional proc for complex matching on the request
243
+ #
244
+ # Examples
245
+ #
246
+ # mock_server_request(:get, '/api/2/account')
247
+ #
248
+ # mock_server_request(:get, '/api/2/account', lambda {|request, recorded_request| request.body == recorded_request.body } )
249
+ #
113
250
  def mock_server_request(method, path, matcher)
114
251
  add_mock_server_matcher({ :method => method, :path => path, :matcher => matcher })
115
252
  end
116
253
 
254
+ # Internal: Insert a matcher hash into the matchers array of the MockServer storage class.
255
+ #
117
256
  def add_mock_server_matcher(matcher)
118
257
  options = self.mock_server_options_read
119
- options[:matchers] << matcher
258
+ options[:matchers].unshift(matcher)
120
259
  mock_server_options_write(options)
121
260
  end
122
261
 
262
+ # Internal: Fetch key from the storage class
263
+ #
123
264
  def mock_server_options_fetch(key, value)
124
265
  if self.mock_server_options_read[key]
125
266
  mock_server_options_get(key)
@@ -128,12 +269,16 @@ module MockServer
128
269
  end
129
270
  end
130
271
 
272
+ # Internal: Setter for the storage class
273
+ #
131
274
  def mock_server_options_set(key, value)
132
275
  hash = self.mock_server_options_read
133
276
  hash[key] = value
134
277
  self.mock_server_options_write(hash)
135
278
  end
136
279
 
280
+ # Internal: Getter for the storage class
281
+ #
137
282
  def mock_server_options_get(key)
138
283
  hash = self.mock_server_options_read
139
284
  hash[key]
metadata CHANGED
@@ -1,60 +1,55 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: mock_server
3
- version: !ruby/object:Gem::Version
4
- hash: 31
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 4
10
- version: 0.2.4
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Charles Barbier
14
9
  - Saimon Moore
15
10
  autorequire:
16
11
  bindir: bin
17
12
  cert_chain: []
18
-
19
- date: 2012-02-03 00:00:00 Z
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
13
+ date: 2012-07-27 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
22
16
  name: rack
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: !ruby/object:Gem::Requirement
25
18
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
- version: "0"
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
33
23
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: hashie
37
24
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: hashie
33
+ requirement: !ruby/object:Gem::Requirement
39
34
  none: false
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- hash: 3
44
- segments:
45
- - 0
46
- version: "0"
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
47
39
  type: :runtime
48
- version_requirements: *id002
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
49
47
  description:
50
48
  email: unixcharles@gmail.com
51
49
  executables: []
52
-
53
50
  extensions: []
54
-
55
51
  extra_rdoc_files: []
56
-
57
- files:
52
+ files:
58
53
  - README.md
59
54
  - LICENSE
60
55
  - lib/mock_server.rb
@@ -66,36 +61,26 @@ files:
66
61
  - lib/mock_server/spec/helpers.rb
67
62
  homepage: http://www.github.com/unixcharles/mock_server
68
63
  licenses: []
69
-
70
64
  post_install_message:
71
65
  rdoc_options: []
72
-
73
- require_paths:
66
+ require_paths:
74
67
  - lib
75
- required_ruby_version: !ruby/object:Gem::Requirement
68
+ required_ruby_version: !ruby/object:Gem::Requirement
76
69
  none: false
77
- requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- hash: 3
81
- segments:
82
- - 0
83
- version: "0"
84
- required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
75
  none: false
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- hash: 3
90
- segments:
91
- - 0
92
- version: "0"
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
93
80
  requirements: []
94
-
95
81
  rubyforge_project:
96
- rubygems_version: 1.8.10
82
+ rubygems_version: 1.8.24
97
83
  signing_key:
98
84
  specification_version: 3
99
85
  summary: Mock you're entire application
100
86
  test_files: []
101
-