surrogate 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rvmrc CHANGED
@@ -1,8 +1,5 @@
1
1
  rvm use ruby-1.9.3-p125
2
2
 
3
- # set up gemfile
4
- export BUNDLE_GEMFILE=gemfiles/rspec_mocks_2.11
5
-
6
3
  root_dir="$(pwd)"
7
4
 
8
5
  alias ac="rspec '$root_dir/spec/acceptance_spec.rb'"
@@ -0,0 +1,21 @@
1
+ ### 0.6.0
2
+
3
+ * Setting an override still requires the invoking code to call with the correct signature
4
+ * Remove `api_method_names` and `api_method_for` and `invocations` from surrogates
5
+ (might break your code if you relied on these, but they were never advertized, and no obvious reason to use them)
6
+ Instead use the reflectors: Surrogate::SurrogateClassReflector and Surrogate::SurrogateInstanceReflector
7
+ * BREAKING CHANGE - Substitutability can check argument "types". This is turned on by default
8
+ * Initialize is no longer implicitly recorded (This might break something, but I don't think this feature was ever advertized, so hopefully people don't depend on it).
9
+ * BREAKING CHANGE - API method signatures are enforced (if meth takes 1 arg, you must pass it 1 arg)
10
+ * The name of a clone is the name of the parent suffixed with '.clone', unless the parent is anonymous (not set to a const), then the name is nil.
11
+ * Inspect messages are shorter and more helpful
12
+ * Inspect messages on class clones mimic the parents
13
+ * Remove comment about the new syntax in the Readme. If you want to switch over, here is a shell script that should get you pretty far:
14
+
15
+ find spec -type file |
16
+ xargs ruby -p -i.old_syntax \
17
+ -e 'gsub /should(_not)?(\s+)have_been_told_to/, "was\\1\\2told_to"' \
18
+ -e 'gsub /should(_not)?(\s+)have_been_asked_(if|for)(_its)?/, "was\\1\\2asked_\\3"' \
19
+ -e 'gsub /should(_not)(\s+)have_been_initialized_with/, "was\\1\\2initialized_with"' \
20
+
21
+
data/Readme.md CHANGED
@@ -17,39 +17,17 @@ discouraged at this time. If you do want to do this (e.g. to make an interface f
17
17
  let me know, and I'll inform you / fork your gem and help update it, for any breaking changes
18
18
  that I introduce.
19
19
 
20
- New Syntax
21
- ==========
22
-
23
- Recently (v0.5.1), a new syntax was added:
24
-
25
- <table>
26
- <tr><th>Old</th><th>New</th></tr>
27
- <tr><td>.should have_been_told_to</td><td>.was told_to</td></tr>
28
- <tr><td>.should have_been_asked_for_its</td><td>.was asked_for</td></tr>
29
- <tr><td>.should have_been_asked_if</td><td>.was asked_if</td></tr>
30
- <tr><td>.should have_been_initialized_with</td><td>.was initialized_with</td></tr>
31
- </table>
32
-
33
- If you want to switch over, here is a shell script that should get you pretty far:
34
-
35
- find spec -type file |
36
- xargs ruby -p -i.old_syntax \
37
- -e 'gsub /should(_not)?(\s+)have_been_told_to/, "was\\1\\2told_to"' \
38
- -e 'gsub /should(_not)?(\s+)have_been_asked_(if|for)(_its)?/, "was\\1\\2asked_\\3"' \
39
- -e 'gsub /should(_not)(\s+)have_been_initialized_with/, "was\\1\\2initialized_with"' \
40
-
41
-
42
20
  Features
43
21
  ========
44
22
 
45
23
  * Declarative syntax
24
+ * Can compare signatures of mocks to signatures of the class being mocked
46
25
  * Support default values
47
26
  * Easily override values
48
27
  * RSpec matchers for asserting what happend (what was invoked, with what args, how many times)
49
28
  * RSpec matchers for asserting the Mock's interface matches the real object
50
29
  * Support for exceptions
51
30
  * Queue return values
52
- * Initialization information is always recorded
53
31
 
54
32
 
55
33
  Usage
@@ -86,7 +64,10 @@ end
86
64
  MockClient.new.request # => ["result1", "result2"]
87
65
  ```
88
66
 
89
- If you care about the **arguments**, your block can receive them.
67
+ If you expect **arguments**, your block should receive them.
68
+ This prevents the issues with dynamic mocks where arguments and parameters can diverge.
69
+ It may seem like more work when you have to write the arguments explicitly, but you only
70
+ need to do this once, and then you can be sure they match on each invocation.
90
71
 
91
72
  ```ruby
92
73
  class MockClient
@@ -98,6 +79,7 @@ MockClient.new.request 3 # => ["result1", "result2", "result3"]
98
79
  ```
99
80
 
100
81
  You don't need a **default if you set the ivar** of the same name (replace `?` with `_p` for predicates, since you can't have question marks in ivar names)
82
+ Note that methods without bodies will not have their arguments checked, and will not be asserted against when comparing signatures.
101
83
 
102
84
  ```ruby
103
85
  class MockClient
@@ -177,6 +159,21 @@ user = MockUser.find 12
177
159
  user.id # => 12
178
160
  ```
179
161
 
162
+ Use **clone** to avoid altering state on the class.
163
+ ```ruby
164
+ class MockUser
165
+ Surrogate.endow self do
166
+ define(:find) { new }
167
+ end
168
+ end
169
+
170
+ user_class = MockUser.clone
171
+ user_class.find
172
+ user_class.was told_to :find
173
+ MockUser.was_not told_to :find
174
+ ```
175
+
176
+
180
177
 
181
178
  RSpec Integration
182
179
  =================
@@ -190,6 +187,21 @@ Load the RSpec matchers.
190
187
  require 'surrogate/rspec'
191
188
  ```
192
189
 
190
+ Last Instance
191
+ -------------
192
+
193
+ Access the last instance of a class
194
+
195
+ ```ruby
196
+ class MockMp3
197
+ Surrogate.endow self
198
+ end
199
+
200
+ mp3_class = MockMp3.clone # because you don't want to mutate the singleton
201
+ mp3 = mp3_class.new
202
+ mp3_class.last_instance.equal? mp3 # => true
203
+ ```
204
+
193
205
  Nouns
194
206
  -----
195
207
 
@@ -198,16 +210,16 @@ Given this mock and assuming the following examples happen within a spec
198
210
  ```ruby
199
211
  class MockMP3
200
212
  Surrogate.endow self
201
- define(:info) { 'some info' }
213
+ define(:info) { |song='Birds Will Sing Forever'| 'some info' }
202
214
  end
203
215
  ```
204
216
 
205
- Check if **was invoked** with `have_been_asked_for_its`
217
+ Check if **was invoked** with `was asked_for` (`was` is an alias of `should`, and `asked_for` is a custom matcher)
206
218
 
207
219
  ```ruby
208
- mp3.should_not have_been_asked_for_its :info
220
+ mp3.was_not asked_for :info
209
221
  mp3.info
210
- mp3.should have_been_asked_for_its :info
222
+ mp3.was asked_for :info
211
223
  ```
212
224
 
213
225
  Invocation **cardinality** by chaining `times(n)`
@@ -215,21 +227,21 @@ Invocation **cardinality** by chaining `times(n)`
215
227
  ```ruby
216
228
  mp3.info
217
229
  mp3.info
218
- mp3.should have_been_asked_for_its(:info).times(2)
230
+ mp3.was asked_for(:info).times(2)
219
231
  ```
220
232
 
221
233
  Invocation **arguments** by chaining `with(args)`
222
234
 
223
235
  ```ruby
224
236
  mp3.info :title
225
- mp3.should have_been_asked_for_its(:info).with(:title)
237
+ mp3.was asked_for(:info).with(:title)
226
238
  ```
227
239
 
228
240
  Supports RSpec's matchers (`no_args`, `hash_including`, etc)
229
241
 
230
242
  ```ruby
231
243
  mp3.info
232
- mp3.should have_been_asked_for_its(:info).with(no_args)
244
+ mp3.was asked_for(:info).with(no_args)
233
245
  ```
234
246
 
235
247
  Cardinality of a specific set of args `with(args)` and `times(n)`
@@ -238,8 +250,8 @@ Cardinality of a specific set of args `with(args)` and `times(n)`
238
250
  mp3.info :title
239
251
  mp3.info :title
240
252
  mp3.info :artist
241
- mp3.should have_been_asked_for_its(:info).with(:title).times(2)
242
- mp3.should have_been_asked_for_its(:info).with(:artist).times(1)
253
+ mp3.was asked_for(:info).with(:title).times(2)
254
+ mp3.was asked_for(:info).with(:artist).times(1)
243
255
  ```
244
256
 
245
257
 
@@ -255,12 +267,12 @@ class MockMP3
255
267
  end
256
268
  ```
257
269
 
258
- Check if **was invoked** with `have_been_told_to`
270
+ Check if **was invoked** with `was told_to`
259
271
 
260
272
  ```ruby
261
- mp3.should_not have_been_told_to :play
273
+ mp3.was_not told_to :play
262
274
  mp3.play
263
- mp3.should have_been_told_to :play
275
+ mp3.was told_to :play
264
276
  ```
265
277
 
266
278
  Also supports the same `with(args)` and `times(n)` that nouns have.
@@ -269,7 +281,7 @@ Also supports the same `with(args)` and `times(n)` that nouns have.
269
281
  Initialization
270
282
  --------------
271
283
 
272
- Query with `have_been_initialized_with`, which is exactly the same as saying `have_been_told_to(:initialize).with(...)`
284
+ Query with `was initialized_with`, which is exactly the same as saying `was told_to(:initialize).with(...)`
273
285
 
274
286
  ```ruby
275
287
  class MockUser
@@ -279,14 +291,14 @@ class MockUser
279
291
  end
280
292
  user = MockUser.new 12
281
293
  user.id.should == 12
282
- user.should have_been_initialized_with 12
294
+ user.was initialized_with 12
283
295
  ```
284
296
 
285
297
 
286
298
  Predicates
287
299
  ----------
288
300
 
289
- Query qith `have_been_asked_if`, all the same chainable methods from above apply.
301
+ Query qith `was asked_if`, all the same chainable methods from above apply.
290
302
 
291
303
  ```ruby
292
304
  class MockUser
@@ -298,15 +310,15 @@ user = MockUser.new
298
310
  user.should_not be_admin
299
311
  user.will_have_admin? true
300
312
  user.should be_admin
301
- user.should have_been_asked_if(:admin?).times(2)
313
+ user.was asked_if(:admin?).times(2)
302
314
  ```
303
315
 
304
316
 
305
- class MockUser
306
-
307
317
  Substitutability
308
318
  ----------------
309
319
 
320
+ Facets of substitutability: method existence, argument types, (and soon argument names)
321
+
310
322
  After you've implemented the real version of your mock (assuming a [top-down](http://vimeo.com/31267109) style of development),
311
323
  how do you prevent your real object from getting out of synch with your mock?
312
324
 
@@ -314,8 +326,8 @@ Assert that your mock has the **same interface** as your real class.
314
326
  This will fail if the mock inherits methods which are not on the real class. It will also fail
315
327
  if the real class has any methods which have not been defined on the mock or inherited by the mock.
316
328
 
317
- Presently, it will ignore methods defined directly in the mock (as it adds quite a few of its own methods,
318
- and generally considers them to be helpers). In a future version, you will be able to tell it to treat other methods
329
+ Presently, it will ignore methods defined directly in the mock (as it generally considers them to be helpers).
330
+ In a future version, you will be able to tell it to treat other methods
319
331
  as part of the API (will fail if they don't match, and maybe record their values).
320
332
 
321
333
  ```ruby
@@ -348,6 +360,13 @@ class UserWithNameAndAddress < UserWithName
348
360
  def address()end
349
361
  end
350
362
  MockUser.should_not substitute_for UserWithNameAndAddress
363
+
364
+ # signatures don't match
365
+ class UserWithWrongSignature
366
+ def initialize()end # no id
367
+ def id()end
368
+ end
369
+ MockUser.should_not substitute_for UserWithWrongSignature
351
370
  ```
352
371
 
353
372
  Sometimes you don't want to have to implement the entire interface.
@@ -405,7 +424,7 @@ describe 'something that creates a user through the service' do
405
424
  to_return
406
425
  end
407
426
 
408
- service.should have_been_told_to(:create).with { |block|
427
+ service.was told_to(:create).with { |block|
409
428
  block.call_with({id: new_id}) # this will be given to the block
410
429
  block.returns old_id # provide a return value, or a block that receives the return value (where you can make assertions)
411
430
  block.before { user_id.should == old_id } # assertions about state of the world before the block is called
@@ -442,34 +461,3 @@ Special Thanks
442
461
  * [Corey Haines](http://coreyhaines.com/) for pairing on substitutability with me
443
462
  * [Enova](http://www.enovafinancial.com/) for giving me time and motivation to work on this during Enova Labs.
444
463
  * [8th Light](http://8thlight.com/) for giving me time to work on this during our weekly Wazas, and the general encouragement and interest
445
-
446
-
447
- TODO
448
- ----
449
-
450
- * Remove dependency on all of RSpec and only depend on rspec-core, then have AC tests for the other shit
451
- * Add proper failure messages for block invocations
452
- * Add a better explanation for motivations
453
- * Figure out whether I'm supposed to be using clone or dup for the object -.^ (looks like there may also be an `initialize_copy` method I can take advantage of instead of crazy stupid shit I'm doing now)
454
- * don't blow up when delegating to the Object#initialize with args (do I still want this, or do I want to force arity matching (and maybe even variable name matching)?)
455
- * config: rspec_mocks loaded, whether unprepared blocks should raise or just return nil
456
- * extract surrogate/rspec into its own gem
457
- * support subset-substitutabilty not being able to touch real methods (e.g. #respond_to?)
458
- * Add a last_instance option so you don't have to track it explicitly
459
- * make substitutability matcher go either way
460
- * make substitutability matcher not care whether either are surrogates
461
- * Add support for operators
462
-
463
-
464
- Future Features
465
- ---------------
466
-
467
- * figure out how to talk about callbacks like #on_success
468
- * have some sort of reinitialization that can hook into setup/teardown steps of test suite
469
- * Support arity checking as part of substitutability
470
- * Ability to disassociate the method name from the test (e.g. you shouldn't need to change a test just because you change a name)
471
- * ability to declare normal methods as being part of the API
472
- * ability to declare a define that uses the overridden method as the body, but can still act like an api method
473
- * assertions for order of invocations & methods
474
- * class generator? (supports a top-down style of development for when you write your mocks before you write your implementations)
475
- * deal with hard dependency on rspec-mocks
@@ -6,6 +6,7 @@ require 'surrogate'
6
6
  <% end %>
7
7
 
8
8
  <% context 'generic it block' do %>
9
+ require 'surrogate/rspec'
9
10
  describe 'whatever' do
10
11
  it 'does something' do
11
12
  __CODE__
@@ -28,39 +29,17 @@ discouraged at this time. If you do want to do this (e.g. to make an interface f
28
29
  let me know, and I'll inform you / fork your gem and help update it, for any breaking changes
29
30
  that I introduce.
30
31
 
31
- New Syntax
32
- ==========
33
-
34
- Recently (v0.5.1), a new syntax was added:
35
-
36
- <table>
37
- <tr><th>Old</th><th>New</th></tr>
38
- <tr><td>.should have_been_told_to</td><td>.was told_to</td></tr>
39
- <tr><td>.should have_been_asked_for_its</td><td>.was asked_for</td></tr>
40
- <tr><td>.should have_been_asked_if</td><td>.was asked_if</td></tr>
41
- <tr><td>.should have_been_initialized_with</td><td>.was initialized_with</td></tr>
42
- </table>
43
-
44
- If you want to switch over, here is a shell script that should get you pretty far:
45
-
46
- find spec -type file |
47
- xargs ruby -p -i.old_syntax \
48
- -e 'gsub /should(_not)?(\s+)have_been_told_to/, "was\\1\\2told_to"' \
49
- -e 'gsub /should(_not)?(\s+)have_been_asked_(if|for)(_its)?/, "was\\1\\2asked_\\3"' \
50
- -e 'gsub /should(_not)(\s+)have_been_initialized_with/, "was\\1\\2initialized_with"' \
51
-
52
-
53
32
  Features
54
33
  ========
55
34
 
56
35
  * Declarative syntax
36
+ * Can compare signatures of mocks to signatures of the class being mocked
57
37
  * Support default values
58
38
  * Easily override values
59
39
  * RSpec matchers for asserting what happend (what was invoked, with what args, how many times)
60
40
  * RSpec matchers for asserting the Mock's interface matches the real object
61
41
  * Support for exceptions
62
42
  * Queue return values
63
- * Initialization information is always recorded
64
43
 
65
44
 
66
45
  Usage
@@ -103,7 +82,10 @@ MockClient.new.request # => ["result1", "result2"]
103
82
  <% end %>
104
83
  ```
105
84
 
106
- If you care about the **arguments**, your block can receive them.
85
+ If you expect **arguments**, your block should receive them.
86
+ This prevents the issues with dynamic mocks where arguments and parameters can diverge.
87
+ It may seem like more work when you have to write the arguments explicitly, but you only
88
+ need to do this once, and then you can be sure they match on each invocation.
107
89
 
108
90
  ```ruby
109
91
  <% test 'api method with arguments', with: :magic_comments do %>
@@ -117,6 +99,7 @@ MockClient.new.request 3 # => ["result1", "result2", "result3"]
117
99
  ```
118
100
 
119
101
  You don't need a **default if you set the ivar** of the same name (replace `?` with `_p` for predicates, since you can't have question marks in ivar names)
102
+ Note that methods without bodies will not have their arguments checked, and will not be asserted against when comparing signatures.
120
103
 
121
104
  ```ruby
122
105
  <% test 'overriding default by setting the ivar', with: :magic_comments do %>
@@ -206,6 +189,23 @@ user.id # => 12
206
189
  <% end %>
207
190
  ```
208
191
 
192
+ Use **clone** to avoid altering state on the class.
193
+ ```ruby
194
+ <% test 'clone to avoid mutating state', with: :rspec, context: 'generic it block' do %>
195
+ class MockUser
196
+ Surrogate.endow self do
197
+ define(:find) { new }
198
+ end
199
+ end
200
+
201
+ user_class = MockUser.clone
202
+ user_class.find
203
+ user_class.was told_to :find
204
+ MockUser.was_not told_to :find
205
+ <% end %>
206
+ ```
207
+
208
+
209
209
 
210
210
  RSpec Integration
211
211
  =================
@@ -222,6 +222,23 @@ require 'surrogate/rspec'
222
222
  require 'surrogate/rspec'
223
223
  ```
224
224
 
225
+ Last Instance
226
+ -------------
227
+
228
+ Access the last instance of a class
229
+
230
+ ```ruby
231
+ <% test "last instance", with: :magic_comments do %>
232
+ class MockMp3
233
+ Surrogate.endow self
234
+ end
235
+
236
+ mp3_class = MockMp3.clone # because you don't want to mutate the singleton
237
+ mp3 = mp3_class.new
238
+ mp3_class.last_instance.equal? mp3 # => true
239
+ <% end %>
240
+ ```
241
+
225
242
  Nouns
226
243
  -----
227
244
 
@@ -231,7 +248,7 @@ Given this mock and assuming the following examples happen within a spec
231
248
  <% test "mock mp3 code shouldn't blow up", with: :magic_comments do %>
232
249
  class MockMP3
233
250
  Surrogate.endow self
234
- define(:info) { 'some info' }
251
+ define(:info) { |song='Birds Will Sing Forever'| 'some info' }
235
252
  end
236
253
  <% end %>
237
254
  ```
@@ -239,7 +256,7 @@ end
239
256
  <% context 'mp3 in spec' do %>
240
257
  class MockMP3
241
258
  Surrogate.endow self
242
- define(:info) { 'some info' }
259
+ define(:info) { |song='Birds Will Sing Forever'| 'some info' }
243
260
  end
244
261
 
245
262
  describe 'the example' do
@@ -250,13 +267,13 @@ describe 'the example' do
250
267
  end
251
268
  <% end %>
252
269
 
253
- Check if **was invoked** with `have_been_asked_for_its`
270
+ Check if **was invoked** with `was asked_for` (`was` is an alias of `should`, and `asked_for` is a custom matcher)
254
271
 
255
272
  ```ruby
256
273
  <% test 'noun invocation', with: :rspec, context: 'mp3 in spec' do %>
257
- mp3.should_not have_been_asked_for_its :info
274
+ mp3.was_not asked_for :info
258
275
  mp3.info
259
- mp3.should have_been_asked_for_its :info
276
+ mp3.was asked_for :info
260
277
  <% end %>
261
278
  ```
262
279
 
@@ -266,7 +283,7 @@ Invocation **cardinality** by chaining `times(n)`
266
283
  <% test 'noun cardinality', with: :rspec, context: 'mp3 in spec' do %>
267
284
  mp3.info
268
285
  mp3.info
269
- mp3.should have_been_asked_for_its(:info).times(2)
286
+ mp3.was asked_for(:info).times(2)
270
287
  <% end %>
271
288
  ```
272
289
 
@@ -275,7 +292,7 @@ Invocation **arguments** by chaining `with(args)`
275
292
  ```ruby
276
293
  <% test 'noun invocation with args', with: :rspec, context: 'mp3 in spec' do %>
277
294
  mp3.info :title
278
- mp3.should have_been_asked_for_its(:info).with(:title)
295
+ mp3.was asked_for(:info).with(:title)
279
296
  <% end %>
280
297
  ```
281
298
 
@@ -284,7 +301,7 @@ Supports RSpec's matchers (`no_args`, `hash_including`, etc)
284
301
  ```ruby
285
302
  <% test 'rspec matchers integration', with: :rspec, context: 'mp3 in spec' do %>
286
303
  mp3.info
287
- mp3.should have_been_asked_for_its(:info).with(no_args)
304
+ mp3.was asked_for(:info).with(no_args)
288
305
  <% end %>
289
306
  ```
290
307
 
@@ -295,8 +312,8 @@ Cardinality of a specific set of args `with(args)` and `times(n)`
295
312
  mp3.info :title
296
313
  mp3.info :title
297
314
  mp3.info :artist
298
- mp3.should have_been_asked_for_its(:info).with(:title).times(2)
299
- mp3.should have_been_asked_for_its(:info).with(:artist).times(1)
315
+ mp3.was asked_for(:info).with(:title).times(2)
316
+ mp3.was asked_for(:info).with(:artist).times(1)
300
317
  <% end %>
301
318
  ```
302
319
 
@@ -328,13 +345,13 @@ describe 'the example' do
328
345
  end
329
346
  <% end %>
330
347
 
331
- Check if **was invoked** with `have_been_told_to`
348
+ Check if **was invoked** with `was told_to`
332
349
 
333
350
  ```ruby
334
351
  <% test 'have_been_told_to', with: :rspec, context: 'mp3 that plays in spec' do %>
335
- mp3.should_not have_been_told_to :play
352
+ mp3.was_not told_to :play
336
353
  mp3.play
337
- mp3.should have_been_told_to :play
354
+ mp3.was told_to :play
338
355
  <% end %>
339
356
  ```
340
357
 
@@ -344,7 +361,7 @@ Also supports the same `with(args)` and `times(n)` that nouns have.
344
361
  Initialization
345
362
  --------------
346
363
 
347
- Query with `have_been_initialized_with`, which is exactly the same as saying `have_been_told_to(:initialize).with(...)`
364
+ Query with `was initialized_with`, which is exactly the same as saying `was told_to(:initialize).with(...)`
348
365
 
349
366
  ```ruby
350
367
  <% test 'initialization test', with: :rspec, context: 'generic it block' do %>
@@ -355,7 +372,7 @@ class MockUser
355
372
  end
356
373
  user = MockUser.new 12
357
374
  user.id.should == 12
358
- user.should have_been_initialized_with 12
375
+ user.was initialized_with 12
359
376
  <% end %>
360
377
  ```
361
378
 
@@ -363,7 +380,7 @@ user.should have_been_initialized_with 12
363
380
  Predicates
364
381
  ----------
365
382
 
366
- Query qith `have_been_asked_if`, all the same chainable methods from above apply.
383
+ Query qith `was asked_if`, all the same chainable methods from above apply.
367
384
 
368
385
  ```ruby
369
386
  <% test 'initialization test', with: :rspec, context: 'generic it block' do %>
@@ -376,16 +393,16 @@ user = MockUser.new
376
393
  user.should_not be_admin
377
394
  user.will_have_admin? true
378
395
  user.should be_admin
379
- user.should have_been_asked_if(:admin?).times(2)
396
+ user.was asked_if(:admin?).times(2)
380
397
  <% end %>
381
398
  ```
382
399
 
383
400
 
384
- class MockUser
385
-
386
401
  Substitutability
387
402
  ----------------
388
403
 
404
+ Facets of substitutability: method existence, argument types, (and soon argument names)
405
+
389
406
  After you've implemented the real version of your mock (assuming a [top-down](http://vimeo.com/31267109) style of development),
390
407
  how do you prevent your real object from getting out of synch with your mock?
391
408
 
@@ -393,8 +410,8 @@ Assert that your mock has the **same interface** as your real class.
393
410
  This will fail if the mock inherits methods which are not on the real class. It will also fail
394
411
  if the real class has any methods which have not been defined on the mock or inherited by the mock.
395
412
 
396
- Presently, it will ignore methods defined directly in the mock (as it adds quite a few of its own methods,
397
- and generally considers them to be helpers). In a future version, you will be able to tell it to treat other methods
413
+ Presently, it will ignore methods defined directly in the mock (as it generally considers them to be helpers).
414
+ In a future version, you will be able to tell it to treat other methods
398
415
  as part of the API (will fail if they don't match, and maybe record their values).
399
416
 
400
417
  ```ruby
@@ -428,6 +445,13 @@ class UserWithNameAndAddress < UserWithName
428
445
  def address()end
429
446
  end
430
447
  MockUser.should_not substitute_for UserWithNameAndAddress
448
+
449
+ # signatures don't match
450
+ class UserWithWrongSignature
451
+ def initialize()end # no id
452
+ def id()end
453
+ end
454
+ MockUser.should_not substitute_for UserWithWrongSignature
431
455
  <% end %>
432
456
  ```
433
457
 
@@ -489,7 +513,7 @@ describe 'something that creates a user through the service' do
489
513
  to_return
490
514
  end
491
515
 
492
- service.should have_been_told_to(:create).with { |block|
516
+ service.was told_to(:create).with { |block|
493
517
  block.call_with({id: new_id}) # this will be given to the block
494
518
  block.returns old_id # provide a return value, or a block that receives the return value (where you can make assertions)
495
519
  block.before { user_id.should == old_id } # assertions about state of the world before the block is called
@@ -527,34 +551,3 @@ Special Thanks
527
551
  * [Corey Haines](http://coreyhaines.com/) for pairing on substitutability with me
528
552
  * [Enova](http://www.enovafinancial.com/) for giving me time and motivation to work on this during Enova Labs.
529
553
  * [8th Light](http://8thlight.com/) for giving me time to work on this during our weekly Wazas, and the general encouragement and interest
530
-
531
-
532
- TODO
533
- ----
534
-
535
- * Remove dependency on all of RSpec and only depend on rspec-core, then have AC tests for the other shit
536
- * Add proper failure messages for block invocations
537
- * Add a better explanation for motivations
538
- * Figure out whether I'm supposed to be using clone or dup for the object -.^ (looks like there may also be an `initialize_copy` method I can take advantage of instead of crazy stupid shit I'm doing now)
539
- * don't blow up when delegating to the Object#initialize with args (do I still want this, or do I want to force arity matching (and maybe even variable name matching)?)
540
- * config: rspec_mocks loaded, whether unprepared blocks should raise or just return nil
541
- * extract surrogate/rspec into its own gem
542
- * support subset-substitutabilty not being able to touch real methods (e.g. #respond_to?)
543
- * Add a last_instance option so you don't have to track it explicitly
544
- * make substitutability matcher go either way
545
- * make substitutability matcher not care whether either are surrogates
546
- * Add support for operators
547
-
548
-
549
- Future Features
550
- ---------------
551
-
552
- * figure out how to talk about callbacks like #on_success
553
- * have some sort of reinitialization that can hook into setup/teardown steps of test suite
554
- * Support arity checking as part of substitutability
555
- * Ability to disassociate the method name from the test (e.g. you shouldn't need to change a test just because you change a name)
556
- * ability to declare normal methods as being part of the API
557
- * ability to declare a define that uses the overridden method as the body, but can still act like an api method
558
- * assertions for order of invocations & methods
559
- * class generator? (supports a top-down style of development for when you write your mocks before you write your implementations)
560
- * deal with hard dependency on rspec-mocks