surrogate 0.3.2 → 0.4.2
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/Rakefile +12 -0
- data/Readme.md +6 -5
- data/lib/surrogate/endower.rb +2 -2
- data/lib/surrogate/hatchery.rb +3 -3
- data/lib/surrogate/hatchling.rb +13 -4
- data/lib/surrogate/rspec/api_method_matchers.rb +60 -73
- data/lib/surrogate/rspec.rb +21 -0
- data/lib/surrogate/values.rb +7 -2
- data/lib/surrogate/version.rb +1 -1
- data/spec/defining_api_methods_spec.rb +195 -187
- data/spec/rspec/have_been_asked_for_its_spec.rb +6 -1
- data/spec/rspec/messages_spec.rb +5 -4
- data/spec/rspec/rspec_mocks_integration_spec.rb +39 -0
- metadata +6 -4
data/Rakefile
CHANGED
data/Readme.md
CHANGED
@@ -64,13 +64,14 @@ end
|
|
64
64
|
MockClient.new.request 3 # => ["result1", "result2", "result3"]
|
65
65
|
```
|
66
66
|
|
67
|
-
You don't need a **default if you set the ivar** of the same name
|
67
|
+
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)
|
68
68
|
|
69
69
|
```ruby
|
70
70
|
class MockClient
|
71
71
|
Surrogate.endow self
|
72
|
-
define(:initialize) { |id| @id = id }
|
72
|
+
define(:initialize) { |id| @id, @connected_p = id, true }
|
73
73
|
define :id
|
74
|
+
define :connected?
|
74
75
|
end
|
75
76
|
MockClient.new(12).id # => 12
|
76
77
|
```
|
@@ -318,7 +319,7 @@ end
|
|
318
319
|
# doesn't matter that real user has a name as long as it has initialize and id
|
319
320
|
MockUser.should substitute_for User, subset: true
|
320
321
|
|
321
|
-
# but now it fails b/c it has no
|
322
|
+
# but now it fails b/c it has no address
|
322
323
|
MockUser.define :address
|
323
324
|
MockUser.should_not substitute_for User, subset: true
|
324
325
|
```
|
@@ -332,7 +333,7 @@ the initializer, you can pass a factory to your class, you can give the class th
|
|
332
333
|
setter and then override it whenever you feel it is necessary, you can use RSpec's `#stub` method to put
|
333
334
|
it into place.
|
334
335
|
|
335
|
-
Personally, I use [Deject](https://rubygems.org/gems/deject) another gem I wrote. For more on why I feel
|
336
|
+
Personally, I use [Deject](https://rubygems.org/gems/deject), another gem I wrote. For more on why I feel
|
336
337
|
it is a better solution than the above methods, see it's [readme](https://github.com/JoshCheek/deject/tree/938edc985c65358c074a7c7b7bbf18dc11e9450e#why-write-this).
|
337
338
|
|
338
339
|
|
@@ -346,6 +347,7 @@ Special Thanks
|
|
346
347
|
==============
|
347
348
|
|
348
349
|
* [Kyle Hargraves](https://github.com/pd) for changing the name of his internal gem so that I could take Surrogate
|
350
|
+
* [David Chelimsky](http://blog.davidchelimsky.net/) for pairing with me to make Surrogate integrate better with RSpec
|
349
351
|
* [Corey Haines](http://coreyhaines.com/) for pairing on substitutability with me
|
350
352
|
* [Enova](http://www.enovafinancial.com/) for giving me time and motivation to work on this during Enova Labs.
|
351
353
|
* [8th Light](http://8thlight.com/) for giving me time to work on this during our weekly Wazas, and the general encouragement and interest
|
@@ -361,7 +363,6 @@ TODO
|
|
361
363
|
Future Features
|
362
364
|
---------------
|
363
365
|
|
364
|
-
* Support all RSpec matchers (hash_including, anything, etc. see them in RSpec::Mocks::ArgumentMatchers)
|
365
366
|
* have some sort of reinitialization that can hook into setup/teardown steps of test suite
|
366
367
|
* Support arity checking as part of substitutability
|
367
368
|
* Support for blocks
|
data/lib/surrogate/endower.rb
CHANGED
@@ -55,7 +55,7 @@ class Surrogate
|
|
55
55
|
@hijacking_initialize = false
|
56
56
|
end
|
57
57
|
initialize = klass.instance_method :initialize
|
58
|
-
klass.
|
58
|
+
klass.__send__ :define_method, :initialize do |*args, &block|
|
59
59
|
initialize.bind(self).call(*args, &block)
|
60
60
|
end
|
61
61
|
end
|
@@ -65,7 +65,7 @@ class Surrogate
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def remember_invocations_for_instances_of(klass)
|
68
|
-
klass.
|
68
|
+
klass.__send__ :define_method, :invocations do |method_name|
|
69
69
|
@hatchling.invocations method_name
|
70
70
|
end
|
71
71
|
end
|
data/lib/surrogate/hatchery.rb
CHANGED
@@ -30,11 +30,11 @@ class Surrogate
|
|
30
30
|
private
|
31
31
|
|
32
32
|
def klass_can_define_api_methods
|
33
|
-
klass.singleton_class.
|
33
|
+
klass.singleton_class.__send__ :define_method, :define, &method(:define)
|
34
34
|
end
|
35
35
|
|
36
36
|
def add_api_method_for(method_name)
|
37
|
-
klass.
|
37
|
+
klass.__send__ :define_method, method_name do |*args, &block|
|
38
38
|
@hatchling.invoke_method method_name, args, &block
|
39
39
|
end
|
40
40
|
end
|
@@ -48,7 +48,7 @@ class Surrogate
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def add_helpers_for(method_name, helper_name)
|
51
|
-
klass.
|
51
|
+
klass.__send__ :define_method, helper_name do |*args, &block|
|
52
52
|
@hatchling.prepare_method method_name, args, &block
|
53
53
|
self
|
54
54
|
end
|
data/lib/surrogate/hatchling.rb
CHANGED
@@ -31,19 +31,28 @@ class Surrogate
|
|
31
31
|
|
32
32
|
# maybe these four should be extracted into their own class
|
33
33
|
def has_ivar?(method_name)
|
34
|
-
instance.instance_variable_defined?
|
34
|
+
instance.instance_variable_defined? ivar_for method_name
|
35
35
|
end
|
36
36
|
|
37
37
|
def set_ivar(method_name, value)
|
38
|
-
instance.instance_variable_set
|
38
|
+
instance.instance_variable_set ivar_for(method_name), value
|
39
39
|
end
|
40
40
|
|
41
41
|
def get_ivar(method_name)
|
42
|
-
instance.instance_variable_get
|
42
|
+
instance.instance_variable_get ivar_for method_name
|
43
43
|
end
|
44
44
|
|
45
45
|
def unset_ivar(method_name)
|
46
|
-
instance.
|
46
|
+
instance.__send__ :remove_instance_variable, ivar_for(method_name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def ivar_for(method_name)
|
50
|
+
case method_name
|
51
|
+
when /\?$/
|
52
|
+
"@#{method_name.to_s.chop}_p"
|
53
|
+
else
|
54
|
+
"@#{method_name}"
|
55
|
+
end
|
47
56
|
end
|
48
57
|
|
49
58
|
private
|
@@ -50,14 +50,14 @@ class Surrogate
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def inspect_arguments(arguments)
|
53
|
-
inspected_arguments = arguments.map { |argument|
|
54
|
-
inspected_arguments << '
|
55
|
-
|
53
|
+
inspected_arguments = arguments.map { |argument| inspect_argument argument }
|
54
|
+
inspected_arguments << 'no args' if inspected_arguments.empty?
|
55
|
+
"`" << inspected_arguments.join(", ") << "'"
|
56
56
|
end
|
57
57
|
|
58
58
|
def inspect_argument(to_inspect)
|
59
|
-
if to_inspect.
|
60
|
-
|
59
|
+
if RSpec.rspec_mocks_loaded? && to_inspect.respond_to?(:description)
|
60
|
+
to_inspect.description
|
61
61
|
else
|
62
62
|
to_inspect.inspect
|
63
63
|
end
|
@@ -67,8 +67,9 @@ class Surrogate
|
|
67
67
|
end
|
68
68
|
|
69
69
|
|
70
|
+
# sigh, surely there is a better name!
|
70
71
|
class Handler < Struct.new(:subject, :language_type)
|
71
|
-
attr_accessor :instance
|
72
|
+
attr_accessor :instance
|
72
73
|
|
73
74
|
def message_for(message_category, message_type)
|
74
75
|
MessagesFor.message_for(language_type, message_category, message_type, binding)
|
@@ -79,7 +80,7 @@ class Surrogate
|
|
79
80
|
end
|
80
81
|
|
81
82
|
def message_type
|
82
|
-
|
83
|
+
:default
|
83
84
|
end
|
84
85
|
|
85
86
|
def invocations
|
@@ -105,23 +106,45 @@ class Surrogate
|
|
105
106
|
def failure_message_for_should_not
|
106
107
|
message_for :should_not, message_type
|
107
108
|
end
|
108
|
-
end
|
109
109
|
|
110
|
+
def times(times_invoked)
|
111
|
+
# is there a good way to remove these conditionals?
|
112
|
+
extend (kind_of?(MatchWithArguments) ? MatchNumTimesWith : MatchNumTimes)
|
113
|
+
self.expected_times_invoked = times_invoked
|
114
|
+
self
|
115
|
+
end
|
110
116
|
|
111
|
-
|
112
|
-
|
113
|
-
|
117
|
+
def with(*arguments)
|
118
|
+
extend (kind_of?(MatchNumTimes) ? MatchNumTimesWith : MatchWithArguments)
|
119
|
+
self.expected_arguments = arguments
|
120
|
+
self
|
114
121
|
end
|
122
|
+
end
|
115
123
|
|
116
|
-
attr_accessor :expected_arguments
|
117
124
|
|
118
|
-
|
119
|
-
|
120
|
-
|
125
|
+
module ArgumentComparer
|
126
|
+
def args_match?(actual_arguments)
|
127
|
+
if RSpec.rspec_mocks_loaded?
|
128
|
+
rspec_arg_expectation = ::RSpec::Mocks::ArgumentExpectation.new *expected_arguments
|
129
|
+
rspec_arg_expectation.args_match? *actual_arguments
|
121
130
|
else
|
122
|
-
|
131
|
+
expected_arguments == actual_arguments
|
123
132
|
end
|
124
133
|
end
|
134
|
+
end
|
135
|
+
|
136
|
+
module MatchWithArguments
|
137
|
+
include ArgumentComparer
|
138
|
+
|
139
|
+
attr_accessor :expected_arguments
|
140
|
+
|
141
|
+
def message_type
|
142
|
+
:with
|
143
|
+
end
|
144
|
+
|
145
|
+
def match?
|
146
|
+
invocations.any? { |invocation| args_match? invocation }
|
147
|
+
end
|
125
148
|
|
126
149
|
def actual_invocation
|
127
150
|
return message_for :other, :not_invoked if times_invoked.zero?
|
@@ -132,8 +155,8 @@ class Surrogate
|
|
132
155
|
|
133
156
|
|
134
157
|
module MatchNumTimes
|
135
|
-
def
|
136
|
-
|
158
|
+
def message_type
|
159
|
+
:times
|
137
160
|
end
|
138
161
|
|
139
162
|
attr_accessor :expected_times_invoked
|
@@ -145,14 +168,16 @@ class Surrogate
|
|
145
168
|
|
146
169
|
|
147
170
|
module MatchNumTimesWith
|
148
|
-
|
149
|
-
|
171
|
+
include ArgumentComparer
|
172
|
+
|
173
|
+
def message_type
|
174
|
+
:with_times
|
150
175
|
end
|
151
176
|
|
152
177
|
attr_accessor :expected_times_invoked, :expected_arguments
|
153
178
|
|
154
179
|
def times_invoked_with_expected_args
|
155
|
-
invocations.select { |invocation| invocation
|
180
|
+
invocations.select { |invocation| args_match? invocation }.size
|
156
181
|
end
|
157
182
|
|
158
183
|
def match?
|
@@ -166,75 +191,37 @@ class Surrogate
|
|
166
191
|
end
|
167
192
|
|
168
193
|
|
194
|
+
surrogate_matcher = lambda do |use_case, matcher, morphable=false|
|
195
|
+
if morphable
|
196
|
+
matcher.chain(:times) { |number| use_case.times number }
|
197
|
+
matcher.chain(:with) { |*arguments| use_case.with *arguments }
|
198
|
+
end
|
169
199
|
|
170
|
-
|
171
|
-
|
172
|
-
# have_been_told_to
|
173
|
-
::RSpec::Matchers.define :have_been_told_to do |verb|
|
174
|
-
use_case = Handler.new verb, :verb
|
175
|
-
|
176
|
-
match do |mocked_instance|
|
200
|
+
matcher.match do |mocked_instance|
|
177
201
|
use_case.instance = mocked_instance
|
178
202
|
use_case.match?
|
179
203
|
end
|
180
204
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
end
|
205
|
+
matcher.failure_message_for_should { use_case.failure_message_for_should }
|
206
|
+
matcher.failure_message_for_should_not { use_case.failure_message_for_should_not }
|
207
|
+
end
|
185
208
|
|
186
|
-
chain :with do |*arguments|
|
187
|
-
use_case.extend (use_case.kind_of?(MatchNumTimes) ? MatchNumTimesWith : MatchWithArguments)
|
188
|
-
use_case.expected_arguments = arguments
|
189
|
-
end
|
190
209
|
|
191
|
-
|
192
|
-
|
210
|
+
# have_been_told_to
|
211
|
+
::RSpec::Matchers.define :have_been_told_to do |verb|
|
212
|
+
surrogate_matcher[Handler.new(verb, :verb), self, true]
|
193
213
|
end
|
194
214
|
|
195
215
|
|
196
216
|
# have_been_asked_for_its
|
197
217
|
::RSpec::Matchers.define :have_been_asked_for_its do |noun|
|
198
|
-
|
199
|
-
|
200
|
-
match do |mocked_instance|
|
201
|
-
use_case.instance = mocked_instance
|
202
|
-
use_case.match?
|
203
|
-
end
|
204
|
-
|
205
|
-
chain :times do |number|
|
206
|
-
use_case.extend (use_case.kind_of?(MatchWithArguments) ? MatchNumTimesWith : MatchNumTimes)
|
207
|
-
use_case.expected_times_invoked = number
|
208
|
-
end
|
209
|
-
|
210
|
-
chain :with do |*arguments|
|
211
|
-
use_case.extend (use_case.kind_of?(MatchNumTimes) ? MatchNumTimesWith : MatchWithArguments)
|
212
|
-
use_case.expected_arguments = arguments
|
213
|
-
end
|
214
|
-
|
215
|
-
failure_message_for_should { use_case.failure_message_for_should }
|
216
|
-
failure_message_for_should_not { use_case.failure_message_for_should_not }
|
218
|
+
surrogate_matcher[Handler.new(noun, :noun), self, true]
|
217
219
|
end
|
218
220
|
|
219
221
|
|
220
222
|
# have_been_initialized_with
|
221
223
|
::RSpec::Matchers.define :have_been_initialized_with do |*init_args|
|
222
|
-
|
223
|
-
use_case.extend MatchWithArguments
|
224
|
-
use_case.expected_arguments = init_args
|
225
|
-
|
226
|
-
match do |mocked_instance|
|
227
|
-
use_case.instance = mocked_instance
|
228
|
-
use_case.match?
|
229
|
-
end
|
230
|
-
|
231
|
-
failure_message_for_should do
|
232
|
-
use_case.failure_message_for_should
|
233
|
-
end
|
234
|
-
|
235
|
-
failure_message_for_should_not do
|
236
|
-
use_case.failure_message_for_should_not
|
237
|
-
end
|
224
|
+
surrogate_matcher[Handler.new(:initialize, :verb).with(*init_args), self]
|
238
225
|
end
|
239
226
|
end
|
240
227
|
end
|
data/lib/surrogate/rspec.rb
CHANGED
@@ -1,4 +1,25 @@
|
|
1
1
|
# Maybe I should be my own gem?
|
2
|
+
|
3
|
+
class Surrogate
|
4
|
+
module RSpec
|
5
|
+
class << self
|
6
|
+
def rspec_mocks_loaded?
|
7
|
+
return @mocks_loaded if @alrady_checked_mocks
|
8
|
+
@alrady_checked_mocks = true
|
9
|
+
require 'rspec/mocks' # can't figure out a way to do this lazily
|
10
|
+
@mocks_loaded = true
|
11
|
+
rescue LoadError
|
12
|
+
@mocks_loaded = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def rspec_mocks_loaded=(bool)
|
16
|
+
@alrady_checked_mocks = true
|
17
|
+
@mocks_loaded = bool
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
2
23
|
require 'surrogate'
|
3
24
|
require 'surrogate/rspec/api_method_matchers'
|
4
25
|
require 'surrogate/rspec/substitutability_matchers'
|
data/lib/surrogate/values.rb
CHANGED
@@ -7,8 +7,13 @@ class Surrogate
|
|
7
7
|
# convert raw arguments into a value
|
8
8
|
def self.factory(*args, &block)
|
9
9
|
arg = args.first
|
10
|
+
# if arg.kind_of? Exception
|
11
|
+
# Raiseable.new arg
|
12
|
+
# else
|
13
|
+
# MethodQueue.new args
|
14
|
+
# end
|
10
15
|
if args.size > 1
|
11
|
-
|
16
|
+
ValueQueue.new args
|
12
17
|
elsif arg.kind_of? Exception
|
13
18
|
Raisable.new arg
|
14
19
|
elsif arg.kind_of? Value
|
@@ -45,7 +50,7 @@ class Surrogate
|
|
45
50
|
end
|
46
51
|
|
47
52
|
|
48
|
-
class
|
53
|
+
class ValueQueue < Value
|
49
54
|
QueueEmpty = Class.new StandardError
|
50
55
|
|
51
56
|
def value(hatchling, method_name)
|
data/lib/surrogate/version.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'define' do
|
4
|
+
let(:mocked_class) { Surrogate.endow Class.new }
|
5
|
+
let(:instance) { mocked_class.new }
|
6
|
+
|
4
7
|
describe 'in the block' do
|
5
8
|
it 'is an api method for the class' do
|
6
9
|
pristine_klass = Class.new do
|
@@ -18,248 +21,253 @@ describe 'define' do
|
|
18
21
|
|
19
22
|
|
20
23
|
describe 'out of the block' do
|
21
|
-
let(:mocked_class) { Surrogate.endow Class.new }
|
22
|
-
let(:instance) { mocked_class.new }
|
23
|
-
|
24
24
|
it 'is an api method for the instance' do
|
25
25
|
mocked_class.define(:book) { 'book' }
|
26
26
|
instance.book.should == 'book'
|
27
27
|
end
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
describe 'for verbs' do
|
31
|
-
before { mocked_class.define :wink }
|
32
|
-
|
33
|
-
describe 'will_<api_method>' do
|
34
|
-
it 'overrides the default value for the api method' do
|
35
|
-
mock1 = mocked_class.new
|
36
|
-
mock2 = mocked_class.new
|
37
|
-
mock1.will_wink :quickly
|
38
|
-
mock2.will_wink :slowly
|
39
|
-
mock1.wink.should == :quickly
|
40
|
-
mock2.wink.should == :slowly
|
41
|
-
mock1.wink.should == :quickly
|
42
|
-
end
|
30
|
+
describe 'declaring the behaviour' do
|
43
31
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
32
|
+
describe 'for verbs' do
|
33
|
+
before { mocked_class.define :wink }
|
34
|
+
|
35
|
+
describe 'will_<api_method>' do
|
36
|
+
it 'overrides the default value for the api method' do
|
37
|
+
mock1 = mocked_class.new
|
38
|
+
mock2 = mocked_class.new
|
39
|
+
mock1.will_wink :quickly
|
40
|
+
mock2.will_wink :slowly
|
41
|
+
mock1.wink.should == :quickly
|
42
|
+
mock2.wink.should == :slowly
|
43
|
+
mock1.wink.should == :quickly
|
48
44
|
end
|
49
45
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
46
|
+
it 'returns the object' do
|
47
|
+
instance = mocked_class.new
|
48
|
+
instance.will_wink(:quickly).should equal instance
|
49
|
+
end
|
50
|
+
end
|
55
51
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
mock.wink.should == :slowly
|
62
|
-
expect { mock.wink }.to raise_error Surrogate::UnpreparedMethodError
|
63
|
-
end
|
52
|
+
describe 'will_<api_method> with multiple arguments' do
|
53
|
+
it 'returns the object' do
|
54
|
+
instance = mocked_class.new
|
55
|
+
instance.will_wink(1, 2, 3).should equal instance
|
56
|
+
end
|
64
57
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
mock.connect.should == :default
|
73
|
-
end
|
58
|
+
context 'it creates a queue of things to find then returns to normal behaviour' do
|
59
|
+
specify 'when there is no default block' do
|
60
|
+
mock = mocked_class.new
|
61
|
+
mock.will_wink :quickly, [:slowly]
|
62
|
+
mock.wink.should == :quickly
|
63
|
+
mock.wink.should == [:slowly]
|
64
|
+
expect { mock.wink }.to raise_error Surrogate::UnpreparedMethodError
|
74
65
|
end
|
75
|
-
end
|
76
66
|
|
77
|
-
|
78
|
-
it 'raises the error on method invocation' do
|
67
|
+
specify 'when there is a default block' do
|
79
68
|
mocked_class = Surrogate.endow(Class.new)
|
80
|
-
mocked_class.define :
|
69
|
+
mocked_class.define(:connect) { :default }
|
81
70
|
mock = mocked_class.new
|
82
|
-
|
83
|
-
|
84
|
-
# for single invocation
|
85
|
-
mock.will_connect error
|
86
|
-
expect { mock.connect }.to raise_error StandardError, "some message"
|
87
|
-
|
88
|
-
# for queue
|
89
|
-
mock.will_connect 1, error, 2
|
71
|
+
mock.will_connect 1, 2
|
90
72
|
mock.connect.should == 1
|
91
|
-
expect { mock.connect }.to raise_error StandardError, "some message"
|
92
73
|
mock.connect.should == 2
|
74
|
+
mock.connect.should == :default
|
93
75
|
end
|
94
76
|
end
|
95
77
|
end
|
96
78
|
|
79
|
+
describe 'when an argument is an error' do
|
80
|
+
it 'raises the error on method invocation' do
|
81
|
+
mocked_class = Surrogate.endow(Class.new)
|
82
|
+
mocked_class.define :connect
|
83
|
+
mock = mocked_class.new
|
84
|
+
error = StandardError.new("some message")
|
85
|
+
|
86
|
+
# for single invocation
|
87
|
+
mock.will_connect error
|
88
|
+
expect { mock.connect }.to raise_error StandardError, "some message"
|
89
|
+
|
90
|
+
# for queue
|
91
|
+
mock.will_connect 1, error, 2
|
92
|
+
mock.connect.should == 1
|
93
|
+
expect { mock.connect }.to raise_error StandardError, "some message"
|
94
|
+
mock.connect.should == 2
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
97
98
|
|
98
|
-
describe 'for nouns' do
|
99
|
-
before { mocked_class.define :age }
|
100
99
|
|
101
|
-
|
102
|
-
|
103
|
-
mock1 = mocked_class.new
|
104
|
-
mock2 = mocked_class.new
|
105
|
-
mock1.will_have_age 12
|
106
|
-
mock2.will_have_age 34
|
107
|
-
mock1.age.should == 12
|
108
|
-
mock2.age.should == 34
|
109
|
-
mock1.age.should == 12
|
110
|
-
end
|
100
|
+
describe 'for nouns' do
|
101
|
+
before { mocked_class.define :age }
|
111
102
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
103
|
+
describe 'will_have_<api_method>' do
|
104
|
+
it 'defines will_have_<api_method> which overrides the default block' do
|
105
|
+
mock1 = mocked_class.new
|
106
|
+
mock2 = mocked_class.new
|
107
|
+
mock1.will_have_age 12
|
108
|
+
mock2.will_have_age 34
|
109
|
+
mock1.age.should == 12
|
110
|
+
mock2.age.should == 34
|
111
|
+
mock1.age.should == 12
|
116
112
|
end
|
117
113
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
114
|
+
it 'returns the object' do
|
115
|
+
instance = mocked_class.new
|
116
|
+
instance.will_have_age(123).should equal instance
|
117
|
+
end
|
118
|
+
end
|
123
119
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
mock.age.should == 34
|
130
|
-
expect { mock.age }.to raise_error Surrogate::UnpreparedMethodError
|
131
|
-
end
|
120
|
+
describe 'wil_have_<api_method> with multiple arguments' do
|
121
|
+
it 'returns the object' do
|
122
|
+
instance = mocked_class.new
|
123
|
+
instance.will_have_age(1,2,3).should equal instance
|
124
|
+
end
|
132
125
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
126
|
+
context 'it creates a queue of things to find then returns to normal behaviour' do
|
127
|
+
specify 'when there is no default block' do
|
128
|
+
mock = mocked_class.new
|
129
|
+
mock.will_have_age 12, 34
|
130
|
+
mock.age.should == 12
|
131
|
+
mock.age.should == 34
|
132
|
+
expect { mock.age }.to raise_error Surrogate::UnpreparedMethodError
|
133
|
+
end
|
134
|
+
|
135
|
+
specify 'when there is a default block' do
|
136
|
+
mocked_class = Surrogate.endow(Class.new)
|
137
|
+
mocked_class.define(:name) { 'default' }
|
138
|
+
mock = mocked_class.new
|
139
|
+
mock.will_have_name 'a', 'b'
|
140
|
+
mock.name.should == 'a'
|
141
|
+
mock.name.should == 'b'
|
142
|
+
mock.name.should == 'default'
|
142
143
|
end
|
143
144
|
end
|
144
145
|
end
|
145
146
|
end
|
147
|
+
end
|
146
148
|
|
147
149
|
|
148
150
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
151
|
+
context 'the api method' do
|
152
|
+
it 'takes any number of arguments' do
|
153
|
+
mocked_class.define(:meth) { 1 }
|
154
|
+
mocked_class.new.meth.should == 1
|
155
|
+
mocked_class.new.meth(1).should == 1
|
156
|
+
mocked_class.new.meth(1, 2).should == 1
|
157
|
+
end
|
156
158
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
159
|
+
it 'raises an UnpreparedMethodError when it has no default block' do
|
160
|
+
mocked_class.define :meth
|
161
|
+
expect { mocked_class.new.meth }.to raise_error(Surrogate::UnpreparedMethodError, /meth/)
|
162
|
+
end
|
161
163
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
164
|
+
it 'considers ivars of the same name to be its default when it has no suffix' do
|
165
|
+
mocked_class.define :meth
|
166
|
+
mocked = mocked_class.new
|
167
|
+
mocked.instance_variable_set :@meth, 123
|
168
|
+
mocked.meth.should == 123
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'considers ivars ending in _p to be its default when it ends in a question mark' do
|
172
|
+
mocked_class.define :meth?
|
173
|
+
mocked = mocked_class.new
|
174
|
+
mocked.instance_variable_set :@meth_p, 123
|
175
|
+
mocked.meth?.should == 123
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'reverts to the default block if invoked and having no ivar' do
|
179
|
+
mocked_class.define(:meth) { 123 }
|
180
|
+
mocked = mocked_class.new
|
181
|
+
mocked.instance_variable_get(:@meth).should be_nil
|
182
|
+
mocked.meth.should == 123
|
183
|
+
end
|
168
184
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
mocked.meth.should == 123
|
185
|
+
describe 'initialization' do
|
186
|
+
specify 'api methods can be an initialize method' do
|
187
|
+
mocked_class.define(:initialize) { @abc = 123 }
|
188
|
+
mocked_class.new.instance_variable_get(:@abc).should == 123
|
174
189
|
end
|
175
190
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
191
|
+
specify 'initialize exsits even if error is raised' do
|
192
|
+
mocked_class.define(:initialize) { raise "simulate runtime error" }
|
193
|
+
expect { mocked_class.new }.to raise_error(RuntimeError, /simulate/)
|
194
|
+
expect { mocked_class.new }.to raise_error(RuntimeError, /simulate/)
|
195
|
+
end
|
181
196
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
end
|
197
|
+
specify 'receives args' do
|
198
|
+
mocked_class.define(:initialize) { |num1, num2| @num = num1 + num2 }
|
199
|
+
mocked_class.new(25, 75).instance_variable_get(:@num).should == 100
|
200
|
+
end
|
187
201
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
202
|
+
specify 'even works with inheritance' do
|
203
|
+
superclass = Class.new
|
204
|
+
superclass.send(:define_method, :initialize) { @a = 1 }
|
205
|
+
subclass = Surrogate.endow Class.new superclass
|
206
|
+
subclass.define :abc
|
207
|
+
subclass.new.instance_variable_get(:@a).should == 1
|
208
|
+
end
|
192
209
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
subclass = Surrogate.endow Class.new superclass
|
197
|
-
subclass.define :abc
|
198
|
-
subclass.new.instance_variable_get(:@a).should == 1
|
210
|
+
context 'when not an api method' do
|
211
|
+
it 'respects arity (this is probably 1.9.3 only)' do
|
212
|
+
expect { mocked_class.new 1 }.to raise_error ArgumentError, 'wrong number of arguments(1 for 0)'
|
199
213
|
end
|
200
214
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
describe 'invocations are recorded anyway' do
|
207
|
-
specify 'even when initialize is defined after surrogate block' do
|
208
|
-
klass = Class.new do
|
209
|
-
Surrogate.endow self
|
210
|
-
def initialize(a) @a = a end
|
211
|
-
end
|
212
|
-
klass.new(1).should have_been_initialized_with 1
|
213
|
-
klass.new(1).instance_variable_get(:@a).should == 1
|
215
|
+
describe 'invocations are recorded anyway' do
|
216
|
+
specify 'even when initialize is defined after surrogate block' do
|
217
|
+
klass = Class.new do
|
218
|
+
Surrogate.endow self
|
219
|
+
def initialize(a) @a = a end
|
214
220
|
end
|
221
|
+
klass.new(1).should have_been_initialized_with 1
|
222
|
+
klass.new(1).instance_variable_get(:@a).should == 1
|
223
|
+
end
|
215
224
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
end
|
221
|
-
klass.new(1).should have_been_initialized_with 1
|
222
|
-
klass.new(1).instance_variable_get(:@a).should == 1
|
225
|
+
specify 'even when initialize is defined before surrogate block' do
|
226
|
+
klass = Class.new do
|
227
|
+
def initialize(a) @a = a end
|
228
|
+
Surrogate.endow self
|
223
229
|
end
|
230
|
+
klass.new(1).should have_been_initialized_with 1
|
231
|
+
klass.new(1).instance_variable_get(:@a).should == 1
|
224
232
|
end
|
225
233
|
end
|
226
234
|
end
|
235
|
+
end
|
227
236
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
end
|
234
|
-
|
235
|
-
specify 'arguments passed to the method will be passed to the block' do
|
236
|
-
mocked_class.define(:meth) { |*args| args }
|
237
|
-
instance = mocked_class.new
|
238
|
-
instance.meth(1).should == [1]
|
239
|
-
instance.meth(1, 2).should == [1, 2]
|
240
|
-
end
|
237
|
+
describe 'it takes a block whos return value will be used as the default' do
|
238
|
+
specify 'the block is instance evaled' do
|
239
|
+
mocked_class.define(:meth) { self }
|
240
|
+
instance = mocked_class.new
|
241
|
+
instance.meth.should equal instance
|
241
242
|
end
|
242
243
|
|
243
|
-
|
244
|
-
mocked_class.define(:meth) {
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
mock.meth [1, 2]
|
249
|
-
mock.invocations(:meth).should == [
|
250
|
-
[1],
|
251
|
-
[1, 2],
|
252
|
-
[[1, 2]],
|
253
|
-
]
|
244
|
+
specify 'arguments passed to the method will be passed to the block' do
|
245
|
+
mocked_class.define(:meth) { |*args| args }
|
246
|
+
instance = mocked_class.new
|
247
|
+
instance.meth(1).should == [1]
|
248
|
+
instance.meth(1, 2).should == [1, 2]
|
254
249
|
end
|
250
|
+
end
|
255
251
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
252
|
+
it 'remembers what it was invoked with' do
|
253
|
+
mocked_class.define(:meth) { nil }
|
254
|
+
mock = mocked_class.new
|
255
|
+
mock.meth 1
|
256
|
+
mock.meth 1, 2
|
257
|
+
mock.meth [1, 2]
|
258
|
+
mock.invocations(:meth).should == [
|
259
|
+
[1],
|
260
|
+
[1, 2],
|
261
|
+
[[1, 2]],
|
262
|
+
]
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'raises an error if asked about invocations for api methods it does not know' do
|
266
|
+
mocked_class.define :meth1
|
267
|
+
mocked_class.define :meth2
|
268
|
+
mock = mocked_class.new
|
269
|
+
expect { mock.invocations(:meth1) }.to_not raise_error
|
270
|
+
expect { mock.invocations(:meth3) }.to raise_error Surrogate::UnknownMethod, /doesn't know "meth3", only knows "initialize", "meth1", "meth2"/
|
263
271
|
end
|
264
272
|
end
|
265
273
|
|
@@ -60,12 +60,17 @@ describe 'RSpec matchers', 'have_been_asked_for_its' do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
describe 'integration with rspec argument_matchers' do
|
63
|
-
it 'works with
|
63
|
+
it 'works with rspec matchers' do
|
64
64
|
instance.should_not have_been_asked_for_its(:size).with(no_args)
|
65
65
|
instance.size(1)
|
66
66
|
instance.should_not have_been_asked_for_its(:size).with(no_args)
|
67
67
|
instance.size
|
68
68
|
instance.should have_been_asked_for_its(:size).with(no_args)
|
69
|
+
|
70
|
+
instance.should_not have_been_asked_for_its(:size).with(hash_including all: true)
|
71
|
+
instance.size any: false, all: true
|
72
|
+
instance.should have_been_asked_for_its(:size).with(hash_including all: true)
|
73
|
+
instance.should_not have_been_asked_for_its(:size).with(hash_including all: false)
|
69
74
|
end
|
70
75
|
end
|
71
76
|
end
|
data/spec/rspec/messages_spec.rb
CHANGED
@@ -11,8 +11,9 @@ describe messages_for, 'argument inspection' do
|
|
11
11
|
messages_for.inspect_argument([/a/]).should == "[/a/]"
|
12
12
|
end
|
13
13
|
|
14
|
-
it 'inspects
|
15
|
-
messages_for.inspect_argument(no_args).should == '
|
14
|
+
it 'inspects rspec matchers' do
|
15
|
+
messages_for.inspect_argument(no_args).should == 'no args'
|
16
|
+
messages_for.inspect_argument(hash_including abc: 123).should == 'hash_including(:abc=>123)'
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
@@ -23,11 +24,11 @@ describe messages_for, 'argument inspection' do
|
|
23
24
|
end
|
24
25
|
|
25
26
|
it "joins arguments with commas" do
|
26
|
-
messages_for.inspect_arguments(['x', no_args]).should == "`\"x\",
|
27
|
+
messages_for.inspect_arguments(['x', no_args]).should == "`\"x\", no args'"
|
27
28
|
end
|
28
29
|
|
29
30
|
it 'returns no_args when the array is empty' do
|
30
|
-
messages_for.inspect_arguments([]).should == "`
|
31
|
+
messages_for.inspect_arguments([]).should == "`no args'"
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'integration with rspec-mocks' do
|
4
|
+
|
5
|
+
let(:mp3) { Surrogate.endow(Class.new).define(:play) { }.new }
|
6
|
+
|
7
|
+
it 'knows that rspec-mocks is loaded' do
|
8
|
+
Surrogate::RSpec.rspec_mocks_loaded?.should equal true
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'when rspec-mocks is loaded' do
|
12
|
+
it 'uses their matchers' do
|
13
|
+
mp3.play "Emily Wells"
|
14
|
+
mp3.should have_been_told_to(:play).with(/emily/i)
|
15
|
+
mp3.should_not have_been_told_to(:play).with(/emily/)
|
16
|
+
|
17
|
+
mp3.play /regex/
|
18
|
+
mp3.should have_been_told_to(:play).with(/regex/)
|
19
|
+
mp3.should_not have_been_told_to(:play).with(/xeger/)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when rspec-mocks is not loaded' do
|
24
|
+
it 'does straight #== comparisons on each argument' do
|
25
|
+
begin
|
26
|
+
Surrogate::RSpec.rspec_mocks_loaded = false
|
27
|
+
|
28
|
+
mp3.play "Emily Wells"
|
29
|
+
mp3.should_not have_been_told_to(:play).with(/emily/i)
|
30
|
+
|
31
|
+
mp3.play /regex/
|
32
|
+
mp3.should have_been_told_to(:play).with(/regex/)
|
33
|
+
mp3.should_not have_been_told_to(:play).with(/xeger/)
|
34
|
+
rescue Exception
|
35
|
+
Surrogate::RSpec.rspec_mocks_loaded = true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: surrogate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.2
|
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-05-
|
12
|
+
date: 2012-05-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70344532950860 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: 2.8.0
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70344532950860
|
25
25
|
description: Framework to aid in handrolling mock/spy objects.
|
26
26
|
email:
|
27
27
|
- josh.cheek@gmail.com
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- spec/rspec/have_been_initialized_with_spec.rb
|
52
52
|
- spec/rspec/have_been_told_to_spec.rb
|
53
53
|
- spec/rspec/messages_spec.rb
|
54
|
+
- spec/rspec/rspec_mocks_integration_spec.rb
|
54
55
|
- spec/rspec/substitute_for_spec.rb
|
55
56
|
- spec/spec_helper.rb
|
56
57
|
- spec/unit/api_comparer_spec.rb
|
@@ -86,6 +87,7 @@ test_files:
|
|
86
87
|
- spec/rspec/have_been_initialized_with_spec.rb
|
87
88
|
- spec/rspec/have_been_told_to_spec.rb
|
88
89
|
- spec/rspec/messages_spec.rb
|
90
|
+
- spec/rspec/rspec_mocks_integration_spec.rb
|
89
91
|
- spec/rspec/substitute_for_spec.rb
|
90
92
|
- spec/spec_helper.rb
|
91
93
|
- spec/unit/api_comparer_spec.rb
|