surrogate 0.6.3 → 0.6.4
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/Changelog.md +5 -0
- data/Readme.md +19 -5
- data/Readme.md.mountain_berry_fields +21 -5
- data/lib/surrogate/endower.rb +22 -1
- data/lib/surrogate/hatchling.rb +7 -1
- data/lib/surrogate/rspec.rb +5 -1
- data/lib/surrogate/rspec/invocation_matcher.rb +1 -9
- data/lib/surrogate/rspec/substitution_matcher.rb +67 -0
- data/lib/surrogate/version.rb +1 -1
- data/spec/defining_api_methods_spec.rb +67 -0
- data/spec/rspec/verb_matcher_spec.rb +9 -1
- metadata +13 -13
- data/lib/surrogate/rspec/substitute_for.rb +0 -61
data/Changelog.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
### 0.6.4
|
2
|
+
|
3
|
+
* Remove explicit reference to RSpec::Matchers so that surrogate/rspec doesn't depend on rspec-expectations
|
4
|
+
* Add `define_setter`, `define_getter`, `define_accessor` which mimic Ruby's `attr_*` methods, but are also recorded / overridable.
|
5
|
+
|
1
6
|
### 0.6.3
|
2
7
|
|
3
8
|
* Allow mass initialization of instances with `MockClass.factory key: value`
|
data/Readme.md
CHANGED
@@ -9,7 +9,7 @@ Handrolling mocks is the best, but involves more overhead than necessary, and us
|
|
9
9
|
error messages. Surrogate addresses this by endowing your objects with common things that most mocks need.
|
10
10
|
Currently it is only integrated with RSpec.
|
11
11
|
|
12
|
-
This codebase should be considered
|
12
|
+
This codebase should be considered volatile until 1.0 release. The outer interface should be
|
13
13
|
fairly stable, with each 0.a.b version having backwards compatibility for any changes to b (ie
|
14
14
|
only refactorings and new features), and possible interface changes (though probably minimal)
|
15
15
|
for changes to a. Depending on the internals of the code (anything not shown in the readme) is
|
@@ -20,8 +20,7 @@ that I introduce.
|
|
20
20
|
Features
|
21
21
|
========
|
22
22
|
|
23
|
-
*
|
24
|
-
* Can compare signatures of mocks to signatures of the class being mocked
|
23
|
+
* Mock does not diverge from real class because compares signatures of mocks to signatures of the real class.
|
25
24
|
* Support default values
|
26
25
|
* Easily override values
|
27
26
|
* RSpec matchers for asserting what happend (what was invoked, with what args, how many times)
|
@@ -64,6 +63,21 @@ end
|
|
64
63
|
MockClient.new.request # => ["result1", "result2"]
|
65
64
|
```
|
66
65
|
|
66
|
+
Ruby's `attr_accessor`, `attr_reader`, `attr_writer` are mimicked with `define_accessor`, `define_reader`, `define_writer`.
|
67
|
+
You can pass a block to `define_accessor` and `define_reader` if you would like to give it a default_value.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
class MockClient
|
71
|
+
Surrogate.endow self
|
72
|
+
define_accessor :default_url
|
73
|
+
end
|
74
|
+
|
75
|
+
client = MockClient.new
|
76
|
+
client.default_url # => nil
|
77
|
+
client.default_url = 'http://whatever.com'
|
78
|
+
client.default_url # => "http://whatever.com"
|
79
|
+
```
|
80
|
+
|
67
81
|
If you expect **arguments**, your block should receive them.
|
68
82
|
This prevents the issues with dynamic mocks where arguments and parameters can diverge.
|
69
83
|
It may seem like more work when you have to write the arguments explicitly, but you only
|
@@ -197,13 +211,13 @@ user.name # => "Jim"
|
|
197
211
|
user.age # => 26
|
198
212
|
|
199
213
|
# use a different name
|
200
|
-
class
|
214
|
+
class MockUserWithRenamedFactory
|
201
215
|
Surrogate.endow self, factory: :construct
|
202
216
|
define(:name) { 'Samantha' }
|
203
217
|
define(:age) { 83 }
|
204
218
|
end
|
205
219
|
|
206
|
-
user =
|
220
|
+
user = MockUserWithRenamedFactory.construct name: 'Milla'
|
207
221
|
user.name # => "Milla"
|
208
222
|
user.age # => 83
|
209
223
|
```
|
@@ -21,7 +21,7 @@ Handrolling mocks is the best, but involves more overhead than necessary, and us
|
|
21
21
|
error messages. Surrogate addresses this by endowing your objects with common things that most mocks need.
|
22
22
|
Currently it is only integrated with RSpec.
|
23
23
|
|
24
|
-
This codebase should be considered
|
24
|
+
This codebase should be considered volatile until 1.0 release. The outer interface should be
|
25
25
|
fairly stable, with each 0.a.b version having backwards compatibility for any changes to b (ie
|
26
26
|
only refactorings and new features), and possible interface changes (though probably minimal)
|
27
27
|
for changes to a. Depending on the internals of the code (anything not shown in the readme) is
|
@@ -32,8 +32,7 @@ that I introduce.
|
|
32
32
|
Features
|
33
33
|
========
|
34
34
|
|
35
|
-
*
|
36
|
-
* Can compare signatures of mocks to signatures of the class being mocked
|
35
|
+
* Mock does not diverge from real class because compares signatures of mocks to signatures of the real class.
|
37
36
|
* Support default values
|
38
37
|
* Easily override values
|
39
38
|
* RSpec matchers for asserting what happend (what was invoked, with what args, how many times)
|
@@ -82,6 +81,23 @@ MockClient.new.request # => ["result1", "result2"]
|
|
82
81
|
<% end %>
|
83
82
|
```
|
84
83
|
|
84
|
+
Ruby's `attr_accessor`, `attr_reader`, `attr_writer` are mimicked with `define_accessor`, `define_reader`, `define_writer`.
|
85
|
+
You can pass a block to `define_accessor` and `define_reader` if you would like to give it a default_value.
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
<% test 'attr_* replacements', with: :magic_comments do %>
|
89
|
+
class MockClient
|
90
|
+
Surrogate.endow self
|
91
|
+
define_accessor :default_url
|
92
|
+
end
|
93
|
+
|
94
|
+
client = MockClient.new
|
95
|
+
client.default_url # => nil
|
96
|
+
client.default_url = 'http://whatever.com'
|
97
|
+
client.default_url # => "http://whatever.com"
|
98
|
+
<% end %>
|
99
|
+
```
|
100
|
+
|
85
101
|
If you expect **arguments**, your block should receive them.
|
86
102
|
This prevents the issues with dynamic mocks where arguments and parameters can diverge.
|
87
103
|
It may seem like more work when you have to write the arguments explicitly, but you only
|
@@ -230,13 +246,13 @@ user.name # => "Jim"
|
|
230
246
|
user.age # => 26
|
231
247
|
|
232
248
|
# use a different name
|
233
|
-
class
|
249
|
+
class MockUserWithRenamedFactory
|
234
250
|
Surrogate.endow self, factory: :construct
|
235
251
|
define(:name) { 'Samantha' }
|
236
252
|
define(:age) { 83 }
|
237
253
|
end
|
238
254
|
|
239
|
-
user =
|
255
|
+
user = MockUserWithRenamedFactory.construct name: 'Milla'
|
240
256
|
user.name # => "Milla"
|
241
257
|
user.age # => 83
|
242
258
|
<% end %>
|
data/lib/surrogate/endower.rb
CHANGED
@@ -78,7 +78,28 @@ class Surrogate
|
|
78
78
|
|
79
79
|
def enable_defining_methods(klass)
|
80
80
|
def klass.define(method_name, options={}, &block)
|
81
|
-
@hatchery.define method_name, options, &block
|
81
|
+
@hatchery.define method_name.to_sym, options, &block
|
82
|
+
end
|
83
|
+
|
84
|
+
def klass.define_reader(*method_names, &block)
|
85
|
+
block ||= lambda {}
|
86
|
+
method_names.each { |method_name| define method_name, &block }
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def klass.define_writer(*method_names)
|
91
|
+
method_names.each do |method_name|
|
92
|
+
define "#{method_name}=" do |value|
|
93
|
+
instance_variable_set("@#{method_name}", value)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
self
|
97
|
+
end
|
98
|
+
|
99
|
+
def klass.define_accessor(*method_names, &block)
|
100
|
+
define_reader(*method_names, &block)
|
101
|
+
define_writer(*method_names)
|
102
|
+
self
|
82
103
|
end
|
83
104
|
end
|
84
105
|
|
data/lib/surrogate/hatchling.rb
CHANGED
@@ -19,7 +19,9 @@ class Surrogate
|
|
19
19
|
def invoke_method(method_name, args, &block)
|
20
20
|
invocation = Invocation.new(args, &block)
|
21
21
|
invoked_methods[method_name] << invocation
|
22
|
-
|
22
|
+
if setter?(method_name) || !has_ivar?(method_name)
|
23
|
+
return get_default method_name, invocation, &block
|
24
|
+
end
|
23
25
|
interfaces_must_match! method_name, args
|
24
26
|
Value.factory(get_ivar method_name).value(method_name)
|
25
27
|
end
|
@@ -35,6 +37,10 @@ class Surrogate
|
|
35
37
|
|
36
38
|
private
|
37
39
|
|
40
|
+
def setter?(method_name)
|
41
|
+
method_name.match(/\w=$/)
|
42
|
+
end
|
43
|
+
|
38
44
|
def invoked_methods
|
39
45
|
@invoked_methods ||= Hash.new do |hash, method_name|
|
40
46
|
must_know method_name
|
data/lib/surrogate/rspec.rb
CHANGED
@@ -23,6 +23,10 @@ class Surrogate
|
|
23
23
|
end
|
24
24
|
|
25
25
|
module Matchers
|
26
|
+
def substitute_for(original_class, options={})
|
27
|
+
SubstitutionMatcher.new original_class, options
|
28
|
+
end
|
29
|
+
|
26
30
|
def have_been_told_to(method_name)
|
27
31
|
VerbMatcher.new method_name
|
28
32
|
end
|
@@ -65,7 +69,7 @@ class Surrogate
|
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
68
|
-
require 'surrogate/rspec/
|
72
|
+
require 'surrogate/rspec/substitution_matcher'
|
69
73
|
require 'surrogate/rspec/predicate_matcher'
|
70
74
|
require 'surrogate/rspec/noun_matcher'
|
71
75
|
require 'surrogate/rspec/initialization_matcher'
|
@@ -9,7 +9,7 @@ class Surrogate
|
|
9
9
|
attr_accessor :times_predicate, :with_filter, :surrogate, :method_name
|
10
10
|
|
11
11
|
def initialize(method_name)
|
12
|
-
self.method_name = method_name
|
12
|
+
self.method_name = method_name.to_sym
|
13
13
|
self.times_predicate = TimesPredicate.new
|
14
14
|
self.with_filter = WithFilter.new
|
15
15
|
end
|
@@ -27,14 +27,6 @@ class Surrogate
|
|
27
27
|
SurrogateInstanceReflector.new(surrogate).invocations(method_name)
|
28
28
|
end
|
29
29
|
|
30
|
-
def failure_message_for_should
|
31
|
-
raise "THIS METHOD SHOULD HAVE BEEN OVERRIDDEN"
|
32
|
-
end
|
33
|
-
|
34
|
-
def failure_message_for_should_not
|
35
|
-
raise "THIS METHOD SHOULD HAVE BEEN OVERRIDDEN"
|
36
|
-
end
|
37
|
-
|
38
30
|
def times(times_invoked)
|
39
31
|
@times_predicate = TimesPredicate.new(times_invoked, :==)
|
40
32
|
self
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Surrogate
|
2
|
+
module RSpec
|
3
|
+
class SubstitutionMatcher
|
4
|
+
def initialize(original_class, options={})
|
5
|
+
@original_class = original_class
|
6
|
+
@comparison = nil
|
7
|
+
@subset_only = options.fetch :subset, false
|
8
|
+
@types = options.fetch :types, true
|
9
|
+
@names = options.fetch :names, false
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(surrogate)
|
13
|
+
@comparison = ApiComparer.new(surrogate, @original_class).compare
|
14
|
+
comparing_fields(@comparison, @subset_only, @types, @names).values.inject(:+).empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def failure_message_for_should
|
19
|
+
extra_instance_methods = @comparison[:instance][:not_on_actual ].to_a # these come in as sets
|
20
|
+
extra_class_methods = @comparison[:class ][:not_on_actual ].to_a
|
21
|
+
missing_instance_methods = @comparison[:instance][:not_on_surrogate].to_a
|
22
|
+
missing_class_methods = @comparison[:class ][:not_on_surrogate].to_a
|
23
|
+
instance_type_mismatch = @comparison[:instance][:types ]
|
24
|
+
class_type_mismatch = @comparison[:class ][:types ]
|
25
|
+
instance_name_mismatch = @comparison[:instance][:names ]
|
26
|
+
class_name_mismatch = @comparison[:class ][:names ]
|
27
|
+
|
28
|
+
|
29
|
+
differences = []
|
30
|
+
differences << "has extra instance methods: #{extra_instance_methods.inspect}" if extra_instance_methods.any?
|
31
|
+
differences << "has extra class methods: #{extra_class_methods.inspect}" if extra_class_methods.any?
|
32
|
+
differences << "is missing instance methods: #{missing_instance_methods}" if !@subset_only && missing_instance_methods.any?
|
33
|
+
differences << "is missing class methods: #{missing_class_methods}" if !@subset_only && missing_class_methods.any?
|
34
|
+
|
35
|
+
if @types # this conditional is not tested, nor are these error messages
|
36
|
+
instance_type_mismatch.each { |name, types| differences << "##{name} had types #{types.inspect}" }
|
37
|
+
class_type_mismatch.each { |name, types| differences << ".#{name} had types #{types.inspect}" }
|
38
|
+
end
|
39
|
+
|
40
|
+
if @names # this conditional is not tested, nor are these error messages
|
41
|
+
instance_name_mismatch.each { |method_name, param_names| differences << "##{method_name} had parameter names #{param_names.inspect}" }
|
42
|
+
class_type_mismatch.each { |method_name, param_names| differences << ".#{method_name} had parameter names #{param_names.inspect}" }
|
43
|
+
end
|
44
|
+
"Was not substitutable because surrogate " << differences.join("\n")
|
45
|
+
end
|
46
|
+
|
47
|
+
def failure_message_for_should_not
|
48
|
+
"Should not have been substitute, but was"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def comparing_fields(comparison, subset_only, types, names)
|
54
|
+
fields = {}
|
55
|
+
fields[:instance_not_on_actual ] = comparison[:instance][:not_on_actual]
|
56
|
+
fields[:class_not_on_actual ] = comparison[:class ][:not_on_actual]
|
57
|
+
fields[:instance_not_on_surrogate] = comparison[:instance][:not_on_surrogate] unless @subset_only
|
58
|
+
fields[:class_not_on_surrogate ] = comparison[:class ][:not_on_surrogate] unless @subset_only
|
59
|
+
fields[:instance_types ] = comparison[:instance][:types] if @types
|
60
|
+
fields[:class_types ] = comparison[:class ][:types] if @types
|
61
|
+
fields[:instance_names ] = comparison[:instance][:names] if @names
|
62
|
+
fields[:class_names ] = comparison[:class ][:names] if @names
|
63
|
+
fields
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/surrogate/version.rb
CHANGED
@@ -47,6 +47,12 @@ describe 'define' do
|
|
47
47
|
it "returns the value that the method returns" do
|
48
48
|
mocked_class.define(:meth) { 1234 }.new.meth.should == 1234
|
49
49
|
end
|
50
|
+
|
51
|
+
it "always invokes the block when the method is a setter (ends in '=')" do
|
52
|
+
a = 0
|
53
|
+
mocked_class.define(:meth=) { |value| a = value }.new.meth = 1234
|
54
|
+
a.should == 1234
|
55
|
+
end
|
50
56
|
end
|
51
57
|
|
52
58
|
describe 'declaring the behaviour' do
|
@@ -170,6 +176,13 @@ describe 'define' do
|
|
170
176
|
expect { mocked_class.new.meth 1, 2 }.to raise_error ArgumentError, /2 for 1/
|
171
177
|
end
|
172
178
|
|
179
|
+
it "can be defined with symbols or strings" do
|
180
|
+
mocked_class.define("meth") { |a| a }
|
181
|
+
mocked_class.define(:other_meth) { |a| a * 2 }
|
182
|
+
mocked_class.new.meth(1).should == 1
|
183
|
+
mocked_class.new.other_meth(1).should == 2
|
184
|
+
end
|
185
|
+
|
173
186
|
it 'raises an UnpreparedMethodError when it has no default block' do
|
174
187
|
mocked_class.define :meth
|
175
188
|
expect { instance.meth }.to raise_error Surrogate::UnpreparedMethodError, /meth/
|
@@ -316,3 +329,57 @@ describe 'define' do
|
|
316
329
|
end
|
317
330
|
end
|
318
331
|
end
|
332
|
+
|
333
|
+
describe 'defining accessors' do
|
334
|
+
specify '#define_reader is an api method for attr_reader' do
|
335
|
+
instance = Surrogate.endow(Class.new).define_reader(:eric, :josh).new
|
336
|
+
instance.was_not asked_for :eric
|
337
|
+
instance.eric.should == nil
|
338
|
+
instance.was asked_for :eric
|
339
|
+
instance.josh.should be_nil
|
340
|
+
end
|
341
|
+
|
342
|
+
specify '#define_reader can be defined with a value' do
|
343
|
+
instance = Surrogate.endow(Class.new).define_reader(:eric, :josh) { "is awesome" }.new
|
344
|
+
instance.eric.should == "is awesome"
|
345
|
+
instance.will_have_eric("less awesome")
|
346
|
+
instance.josh.should == "is awesome"
|
347
|
+
end
|
348
|
+
|
349
|
+
specify '#define_writer is an api method for attr_writer' do
|
350
|
+
instance = Surrogate.endow(Class.new).define_writer(:eric, :josh).new
|
351
|
+
instance.was_not told_to :eric=
|
352
|
+
instance.eric = "was here"
|
353
|
+
instance.instance_variable_get("@eric").should == "was here"
|
354
|
+
instance.was told_to :eric=
|
355
|
+
instance.josh = "was here"
|
356
|
+
end
|
357
|
+
|
358
|
+
specify '#define_accessor is an api method for attr_accessor' do
|
359
|
+
instance = Surrogate.endow(Class.new).define_accessor(:eric, :josh).new
|
360
|
+
instance.was_not asked_for :eric
|
361
|
+
instance.eric.should == nil
|
362
|
+
instance.eric = "was here"
|
363
|
+
instance.eric.should == "was here"
|
364
|
+
instance.was asked_for :eric
|
365
|
+
instance.josh = "was also here"
|
366
|
+
instance.josh.should == "was also here"
|
367
|
+
end
|
368
|
+
|
369
|
+
specify '#define_accessor can take a block for the reader' do
|
370
|
+
instance = Surrogate.endow(Class.new).define_accessor(:eric) {"was here"}.new
|
371
|
+
instance.was_not asked_for :eric
|
372
|
+
instance.eric.should == "was here"
|
373
|
+
instance.was asked_for :eric
|
374
|
+
instance.eric = "was somewhere else"
|
375
|
+
instance.eric.should == "was somewhere else"
|
376
|
+
end
|
377
|
+
|
378
|
+
specify 'they can be defined on the class or the instance' do
|
379
|
+
klass = Surrogate.endow(Class.new) { define_accessor(:i_belong_with_you) { 'you belong with me' } }
|
380
|
+
klass.i_belong_with_you.should == 'you belong with me'
|
381
|
+
klass.i_belong_with_you = "you're my sweetheart"
|
382
|
+
klass.i_belong_with_you.should == "you're my sweetheart"
|
383
|
+
klass.was told_to :i_belong_with_you
|
384
|
+
end
|
385
|
+
end
|
@@ -23,7 +23,7 @@ shared_examples_for 'a verb matcher' do
|
|
23
23
|
describe 'default use case' do
|
24
24
|
before { mocked_class.define :kick, default: [] }
|
25
25
|
|
26
|
-
example 'passes if has been invoked at least once' do
|
26
|
+
example 'passes with a symbol if has been invoked at least once' do
|
27
27
|
did_not :kick
|
28
28
|
instance.kick
|
29
29
|
did :kick
|
@@ -31,6 +31,14 @@ shared_examples_for 'a verb matcher' do
|
|
31
31
|
did :kick
|
32
32
|
end
|
33
33
|
|
34
|
+
example 'passes with a string if invoked at least once' do
|
35
|
+
did_not "kick"
|
36
|
+
instance.kick
|
37
|
+
did "kick"
|
38
|
+
instance.kick
|
39
|
+
did "kick"
|
40
|
+
end
|
41
|
+
|
34
42
|
example 'failure message for should' do
|
35
43
|
expect { did :kick }.to \
|
36
44
|
raise_error(RSpec::Expectations::ExpectationNotMetError, /was never told to kick/)
|
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.6.
|
4
|
+
version: 0.6.4
|
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-
|
12
|
+
date: 2012-12-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70270737052860 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70270737052860
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70270737052300 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '2.4'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70270737052300
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: mountain_berry_fields
|
38
|
-
requirement: &
|
38
|
+
requirement: &70270737051720 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 1.0.3
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70270737051720
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: mountain_berry_fields-rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70270737051180 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.0.3
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70270737051180
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: mountain_berry_fields-magic_comments
|
60
|
-
requirement: &
|
60
|
+
requirement: &70270737050560 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: 1.0.1
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70270737050560
|
69
69
|
description: Framework to aid in handrolling mock/spy objects.
|
70
70
|
email:
|
71
71
|
- josh.cheek@gmail.com
|
@@ -99,7 +99,7 @@ files:
|
|
99
99
|
- lib/surrogate/rspec/invocation_matcher.rb
|
100
100
|
- lib/surrogate/rspec/noun_matcher.rb
|
101
101
|
- lib/surrogate/rspec/predicate_matcher.rb
|
102
|
-
- lib/surrogate/rspec/
|
102
|
+
- lib/surrogate/rspec/substitution_matcher.rb
|
103
103
|
- lib/surrogate/rspec/times_predicate.rb
|
104
104
|
- lib/surrogate/rspec/verb_matcher.rb
|
105
105
|
- lib/surrogate/rspec/with_filter.rb
|
@@ -1,61 +0,0 @@
|
|
1
|
-
class Surrogate
|
2
|
-
# turn this into a real class
|
3
|
-
::RSpec::Matchers.define :substitute_for do |original_class, options={}|
|
4
|
-
|
5
|
-
comparison = nil
|
6
|
-
subset_only = options[:subset]
|
7
|
-
types = options.fetch :types, true
|
8
|
-
names = options.fetch :names, false
|
9
|
-
|
10
|
-
def comparing_fields(comparison, subset_only, types, names)
|
11
|
-
fields = {}
|
12
|
-
fields[:instance_not_on_actual ] = comparison[:instance][:not_on_actual]
|
13
|
-
fields[:class_not_on_actual ] = comparison[:class ][:not_on_actual]
|
14
|
-
fields[:instance_not_on_surrogate] = comparison[:instance][:not_on_surrogate] unless subset_only
|
15
|
-
fields[:class_not_on_surrogate ] = comparison[:class ][:not_on_surrogate] unless subset_only
|
16
|
-
fields[:instance_types ] = comparison[:instance][:types] if types
|
17
|
-
fields[:class_types ] = comparison[:class ][:types] if types
|
18
|
-
fields[:instance_names ] = comparison[:instance][:names] if names
|
19
|
-
fields[:class_names ] = comparison[:class ][:names] if names
|
20
|
-
fields
|
21
|
-
end
|
22
|
-
|
23
|
-
match do |mocked_class|
|
24
|
-
comparison = ApiComparer.new(mocked_class, original_class).compare
|
25
|
-
comparing_fields(comparison, subset_only, types, names).values.inject(:+).empty?
|
26
|
-
end
|
27
|
-
|
28
|
-
failure_message_for_should do
|
29
|
-
extra_instance_methods = comparison[:instance][:not_on_actual ].to_a # these come in as sets
|
30
|
-
extra_class_methods = comparison[:class ][:not_on_actual ].to_a
|
31
|
-
missing_instance_methods = comparison[:instance][:not_on_surrogate].to_a
|
32
|
-
missing_class_methods = comparison[:class ][:not_on_surrogate].to_a
|
33
|
-
instance_type_mismatch = comparison[:instance][:types ]
|
34
|
-
class_type_mismatch = comparison[:class ][:types ]
|
35
|
-
instance_name_mismatch = comparison[:instance][:names ]
|
36
|
-
class_name_mismatch = comparison[:class ][:names ]
|
37
|
-
|
38
|
-
|
39
|
-
differences = []
|
40
|
-
differences << "has extra instance methods: #{extra_instance_methods.inspect}" if extra_instance_methods.any?
|
41
|
-
differences << "has extra class methods: #{extra_class_methods.inspect}" if extra_class_methods.any?
|
42
|
-
differences << "is missing instance methods: #{missing_instance_methods}" if !subset_only && missing_instance_methods.any?
|
43
|
-
differences << "is missing class methods: #{missing_class_methods}" if !subset_only && missing_class_methods.any?
|
44
|
-
|
45
|
-
if types # this conditional is not tested, nor are these error messages
|
46
|
-
instance_type_mismatch.each { |name, types| differences << "##{name} had types #{types.inspect}" }
|
47
|
-
class_type_mismatch.each { |name, types| differences << ".#{name} had types #{types.inspect}" }
|
48
|
-
end
|
49
|
-
|
50
|
-
if names # this conditional is not tested, nor are these error messages
|
51
|
-
instance_name_mismatch.each { |method_name, param_names| differences << "##{method_name} had parameter names #{param_names.inspect}" }
|
52
|
-
class_type_mismatch.each { |method_name, param_names| differences << ".#{method_name} had parameter names #{param_names.inspect}" }
|
53
|
-
end
|
54
|
-
"Was not substitutable because surrogate " << differences.join("\n")
|
55
|
-
end
|
56
|
-
|
57
|
-
failure_message_for_should_not do
|
58
|
-
"Should not have been substitute, but was"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|