surrogate 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Readme.md +52 -10
- data/lib/surrogate/hatchling.rb +28 -25
- data/lib/surrogate/invocation.rb +17 -0
- data/lib/surrogate/options.rb +2 -2
- data/lib/surrogate/rspec/abstract_failure_message.rb +59 -0
- data/lib/surrogate/rspec/have_been_asked_for_its.rb +78 -0
- data/lib/surrogate/rspec/have_been_initialized_with.rb +12 -0
- data/lib/surrogate/rspec/have_been_told_to.rb +77 -0
- data/lib/surrogate/rspec/invocation_matcher.rb +81 -0
- data/lib/surrogate/rspec/{substitutability_matchers.rb → substitute_for.rb} +0 -0
- data/lib/surrogate/rspec/times_predicate.rb +19 -0
- data/lib/surrogate/rspec/with_filter.rb +123 -0
- data/lib/surrogate/rspec.rb +25 -2
- data/lib/surrogate/values.rb +23 -30
- data/lib/surrogate/version.rb +1 -1
- data/lib/surrogate.rb +1 -0
- data/spec/acceptance_spec.rb +5 -5
- data/spec/defining_api_methods_spec.rb +19 -42
- data/spec/rspec/block_support_spec.rb +129 -0
- data/spec/rspec/messages_spec.rb +13 -10
- data/surrogate.gemspec +1 -1
- metadata +18 -9
- data/lib/surrogate/rspec/api_method_matchers.rb +0 -229
data/Readme.md
CHANGED
@@ -127,9 +127,6 @@ player.will_move 1, 9, 3
|
|
127
127
|
player.move # => 1
|
128
128
|
player.move # => 9
|
129
129
|
player.move # => 3
|
130
|
-
|
131
|
-
# then back to default behaviour (or error if none provided)
|
132
|
-
player.move # => 20
|
133
130
|
```
|
134
131
|
|
135
132
|
You can define **initialize**
|
@@ -260,8 +257,8 @@ After you've implemented the real version of your mock (assuming a [top-down](ht
|
|
260
257
|
how do you prevent your real object from getting out of synch with your mock?
|
261
258
|
|
262
259
|
Assert that your mock has the **same interface** as your real class.
|
263
|
-
This will fail if the mock inherits methods
|
264
|
-
if the real class has
|
260
|
+
This will fail if the mock inherits methods which are not on the real class. It will also fail
|
261
|
+
if the real class has any methods which have not been defined on the mock or inherited by the mock.
|
265
262
|
|
266
263
|
Presently, it will ignore methods defined directly in the mock (as it adds quite a few of its own methods,
|
267
264
|
and generally considers them to be helpers). In a future version, you will be able to tell it to treat other methods
|
@@ -325,6 +322,46 @@ MockUser.should_not substitute_for User, subset: true
|
|
325
322
|
```
|
326
323
|
|
327
324
|
|
325
|
+
Blocks
|
326
|
+
------
|
327
|
+
|
328
|
+
When your method is invoked with a block, you can make assertions about the block.
|
329
|
+
|
330
|
+
_Note: Right now, block error messages have not been addressed (which means they are probably confusing as shit)_
|
331
|
+
|
332
|
+
Before/after hooks (make assertions here)
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
class MockService
|
336
|
+
Surrogate.endow self
|
337
|
+
define(:create) {}
|
338
|
+
end
|
339
|
+
|
340
|
+
describe 'something that creates a user through the service' do
|
341
|
+
let(:old_id) { 12 }
|
342
|
+
let(:new_id) { 123 }
|
343
|
+
|
344
|
+
it 'updates the user_id and returns the old_id' do
|
345
|
+
user_id = old_id
|
346
|
+
service = MockService.new
|
347
|
+
|
348
|
+
service.create do |user|
|
349
|
+
to_return = user_id
|
350
|
+
user_id = user[:id]
|
351
|
+
to_return
|
352
|
+
end
|
353
|
+
|
354
|
+
service.should have_been_told_to(:create).with { |block|
|
355
|
+
block.call_with({id: new_id}) # this will be given to the block
|
356
|
+
block.returns old_id # provide a return value, or a block that receives the return value (where you can make assertions)
|
357
|
+
block.before { user_id.should == old_id } # assertions about state of the world before the block is called
|
358
|
+
block.after { user_id.should == new_id } # assertions about the state of the world after the block is called
|
359
|
+
}
|
360
|
+
end
|
361
|
+
end
|
362
|
+
```
|
363
|
+
|
364
|
+
|
328
365
|
How do I introduce my mocks?
|
329
366
|
============================
|
330
367
|
|
@@ -356,22 +393,27 @@ Special Thanks
|
|
356
393
|
TODO
|
357
394
|
----
|
358
395
|
|
396
|
+
* Add proper failure messages for block invocations
|
397
|
+
* Add `was told_to` syntax
|
398
|
+
* Add support for predicates
|
359
399
|
* Add a better explanation for motivations
|
360
400
|
* 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)
|
361
401
|
* don't blow up when delegating to the Object#initialize with args
|
362
|
-
*
|
402
|
+
* config: rspec_mocks loaded, whether unprepared blocks should raise or just return nil
|
403
|
+
* extract surrogate/rspec into its own gem
|
404
|
+
* support subset-substitutabilty not being able to touch real methods (e.g. #respond_to?)
|
405
|
+
* Add a last_instance option so you don't have to track it explicitly
|
363
406
|
|
364
407
|
|
365
408
|
Future Features
|
366
409
|
---------------
|
367
410
|
|
411
|
+
* figure out how to talk about callbacks like #on_success
|
368
412
|
* have some sort of reinitialization that can hook into setup/teardown steps of test suite
|
369
413
|
* Support arity checking as part of substitutability
|
370
|
-
* Support for blocks
|
371
414
|
* 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)
|
372
|
-
* declare normal methods as being part of the API
|
415
|
+
* ability to declare normal methods as being part of the API
|
416
|
+
* ability to declare a define that uses the overridden method as the body, but can still act like an api method
|
373
417
|
* assertions for order of invocations & methods
|
374
418
|
* class generator? (supports a top-down style of development for when you write your mocks before you write your implementations)
|
375
|
-
* extract surrogate/rspec into its own gem?
|
376
419
|
* deal with hard dependency on rspec-mocks
|
377
|
-
* support subset-substitutabilty not being able to touch real methods (e.g. #respond_to?)
|
data/lib/surrogate/hatchling.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
class Surrogate
|
2
|
-
|
2
|
+
SurrogateError = Class.new StandardError
|
3
|
+
UnknownMethod = Class.new SurrogateError
|
3
4
|
|
4
5
|
|
5
6
|
# This contains the unique behaviour for each instance
|
@@ -16,9 +17,10 @@ class Surrogate
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def invoke_method(method_name, args, &block)
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
invocation = Invocation.new(args, &block)
|
21
|
+
invoked_methods[method_name] << invocation
|
22
|
+
return get_default method_name, invocation, &block unless has_ivar? method_name
|
23
|
+
Value.factory(get_ivar method_name).value(method_name)
|
22
24
|
end
|
23
25
|
|
24
26
|
def prepare_method(method_name, args, &block)
|
@@ -29,7 +31,28 @@ class Surrogate
|
|
29
31
|
invoked_methods[method_name]
|
30
32
|
end
|
31
33
|
|
32
|
-
|
34
|
+
private
|
35
|
+
|
36
|
+
def invoked_methods
|
37
|
+
@invoked_methods ||= Hash.new do |hash, method_name|
|
38
|
+
must_know method_name
|
39
|
+
hash[method_name] = []
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_default(method_name, invocation)
|
44
|
+
api_methods[method_name].default instance, invocation do
|
45
|
+
raise UnpreparedMethodError, "#{method_name} has been invoked without being told how to behave"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def must_know(method_name)
|
50
|
+
return if api_methods.has_key? method_name
|
51
|
+
known_methods = api_methods.keys.map(&:to_s).map(&:inspect).join ', '
|
52
|
+
raise UnknownMethod, "doesn't know \"#{method_name}\", only knows #{known_methods}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# maybe these ivar methods should be extracted into their own class
|
33
56
|
def has_ivar?(method_name)
|
34
57
|
instance.instance_variable_defined? ivar_for method_name
|
35
58
|
end
|
@@ -55,26 +78,6 @@ class Surrogate
|
|
55
78
|
end
|
56
79
|
end
|
57
80
|
|
58
|
-
private
|
59
|
-
|
60
|
-
def invoked_methods
|
61
|
-
@invoked_methods ||= Hash.new do |hash, method_name|
|
62
|
-
must_know method_name
|
63
|
-
hash[method_name] = []
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def get_default(method_name, args, &block)
|
68
|
-
api_methods[method_name].default instance, args, block do
|
69
|
-
raise UnpreparedMethodError, "#{method_name} has been invoked without being told how to behave"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def must_know(method_name)
|
74
|
-
return if api_methods.has_key? method_name
|
75
|
-
known_methods = api_methods.keys.map(&:to_s).map(&:inspect).join ', '
|
76
|
-
raise UnknownMethod, "doesn't know \"#{method_name}\", only knows #{known_methods}"
|
77
|
-
end
|
78
81
|
end
|
79
82
|
end
|
80
83
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Surrogate
|
2
|
+
class Invocation
|
3
|
+
attr_accessor :args, :block
|
4
|
+
|
5
|
+
def initialize(args, &block)
|
6
|
+
self.args, self.block = args, block
|
7
|
+
end
|
8
|
+
|
9
|
+
def has_block?
|
10
|
+
!!block
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(invocation)
|
14
|
+
args == invocation.args && has_block? == invocation.has_block?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/surrogate/options.rb
CHANGED
@@ -20,7 +20,7 @@ class Surrogate
|
|
20
20
|
options
|
21
21
|
end
|
22
22
|
|
23
|
-
def default(instance,
|
23
|
+
def default(instance, invocation, &no_default)
|
24
24
|
if options.has_key? :default
|
25
25
|
options[:default]
|
26
26
|
elsif default_proc
|
@@ -31,7 +31,7 @@ class Surrogate
|
|
31
31
|
# the options, then we only have to bind it to an instance and invoke
|
32
32
|
BindableBlock.new(instance.class, &default_proc)
|
33
33
|
.bind(instance)
|
34
|
-
.call(*args, &block)
|
34
|
+
.call(*invocation.args, &invocation.block)
|
35
35
|
else
|
36
36
|
no_default.call
|
37
37
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Surrogate
|
2
|
+
module RSpec
|
3
|
+
class AbstractFailureMessage
|
4
|
+
class ArgsInspector
|
5
|
+
def self.inspect(invocation)
|
6
|
+
inspected_arguments = invocation.args.map { |argument| inspect_argument argument }
|
7
|
+
inspected_arguments << 'no args' if inspected_arguments.empty?
|
8
|
+
"`" << inspected_arguments.join(", ") << "'"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.inspect_argument(to_inspect)
|
12
|
+
if RSpec.rspec_mocks_loaded? && to_inspect.respond_to?(:description)
|
13
|
+
to_inspect.description
|
14
|
+
else
|
15
|
+
to_inspect.inspect
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :method_name, :invocations, :with_filter, :times_predicate
|
21
|
+
|
22
|
+
def initialize(method_name, invocations, with_filter, times_predicate)
|
23
|
+
self.method_name = method_name
|
24
|
+
self.invocations = invocations
|
25
|
+
self.with_filter = with_filter
|
26
|
+
self.times_predicate = times_predicate
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_message
|
30
|
+
raise "I should have been overridden"
|
31
|
+
end
|
32
|
+
|
33
|
+
def times_invoked
|
34
|
+
invocations.size
|
35
|
+
end
|
36
|
+
|
37
|
+
def inspect_arguments(arguments)
|
38
|
+
# can we fix this as soon as an array enters the system instead of catching it here?
|
39
|
+
if arguments.kind_of? Invocation
|
40
|
+
ArgsInspector.inspect arguments
|
41
|
+
else
|
42
|
+
ArgsInspector.inspect Invocation.new arguments
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def expected_invocation
|
47
|
+
with_filter.expected_invocation
|
48
|
+
end
|
49
|
+
|
50
|
+
def times_msg(n)
|
51
|
+
"#{n} time#{'s' unless n == 1}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def expected_times_invoked
|
55
|
+
times_predicate.expected_times_invoked
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'surrogate/rspec/invocation_matcher'
|
2
|
+
|
3
|
+
class Surrogate
|
4
|
+
module RSpec
|
5
|
+
class HaveBeenAskedForIts < InvocationMatcher
|
6
|
+
|
7
|
+
class FailureMessageShouldDefault < AbstractFailureMessage
|
8
|
+
def get_message
|
9
|
+
"was never asked for its #{ method_name }"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class FailureMessageShouldWith < AbstractFailureMessage
|
14
|
+
def get_message
|
15
|
+
message = "should have been asked for its #{ method_name } with #{ inspect_arguments expected_invocation }, but "
|
16
|
+
if times_invoked.zero?
|
17
|
+
message << "was never asked"
|
18
|
+
else
|
19
|
+
inspected_invocations = invocations.map { |invocation| inspect_arguments invocation }
|
20
|
+
message << "got #{inspected_invocations.join ', '}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class FailureMessageShouldTimes < AbstractFailureMessage
|
26
|
+
def get_message
|
27
|
+
"should have been asked for its #{ method_name } #{ times_msg expected_times_invoked }, but was asked #{ times_msg times_invoked }"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class FailureMessageWithTimes < AbstractFailureMessage
|
32
|
+
def get_message
|
33
|
+
message = "should have been asked for its #{ method_name } #{ times_msg expected_times_invoked } with #{ inspect_arguments expected_invocation }, but "
|
34
|
+
if times_invoked.zero?
|
35
|
+
message << "was never asked"
|
36
|
+
else
|
37
|
+
message << "was asked #{times_msg times_invoked}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class FailureMessageShouldNotDefault < AbstractFailureMessage
|
43
|
+
def get_message
|
44
|
+
"shouldn't have been asked for its #{ method_name }, but was asked #{ times_msg times_invoked }"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class FailureMessageShouldNotWith < AbstractFailureMessage
|
49
|
+
def get_message
|
50
|
+
message = "should not have been asked for its #{ method_name } with #{ inspect_arguments expected_invocation }, but "
|
51
|
+
if times_invoked.zero?
|
52
|
+
message << "was never asked"
|
53
|
+
else
|
54
|
+
inspected_invocations = invocations.map { |invocation| inspect_arguments invocation }
|
55
|
+
message << "got #{inspected_invocations.join ', '}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class FailureMessageShouldNotTimes < AbstractFailureMessage
|
61
|
+
def get_message
|
62
|
+
"shouldn't have been asked for its #{ method_name } #{ times_msg expected_times_invoked }, but was"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class FailureMessageShouldNotWithTimes < AbstractFailureMessage
|
67
|
+
def get_message
|
68
|
+
message = "should not have been asked for its #{ method_name } #{ times_msg expected_times_invoked } with #{ inspect_arguments expected_invocation }, "
|
69
|
+
if times_invoked.zero?
|
70
|
+
message << "was never asked"
|
71
|
+
else
|
72
|
+
message << "was asked #{times_msg times_invoked}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'surrogate/rspec/have_been_told_to'
|
2
|
+
|
3
|
+
class Surrogate
|
4
|
+
module RSpec
|
5
|
+
class HaveBeenInitializedWith < HaveBeenToldTo
|
6
|
+
def initialize(*initialization_args, &block)
|
7
|
+
super :initialize
|
8
|
+
with *initialization_args, &block
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'surrogate/rspec/invocation_matcher'
|
2
|
+
|
3
|
+
class Surrogate
|
4
|
+
module RSpec
|
5
|
+
class HaveBeenToldTo < InvocationMatcher
|
6
|
+
class FailureMessageShouldDefault < AbstractFailureMessage
|
7
|
+
def get_message
|
8
|
+
"was never told to #{ method_name }"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class FailureMessageShouldWith < AbstractFailureMessage
|
13
|
+
def get_message
|
14
|
+
message = "should have been told to #{ method_name } with #{ inspect_arguments expected_invocation }, but "
|
15
|
+
if times_invoked.zero?
|
16
|
+
message << "was never told to"
|
17
|
+
else
|
18
|
+
inspected_invocations = invocations.map { |invocation| inspect_arguments invocation }
|
19
|
+
message << "got #{inspected_invocations.join ', '}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class FailureMessageShouldTimes < AbstractFailureMessage
|
25
|
+
def get_message
|
26
|
+
"should have been told to #{ method_name } #{ times_msg expected_times_invoked } but was told to #{ method_name } #{ times_msg times_invoked }"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class FailureMessageWithTimes < AbstractFailureMessage
|
31
|
+
def get_message
|
32
|
+
message = "should have been told to #{ method_name } #{ times_msg expected_times_invoked } with #{ inspect_arguments expected_invocation }, but "
|
33
|
+
if times_invoked.zero?
|
34
|
+
message << "was never told to"
|
35
|
+
else
|
36
|
+
message << "got it #{times_msg times_invoked}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class FailureMessageShouldNotDefault < AbstractFailureMessage
|
42
|
+
def get_message
|
43
|
+
"shouldn't have been told to #{ method_name }, but was told to #{ method_name } #{ times_msg times_invoked }"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class FailureMessageShouldNotWith < AbstractFailureMessage
|
48
|
+
def get_message
|
49
|
+
message = "should not have been told to #{ method_name } with #{ inspect_arguments expected_invocation }, but "
|
50
|
+
if times_invoked.zero?
|
51
|
+
message << "was never told to"
|
52
|
+
else
|
53
|
+
inspected_invocations = invocations.map { |invocation| inspect_arguments invocation }
|
54
|
+
message << "got #{inspected_invocations.join ', '}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class FailureMessageShouldNotTimes < AbstractFailureMessage
|
60
|
+
def get_message
|
61
|
+
"shouldn't have been told to #{ method_name } #{ times_msg expected_times_invoked }, but was"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class FailureMessageShouldNotWithTimes < AbstractFailureMessage
|
66
|
+
def get_message
|
67
|
+
message = "should not have been told to #{ method_name } #{ times_msg expected_times_invoked } with #{ inspect_arguments expected_invocation }, but "
|
68
|
+
if times_invoked.zero?
|
69
|
+
message << "was never told to"
|
70
|
+
else
|
71
|
+
message << "got it #{times_msg times_invoked}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'surrogate/rspec/abstract_failure_message'
|
2
|
+
require 'surrogate/rspec/times_predicate'
|
3
|
+
require 'surrogate/rspec/with_filter'
|
4
|
+
|
5
|
+
class Surrogate
|
6
|
+
module RSpec
|
7
|
+
class InvocationMatcher
|
8
|
+
attr_accessor :times_predicate, :with_filter, :surrogate, :method_name
|
9
|
+
|
10
|
+
def initialize(method_name)
|
11
|
+
self.method_name = method_name
|
12
|
+
self.times_predicate = TimesPredicate.new
|
13
|
+
self.with_filter = WithFilter.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(surrogate)
|
17
|
+
self.surrogate = surrogate
|
18
|
+
times_predicate.matches? filtered_args
|
19
|
+
end
|
20
|
+
|
21
|
+
def filtered_args
|
22
|
+
@filtered_args ||= with_filter.filter invocations
|
23
|
+
end
|
24
|
+
|
25
|
+
def invocations
|
26
|
+
surrogate.invocations(method_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def failure_message_for_should
|
30
|
+
raise "THIS METHOD SHOULD HAVE BEEN OVERRIDDEN"
|
31
|
+
end
|
32
|
+
|
33
|
+
def failure_message_for_should_not
|
34
|
+
raise "THIS METHOD SHOULD HAVE BEEN OVERRIDDEN"
|
35
|
+
end
|
36
|
+
|
37
|
+
def times(times_invoked)
|
38
|
+
@times_predicate = TimesPredicate.new(times_invoked, :==)
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def with(*arguments, &expectation_block)
|
43
|
+
self.with_filter = WithFilter.new arguments, :args_must_match, &expectation_block
|
44
|
+
arguments << expectation_block if expectation_block
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def failure_message_for_should
|
49
|
+
message_for(
|
50
|
+
if times_predicate.default? && with_filter.default?
|
51
|
+
:FailureMessageShouldDefault
|
52
|
+
elsif times_predicate.default?
|
53
|
+
:FailureMessageShouldWith
|
54
|
+
elsif with_filter.default?
|
55
|
+
:FailureMessageShouldTimes
|
56
|
+
else
|
57
|
+
:FailureMessageWithTimes
|
58
|
+
end
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def failure_message_for_should_not
|
63
|
+
message_for(
|
64
|
+
if times_predicate.default? && with_filter.default?
|
65
|
+
:FailureMessageShouldNotDefault
|
66
|
+
elsif times_predicate.default?
|
67
|
+
:FailureMessageShouldNotWith
|
68
|
+
elsif with_filter.default?
|
69
|
+
:FailureMessageShouldNotTimes
|
70
|
+
else
|
71
|
+
:FailureMessageShouldNotWithTimes
|
72
|
+
end
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def message_for(failure_class_name)
|
77
|
+
self.class.const_get(failure_class_name).new(method_name, invocations, with_filter, times_predicate).get_message
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Surrogate
|
2
|
+
module RSpec
|
3
|
+
class TimesPredicate
|
4
|
+
attr_accessor :expected_times_invoked, :comparer
|
5
|
+
def initialize(expected_times_invoked=0, comparer=:<)
|
6
|
+
self.expected_times_invoked = expected_times_invoked
|
7
|
+
self.comparer = comparer
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(invocations)
|
11
|
+
expected_times_invoked.send comparer, invocations.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def default?
|
15
|
+
expected_times_invoked == 0 && comparer == :<
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
class Surrogate
|
2
|
+
module RSpec
|
3
|
+
class WithFilter
|
4
|
+
|
5
|
+
class BlockAsserter
|
6
|
+
def initialize(definition_block)
|
7
|
+
@call_with = Invocation.new []
|
8
|
+
definition_block.call self
|
9
|
+
end
|
10
|
+
|
11
|
+
def call_with(*args, &block)
|
12
|
+
@call_with = Invocation.new args, &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def returns(value=nil, &block)
|
16
|
+
@returns = block || lambda { |returned| returned.should == value }
|
17
|
+
end
|
18
|
+
|
19
|
+
def before(&block)
|
20
|
+
@before = block
|
21
|
+
end
|
22
|
+
|
23
|
+
def after(&block)
|
24
|
+
@after = block
|
25
|
+
end
|
26
|
+
|
27
|
+
def arity(n)
|
28
|
+
@arity = n
|
29
|
+
end
|
30
|
+
|
31
|
+
def matches?(block_to_test)
|
32
|
+
matches = before_matches? block_to_test
|
33
|
+
matches &&= return_value_matches? block_to_test
|
34
|
+
matches &&= arity_matches? block_to_test
|
35
|
+
matches &&= after_matches? block_to_test
|
36
|
+
matches
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# matches if no return specified, or returned value == specified value
|
42
|
+
def return_value_matches?(block_to_test)
|
43
|
+
returned_value = block_to_test.call(*@call_with.args, &@call_with.block)
|
44
|
+
@returns.call returned_value if @returns
|
45
|
+
true
|
46
|
+
rescue ::RSpec::Expectations::ExpectationNotMetError
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
50
|
+
# matches if the first time it is called, it raises nothing
|
51
|
+
def before_matches?(*)
|
52
|
+
@before_has_been_invoked || (@before && @before.call)
|
53
|
+
ensure
|
54
|
+
return @before_has_been_invoked = true unless $!
|
55
|
+
end
|
56
|
+
|
57
|
+
# matches if nothing is raised
|
58
|
+
def after_matches?(block_to_test)
|
59
|
+
@after && @after.call
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def arity_matches?(block_to_test)
|
64
|
+
return true unless @arity
|
65
|
+
block_to_test.arity == @arity
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_accessor :block_to_test
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
# rename args to invocation
|
75
|
+
attr_accessor :expected_invocation, :block, :pass, :filter_name
|
76
|
+
|
77
|
+
def initialize(args=[], filter_name=:default_filter, &block)
|
78
|
+
self.expected_invocation = Invocation.new args.dup, &block
|
79
|
+
self.block = block
|
80
|
+
self.pass = send filter_name
|
81
|
+
self.filter_name = filter_name
|
82
|
+
end
|
83
|
+
|
84
|
+
def filter(invocations)
|
85
|
+
invocations.select &pass
|
86
|
+
end
|
87
|
+
|
88
|
+
def default?
|
89
|
+
filter_name == :default_filter
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def default_filter
|
95
|
+
Proc.new { true }
|
96
|
+
end
|
97
|
+
|
98
|
+
def args_must_match
|
99
|
+
lambda { |invocation| args_match?(invocation) && blocks_match?(invocation) }
|
100
|
+
end
|
101
|
+
|
102
|
+
def blocks_match?(actual_invocation)
|
103
|
+
# surely this is wrong
|
104
|
+
return true unless expected_invocation.has_block?
|
105
|
+
return unless actual_invocation.has_block?
|
106
|
+
block_asserter.matches? actual_invocation.block
|
107
|
+
end
|
108
|
+
|
109
|
+
def block_asserter
|
110
|
+
@block_asserter ||= BlockAsserter.new expected_invocation.block
|
111
|
+
end
|
112
|
+
|
113
|
+
def args_match?(actual_invocation)
|
114
|
+
if RSpec.rspec_mocks_loaded?
|
115
|
+
rspec_arg_expectation = ::RSpec::Mocks::ArgumentExpectation.new *expected_invocation.args
|
116
|
+
rspec_arg_expectation.args_match? *actual_invocation.args
|
117
|
+
else
|
118
|
+
expected_arguments == actual_arguments
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|