shoulda-matchers 2.6.0 → 2.6.1.rc1
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/Gemfile.lock +1 -1
- data/NEWS.md +34 -0
- data/README.md +14 -0
- data/features/activemodel_integration.feature +15 -0
- data/features/step_definitions/activemodel_steps.rb +21 -0
- data/gemfiles/3.0.gemfile.lock +1 -1
- data/gemfiles/3.1.gemfile.lock +1 -1
- data/gemfiles/3.2.gemfile.lock +1 -1
- data/gemfiles/4.0.0.gemfile.lock +1 -1
- data/gemfiles/4.0.1.gemfile.lock +1 -1
- data/gemfiles/4.1.gemfile.lock +1 -1
- data/lib/shoulda/matchers.rb +1 -0
- data/lib/shoulda/matchers/action_controller/callback_matcher.rb +11 -6
- data/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb +59 -95
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +10 -18
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +10 -0
- data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +60 -18
- data/lib/shoulda/matchers/active_model/errors.rb +9 -7
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +4 -0
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +24 -5
- data/lib/shoulda/matchers/doublespeak.rb +27 -0
- data/lib/shoulda/matchers/doublespeak/double.rb +74 -0
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +54 -0
- data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +27 -0
- data/lib/shoulda/matchers/doublespeak/object_double.rb +32 -0
- data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +30 -0
- data/lib/shoulda/matchers/doublespeak/structs.rb +8 -0
- data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +34 -0
- data/lib/shoulda/matchers/doublespeak/world.rb +38 -0
- data/lib/shoulda/matchers/independent/delegate_matcher.rb +112 -61
- data/lib/shoulda/matchers/integrations/test_unit.rb +8 -6
- data/lib/shoulda/matchers/rails_shim.rb +16 -0
- data/lib/shoulda/matchers/version.rb +1 -1
- data/spec/shoulda/matchers/action_controller/callback_matcher_spec.rb +22 -19
- data/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +174 -65
- data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +14 -0
- data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +553 -211
- data/spec/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +6 -0
- data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +22 -0
- data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +23 -4
- data/spec/shoulda/matchers/doublespeak/double_collection_spec.rb +102 -0
- data/spec/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +21 -0
- data/spec/shoulda/matchers/doublespeak/double_spec.rb +144 -0
- data/spec/shoulda/matchers/doublespeak/object_double_spec.rb +77 -0
- data/spec/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +40 -0
- data/spec/shoulda/matchers/doublespeak/stub_implementation_spec.rb +88 -0
- data/spec/shoulda/matchers/doublespeak/world_spec.rb +88 -0
- data/spec/shoulda/matchers/doublespeak_spec.rb +19 -0
- data/spec/shoulda/matchers/independent/delegate_matcher_spec.rb +105 -39
- data/spec/support/controller_builder.rb +18 -9
- data/spec/support/rails_versions.rb +4 -0
- metadata +34 -8
@@ -0,0 +1,32 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module Doublespeak
|
4
|
+
class ObjectDouble < BasicObject
|
5
|
+
attr_reader :calls
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@calls = []
|
9
|
+
@calls_by_method_name = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def calls_to(method_name)
|
13
|
+
@calls_by_method_name[method_name] || []
|
14
|
+
end
|
15
|
+
|
16
|
+
def respond_to?(name, include_private = nil)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(method_name, *args, &block)
|
21
|
+
calls << MethodCallWithName.new(method_name, args, block)
|
22
|
+
(calls_by_method_name[method_name] ||= []) << MethodCall.new(args, block)
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :calls_by_method_name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module Doublespeak
|
4
|
+
class ProxyImplementation
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
DoubleImplementationRegistry.register(self, :proxy)
|
8
|
+
|
9
|
+
def_delegators :stub_implementation, :returns
|
10
|
+
|
11
|
+
def self.create
|
12
|
+
new(StubImplementation.new)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(stub_implementation)
|
16
|
+
@stub_implementation = stub_implementation
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(double, object, args, block)
|
20
|
+
stub_implementation.call(double, object, args, block)
|
21
|
+
double.call_original_method(object, args, block)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :stub_implementation
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module Doublespeak
|
4
|
+
class StubImplementation
|
5
|
+
DoubleImplementationRegistry.register(self, :stub)
|
6
|
+
|
7
|
+
def self.create
|
8
|
+
new
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@implementation = proc { nil }
|
13
|
+
end
|
14
|
+
|
15
|
+
def returns(value = nil, &block)
|
16
|
+
if block
|
17
|
+
@implementation = block
|
18
|
+
else
|
19
|
+
@implementation = proc { value }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(double, object, args, block)
|
24
|
+
double.record_call(args, block)
|
25
|
+
implementation.call(object, args, block)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :implementation
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module Doublespeak
|
4
|
+
class World
|
5
|
+
def register_double_collection(klass)
|
6
|
+
double_collection = DoubleCollection.new(klass)
|
7
|
+
double_collections_by_class[klass] = double_collection
|
8
|
+
double_collection
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_doubles_activated
|
12
|
+
activate
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
deactivate
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def activate
|
21
|
+
double_collections_by_class.each do |klass, double_collection|
|
22
|
+
double_collection.activate
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def deactivate
|
27
|
+
double_collections_by_class.each do |klass, double_collection|
|
28
|
+
double_collection.deactivate
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def double_collections_by_class
|
33
|
+
@_double_collections_by_class ||= {}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -5,19 +5,21 @@ module Shoulda # :nodoc:
|
|
5
5
|
# Ensure that a given method is delegated properly.
|
6
6
|
#
|
7
7
|
# Basic Syntax:
|
8
|
-
# it { should delegate_method(
|
8
|
+
# it { should delegate_method(method_name).to(delegate_name) }
|
9
9
|
#
|
10
10
|
# Options:
|
11
|
-
# * <tt>:as</tt> -
|
12
|
-
#
|
13
|
-
# * <tt>:with_arguments</tt> -
|
14
|
-
#
|
11
|
+
# * <tt>:as</tt> - The name of the delegating method. Defaults to
|
12
|
+
# method_name.
|
13
|
+
# * <tt>:with_arguments</tt> - Tests that the delegate method is called
|
14
|
+
# with certain arguments.
|
15
15
|
#
|
16
16
|
# Examples:
|
17
|
+
# it { should delegate_method(:deliver_mail).to(:mailman) }
|
17
18
|
# it { should delegate_method(:deliver_mail).to(:mailman).
|
18
|
-
# as(:
|
19
|
+
# as(:deliver_mail_via_mailman) }
|
19
20
|
# it { should delegate_method(:deliver_mail).to(:mailman).
|
20
|
-
#
|
21
|
+
# as(:deliver_mail_hastily).
|
22
|
+
# with_arguments('221B Baker St.', hastily: true) }
|
21
23
|
#
|
22
24
|
def delegate_method(delegating_method)
|
23
25
|
DelegateMatcher.new(delegating_method)
|
@@ -26,20 +28,23 @@ module Shoulda # :nodoc:
|
|
26
28
|
class DelegateMatcher
|
27
29
|
def initialize(delegating_method)
|
28
30
|
@delegating_method = delegating_method
|
31
|
+
@method_on_target = @delegating_method
|
32
|
+
@target_double = Doublespeak::ObjectDouble.new
|
33
|
+
|
29
34
|
@delegated_arguments = []
|
35
|
+
@target_method = nil
|
36
|
+
@subject = nil
|
37
|
+
@subject_double_collection = nil
|
30
38
|
end
|
31
39
|
|
32
|
-
def matches?(
|
33
|
-
@subject =
|
40
|
+
def matches?(subject)
|
41
|
+
@subject = subject
|
42
|
+
|
34
43
|
ensure_target_method_is_present!
|
35
|
-
stub_target
|
36
44
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
rescue NoMethodError
|
41
|
-
false
|
42
|
-
end
|
45
|
+
subject_has_delegating_method? &&
|
46
|
+
subject_has_target_method? &&
|
47
|
+
subject_delegates_to_target_correctly?
|
43
48
|
end
|
44
49
|
|
45
50
|
def description
|
@@ -48,10 +53,6 @@ module Shoulda # :nodoc:
|
|
48
53
|
)
|
49
54
|
end
|
50
55
|
|
51
|
-
def does_not_match?(subject)
|
52
|
-
raise InvalidDelegateMatcher
|
53
|
-
end
|
54
|
-
|
55
56
|
def to(target_method)
|
56
57
|
@target_method = target_method
|
57
58
|
self
|
@@ -68,69 +69,77 @@ module Shoulda # :nodoc:
|
|
68
69
|
end
|
69
70
|
|
70
71
|
def failure_message
|
71
|
-
base = "Expected #{
|
72
|
+
base = "Expected #{formatted_delegating_method_name} to delegate to #{formatted_target_method_name}"
|
72
73
|
add_clarifications_to(base)
|
74
|
+
base << "\nCalls on #{formatted_target_method_name}:"
|
75
|
+
base << formatted_calls_on_target
|
76
|
+
base.strip
|
73
77
|
end
|
74
78
|
alias failure_message_for_should failure_message
|
75
79
|
|
80
|
+
def failure_message_when_negated
|
81
|
+
base = "Expected #{formatted_delegating_method_name} not to delegate to #{formatted_target_method_name}"
|
82
|
+
add_clarifications_to(base)
|
83
|
+
base << ', but it did'
|
84
|
+
end
|
85
|
+
alias failure_message_for_should_not failure_message_when_negated
|
86
|
+
|
76
87
|
private
|
77
88
|
|
78
|
-
attr_reader
|
79
|
-
:
|
89
|
+
attr_reader \
|
90
|
+
:delegated_arguments,
|
91
|
+
:delegating_method,
|
92
|
+
:method,
|
93
|
+
:method_on_target,
|
94
|
+
:subject,
|
95
|
+
:subject_double_collection,
|
96
|
+
:target_double,
|
97
|
+
:target_method
|
80
98
|
|
81
99
|
def add_clarifications_to(message)
|
82
|
-
if delegated_arguments.
|
100
|
+
if delegated_arguments.any?
|
83
101
|
message << " with arguments: #{delegated_arguments.inspect}"
|
84
102
|
end
|
85
103
|
|
86
|
-
if method_on_target
|
104
|
+
if method_on_target != delegating_method
|
87
105
|
message << " as ##{method_on_target}"
|
88
106
|
end
|
89
107
|
|
90
108
|
message
|
91
109
|
end
|
92
110
|
|
93
|
-
def
|
94
|
-
|
111
|
+
def formatted_delegating_method_name
|
112
|
+
formatted_method_name_for(delegating_method)
|
95
113
|
end
|
96
114
|
|
97
|
-
def
|
98
|
-
|
115
|
+
def formatted_target_method_name
|
116
|
+
formatted_method_name_for(target_method)
|
99
117
|
end
|
100
118
|
|
101
|
-
def
|
102
|
-
if Class
|
103
|
-
subject.name + '.' +
|
119
|
+
def formatted_method_name_for(method_name)
|
120
|
+
if subject.is_a?(Class)
|
121
|
+
subject.name + '.' + method_name.to_s
|
104
122
|
else
|
105
|
-
subject.class.name + '#' +
|
123
|
+
subject.class.name + '#' + method_name.to_s
|
106
124
|
end
|
107
125
|
end
|
108
126
|
|
109
|
-
def
|
110
|
-
|
127
|
+
def target_received_method?
|
128
|
+
calls_to_method_on_target.any?
|
111
129
|
end
|
112
130
|
|
113
|
-
def
|
114
|
-
|
131
|
+
def target_received_method_with_delegated_arguments?
|
132
|
+
calls_to_method_on_target.any? do |call|
|
133
|
+
call.args == delegated_arguments
|
134
|
+
end
|
115
135
|
end
|
116
136
|
|
117
|
-
def
|
118
|
-
|
137
|
+
def subject_has_delegating_method?
|
138
|
+
subject.respond_to?(delegating_method)
|
119
139
|
end
|
120
140
|
|
121
|
-
def
|
122
|
-
|
123
|
-
local_target_method = target_method
|
124
|
-
|
125
|
-
subject.instance_eval do
|
126
|
-
define_singleton_method local_target_method do
|
127
|
-
local_stubbed_target
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def stubbed_target
|
133
|
-
@stubbed_target ||= StubbedTarget.new(stubbed_method)
|
141
|
+
def subject_has_target_method?
|
142
|
+
subject.respond_to?(target_method)
|
134
143
|
end
|
135
144
|
|
136
145
|
def ensure_target_method_is_present!
|
@@ -138,18 +147,60 @@ module Shoulda # :nodoc:
|
|
138
147
|
raise TargetNotDefinedError
|
139
148
|
end
|
140
149
|
end
|
141
|
-
end
|
142
150
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
151
|
+
def subject_delegates_to_target_correctly?
|
152
|
+
register_subject_double_collection
|
153
|
+
|
154
|
+
Doublespeak.with_doubles_activated do
|
155
|
+
subject.public_send(delegating_method, *delegated_arguments)
|
156
|
+
end
|
157
|
+
|
158
|
+
if delegated_arguments.any?
|
159
|
+
target_received_method_with_delegated_arguments?
|
160
|
+
else
|
161
|
+
target_received_method?
|
162
|
+
end
|
147
163
|
end
|
148
|
-
end
|
149
164
|
|
150
|
-
|
151
|
-
|
152
|
-
|
165
|
+
def register_subject_double_collection
|
166
|
+
double_collection =
|
167
|
+
Doublespeak.register_double_collection(subject.singleton_class)
|
168
|
+
double_collection.register_stub(target_method).
|
169
|
+
to_return(target_double)
|
170
|
+
|
171
|
+
@subject_double_collection = double_collection
|
172
|
+
end
|
173
|
+
|
174
|
+
def calls_to_method_on_target
|
175
|
+
target_double.calls_to(method_on_target)
|
176
|
+
end
|
177
|
+
|
178
|
+
def calls_on_target
|
179
|
+
target_double.calls
|
180
|
+
end
|
181
|
+
|
182
|
+
def formatted_calls_on_target
|
183
|
+
string = ""
|
184
|
+
|
185
|
+
if calls_on_target.any?
|
186
|
+
string << "\n"
|
187
|
+
calls_on_target.each_with_index do |call, i|
|
188
|
+
name = call.method_name
|
189
|
+
args = call.args.map { |arg| arg.inspect }.join(', ')
|
190
|
+
string << "#{i+1}) #{name}(#{args})\n"
|
191
|
+
end
|
192
|
+
else
|
193
|
+
string << " (none)"
|
194
|
+
end
|
195
|
+
|
196
|
+
string
|
197
|
+
end
|
198
|
+
|
199
|
+
class TargetNotDefinedError < StandardError
|
200
|
+
def message
|
201
|
+
'Delegation needs a target. Use the #to method to define one, e.g.
|
202
|
+
`post_office.should delegate(:deliver_mail).to(:mailman)`'.squish
|
203
|
+
end
|
153
204
|
end
|
154
205
|
end
|
155
206
|
end
|
@@ -21,12 +21,14 @@ end
|
|
21
21
|
|
22
22
|
if defined?(ActiveSupport::TestCase)
|
23
23
|
ActiveSupport::TestCase.class_eval do
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
if defined?(Shoulda::Matchers::ActiveRecord)
|
25
|
+
include Shoulda::Matchers::ActiveRecord
|
26
|
+
extend Shoulda::Matchers::ActiveRecord
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
if defined?(Shoulda::Matchers::ActiveModel)
|
30
|
+
include Shoulda::Matchers::ActiveModel
|
31
|
+
extend Shoulda::Matchers::ActiveModel
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
@@ -33,6 +33,14 @@ module Shoulda # :nodoc:
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
def self.verb_for_update
|
37
|
+
if action_pack_gte_4_1?
|
38
|
+
:patch
|
39
|
+
else
|
40
|
+
:put
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
36
44
|
def self.active_record_major_version
|
37
45
|
::ActiveRecord::VERSION::MAJOR
|
38
46
|
end
|
@@ -44,6 +52,14 @@ module Shoulda # :nodoc:
|
|
44
52
|
def self.action_pack_major_version
|
45
53
|
::ActionPack::VERSION::MAJOR
|
46
54
|
end
|
55
|
+
|
56
|
+
def self.action_pack_gte_4_1?
|
57
|
+
Gem::Requirement.new('>= 4.1').satisfied_by?(action_pack_version)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.action_pack_version
|
61
|
+
Gem::Version.new(::ActionPack::VERSION::STRING)
|
62
|
+
end
|
47
63
|
end
|
48
64
|
end
|
49
65
|
end
|