rspec-mocks 2.12.0 → 2.12.1
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 +11 -1
- data/README.md +3 -3
- data/lib/rspec/mocks/any_instance/chain.rb +2 -1
- data/lib/rspec/mocks/any_instance/recorder.rb +10 -10
- data/lib/rspec/mocks/instance_method_stasher.rb +18 -3
- data/lib/rspec/mocks/method_double.rb +22 -0
- data/lib/rspec/mocks/mutate_const.rb +8 -2
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/and_call_original_spec.rb +41 -0
- data/spec/rspec/mocks/mutate_const_spec.rb +8 -0
- data/spec/rspec/mocks/partial_mock_spec.rb +1 -1
- data/spec/rspec/mocks/stash_spec.rb +27 -0
- metadata +72 -62
data/Changelog.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
|
+
### 2.12.1 / 2012-12-21
|
2
|
+
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.12.0...v2.12.1)
|
3
|
+
|
4
|
+
Bug fixes
|
5
|
+
|
6
|
+
* Fix `any_instance` to support `and_call_original`.
|
7
|
+
(Myron Marston)
|
8
|
+
* Properly restore stubbed aliased methods on rubies
|
9
|
+
that report the incorrect owner (Myron Marston and Andy Lindeman).
|
10
|
+
|
1
11
|
### 2.12.0 / 2012-11-12
|
2
|
-
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.3...
|
12
|
+
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.3...v2.12.0)
|
3
13
|
|
4
14
|
Enhancements
|
5
15
|
|
data/README.md
CHANGED
@@ -271,10 +271,10 @@ While this is a good thing when you really need it, you probably don't really
|
|
271
271
|
need it! Take care to specify only the things that matter to the behavior of
|
272
272
|
your code.
|
273
273
|
|
274
|
-
## Stubbing Constants
|
274
|
+
## Stubbing and Hiding Constants
|
275
275
|
|
276
|
-
See the [
|
277
|
-
README](https://github.com/rspec/rspec-mocks/blob/master/features/
|
276
|
+
See the [mutating constants
|
277
|
+
README](https://github.com/rspec/rspec-mocks/blob/master/features/mutating_constants/README.md)
|
278
278
|
for info on this feature.
|
279
279
|
|
280
280
|
## Use `before(:each)`, not `before(:all)`
|
@@ -13,7 +13,7 @@ module RSpec
|
|
13
13
|
# @see RSpec::Mocks::MessageExpectation#$1
|
14
14
|
#
|
15
15
|
def record(method_name)
|
16
|
-
class_eval(<<-EOM, __FILE__, __LINE__)
|
16
|
+
class_eval(<<-EOM, __FILE__, __LINE__ + 1)
|
17
17
|
def #{method_name}(*args, &block)
|
18
18
|
record(:#{method_name}, *args, &block)
|
19
19
|
end
|
@@ -25,6 +25,7 @@ module RSpec
|
|
25
25
|
record :and_raise
|
26
26
|
record :and_throw
|
27
27
|
record :and_yield
|
28
|
+
record :and_call_original
|
28
29
|
record :with
|
29
30
|
record :once
|
30
31
|
record :twice
|
@@ -109,6 +109,14 @@ module RSpec
|
|
109
109
|
@played_methods[method_name]
|
110
110
|
end
|
111
111
|
|
112
|
+
def build_alias_method_name(method_name)
|
113
|
+
"__#{method_name}_without_any_instance__"
|
114
|
+
end
|
115
|
+
|
116
|
+
def already_observing?(method_name)
|
117
|
+
@observed_methods.include?(method_name)
|
118
|
+
end
|
119
|
+
|
112
120
|
private
|
113
121
|
|
114
122
|
def normalize_chain(*args)
|
@@ -130,10 +138,6 @@ module RSpec
|
|
130
138
|
end
|
131
139
|
end
|
132
140
|
|
133
|
-
def build_alias_method_name(method_name)
|
134
|
-
"__#{method_name}_without_any_instance__"
|
135
|
-
end
|
136
|
-
|
137
141
|
def restore_original_method!(method_name)
|
138
142
|
alias_method_name = build_alias_method_name(method_name)
|
139
143
|
@klass.class_eval do
|
@@ -165,15 +169,11 @@ module RSpec
|
|
165
169
|
@observed_methods.delete(method_name)
|
166
170
|
end
|
167
171
|
|
168
|
-
def already_observing?(method_name)
|
169
|
-
@observed_methods.include?(method_name)
|
170
|
-
end
|
171
|
-
|
172
172
|
def observe!(method_name)
|
173
173
|
stop_observing!(method_name) if already_observing?(method_name)
|
174
174
|
@observed_methods << method_name
|
175
175
|
backup_method!(method_name)
|
176
|
-
@klass.class_eval(<<-EOM, __FILE__, __LINE__)
|
176
|
+
@klass.class_eval(<<-EOM, __FILE__, __LINE__ + 1)
|
177
177
|
def #{method_name}(*args, &blk)
|
178
178
|
klass = ::Object.instance_method(:method).bind(self).call(:#{method_name}).owner
|
179
179
|
klass.__recorder.playback!(self, :#{method_name})
|
@@ -184,7 +184,7 @@ module RSpec
|
|
184
184
|
|
185
185
|
def mark_invoked!(method_name)
|
186
186
|
backup_method!(method_name)
|
187
|
-
@klass.class_eval(<<-EOM, __FILE__, __LINE__)
|
187
|
+
@klass.class_eval(<<-EOM, __FILE__, __LINE__ + 1)
|
188
188
|
def #{method_name}(*args, &blk)
|
189
189
|
method_name = :#{method_name}
|
190
190
|
klass = ::Object.instance_method(:method).bind(self).call(:#{method_name}).owner
|
@@ -30,14 +30,29 @@ module RSpec
|
|
30
30
|
end
|
31
31
|
|
32
32
|
# @private
|
33
|
-
def method_defined_on_klass?
|
34
|
-
|
33
|
+
def method_defined_on_klass?(klass = @klass)
|
34
|
+
klass.method_defined?(@method) || klass.private_method_defined?(@method)
|
35
35
|
end
|
36
36
|
|
37
37
|
if ::UnboundMethod.method_defined?(:owner)
|
38
38
|
# @private
|
39
39
|
def method_owned_by_klass?
|
40
|
-
@klass.instance_method(@method).owner
|
40
|
+
owner = @klass.instance_method(@method).owner
|
41
|
+
# On 1.8 (and some 1.9s -- e.g. rubinius) aliased methods
|
42
|
+
# can report the wrong owner. Example:
|
43
|
+
# class MyClass
|
44
|
+
# class << self
|
45
|
+
# alias alternate_new new
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# MyClass.owner(:alternate_new) returns `Class` on 1.8,
|
50
|
+
# but we need to consider the owner to be `MyClass` because
|
51
|
+
# it is not actually available on `Class` but is on `MyClass`.
|
52
|
+
# Hence, we verify that the owner actually has the method defined.
|
53
|
+
# If the given owner does not have the method defined, we assume
|
54
|
+
# that the method is actually owned by @klass.
|
55
|
+
owner == @klass || !(method_defined_on_klass?(owner))
|
41
56
|
end
|
42
57
|
else
|
43
58
|
# @private
|
@@ -45,6 +45,15 @@ module RSpec
|
|
45
45
|
if @method_stasher.method_is_stashed?
|
46
46
|
# Example: a singleton method defined on @object
|
47
47
|
method_handle_for(@object, @method_stasher.stashed_method_name)
|
48
|
+
elsif meth = original_unrecorded_any_instance_method
|
49
|
+
# Example: a method that has been mocked through
|
50
|
+
# klass.any_instance.should_receive(:msg).and_call_original
|
51
|
+
# any_instance.should_receive(:msg) causes the method to be
|
52
|
+
# replaced with a proxy method, and then `and_call_original`
|
53
|
+
# is recorded and played back on the object instance. We need
|
54
|
+
# special handling here to get a handle on the original method
|
55
|
+
# object rather than the proxy method.
|
56
|
+
meth
|
48
57
|
else
|
49
58
|
begin
|
50
59
|
# Example: an instance method defined on @object's class.
|
@@ -75,6 +84,19 @@ module RSpec
|
|
75
84
|
end
|
76
85
|
end
|
77
86
|
|
87
|
+
def original_unrecorded_any_instance_method
|
88
|
+
return nil unless any_instance_class_recorder_observing_method?(@object.class)
|
89
|
+
alias_name = @object.class.__recorder.build_alias_method_name(@method_name)
|
90
|
+
@object.method(alias_name)
|
91
|
+
end
|
92
|
+
|
93
|
+
def any_instance_class_recorder_observing_method?(klass)
|
94
|
+
return true if klass.__recorder.already_observing?(@method_name)
|
95
|
+
superklass = klass.superclass
|
96
|
+
return false if superklass.nil?
|
97
|
+
any_instance_class_recorder_observing_method?(superklass)
|
98
|
+
end
|
99
|
+
|
78
100
|
if RUBY_VERSION.to_f > 1.8
|
79
101
|
# @private
|
80
102
|
def original_method_from_superclass
|
@@ -57,16 +57,22 @@ module RSpec
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def recursive_const_get(const_name)
|
60
|
-
const_name.split('::').inject(Object)
|
60
|
+
normalize_const_name(const_name).split('::').inject(Object) do |mod, name|
|
61
|
+
get_const_defined_on(mod, name)
|
62
|
+
end
|
61
63
|
end
|
62
64
|
|
63
65
|
def recursive_const_defined?(const_name)
|
64
|
-
const_name.split('::').inject([Object, '']) do |(mod, full_name), name|
|
66
|
+
normalize_const_name(const_name).split('::').inject([Object, '']) do |(mod, full_name), name|
|
65
67
|
yield(full_name, name) if block_given? && !mod.is_a?(Module)
|
66
68
|
return false unless const_defined_on?(mod, name)
|
67
69
|
[get_const_defined_on(mod, name), [mod, name].join('::')]
|
68
70
|
end
|
69
71
|
end
|
72
|
+
|
73
|
+
def normalize_const_name(const_name)
|
74
|
+
const_name.sub(/\A::/, '')
|
75
|
+
end
|
70
76
|
end
|
71
77
|
|
72
78
|
# Provides information about constants that may (or may not)
|
data/lib/rspec/mocks/version.rb
CHANGED
@@ -37,6 +37,24 @@ describe "and_call_original" do
|
|
37
37
|
expect(instance.foo).to eq(:bar)
|
38
38
|
end
|
39
39
|
|
40
|
+
context 'when using any_instance' do
|
41
|
+
it 'works for instance methods defined on the class' do
|
42
|
+
klass.any_instance.should_receive(:meth_1).and_call_original
|
43
|
+
expect(klass.new.meth_1).to eq(:original)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'works for instance methods defined on the superclass of the class' do
|
47
|
+
subclass = Class.new(klass)
|
48
|
+
subclass.any_instance.should_receive(:meth_1).and_call_original
|
49
|
+
expect(subclass.new.meth_1).to eq(:original)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'works when mocking the method on one class and calling the method on an instance of a subclass' do
|
53
|
+
klass.any_instance.should_receive(:meth_1).and_call_original
|
54
|
+
expect(Class.new(klass).new.meth_1).to eq(:original)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
40
58
|
if RUBY_VERSION.to_f > 1.8
|
41
59
|
it 'works for class methods defined on a superclass' do
|
42
60
|
subclass = Class.new(klass)
|
@@ -78,6 +96,17 @@ describe "and_call_original" do
|
|
78
96
|
expect(inst.meth_1).to eq(:original)
|
79
97
|
end
|
80
98
|
|
99
|
+
it 'works for aliased methods' do
|
100
|
+
klass = Class.new do
|
101
|
+
class << self
|
102
|
+
alias alternate_new new
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
klass.should_receive(:alternate_new).and_call_original
|
107
|
+
expect(klass.alternate_new).to be_an_instance_of(klass)
|
108
|
+
end
|
109
|
+
|
81
110
|
context 'on an object that defines method_missing' do
|
82
111
|
before do
|
83
112
|
klass.class_eval do
|
@@ -98,6 +127,18 @@ describe "and_call_original" do
|
|
98
127
|
expect(instance.greet_jack).to eq("Hello, jack")
|
99
128
|
end
|
100
129
|
|
130
|
+
it 'works for an any_instance partial mock' do
|
131
|
+
klass.any_instance.should_receive(:greet_jack).and_call_original
|
132
|
+
expect(instance.greet_jack).to eq("Hello, jack")
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'raises an error for an unhandled message for an any_instance partial mock' do
|
136
|
+
klass.any_instance.should_receive(:not_a_handled_message).and_call_original
|
137
|
+
expect {
|
138
|
+
instance.not_a_handled_message
|
139
|
+
}.to raise_error(NameError, /not_a_handled_message/)
|
140
|
+
end
|
141
|
+
|
101
142
|
it 'raises an error on invocation if method_missing does not handle the message' do
|
102
143
|
instance.should_receive(:not_a_handled_message).and_call_original
|
103
144
|
|
@@ -141,6 +141,10 @@ module RSpec
|
|
141
141
|
it_behaves_like "loaded constant hiding", "TestClass::Nested"
|
142
142
|
end
|
143
143
|
|
144
|
+
context 'for a loaded context prefixed with ::' do
|
145
|
+
it_behaves_like 'loaded constant hiding', "::TestClass"
|
146
|
+
end
|
147
|
+
|
144
148
|
context 'for an unloaded constant with nested name that matches a top-level constant' do
|
145
149
|
it_behaves_like "unloaded constant hiding", "TestClass::Hash"
|
146
150
|
|
@@ -291,6 +295,10 @@ module RSpec
|
|
291
295
|
it_behaves_like "loaded constant stubbing", "TestClass::Nested"
|
292
296
|
end
|
293
297
|
|
298
|
+
context 'for a loaded context prefixed with ::' do
|
299
|
+
it_behaves_like 'loaded constant stubbing', "::TestClass"
|
300
|
+
end
|
301
|
+
|
294
302
|
context 'for an unloaded constant with nested name that matches a top-level constant' do
|
295
303
|
it_behaves_like "unloaded constant stubbing", "TestClass::Hash"
|
296
304
|
end
|
@@ -11,6 +11,7 @@ module RSpec
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
14
|
+
|
14
15
|
it "keeps the original method intact after multiple expectations are added on the same method" do
|
15
16
|
klass.should_receive(:foo).with(:fizbaz).and_return(:wowwow)
|
16
17
|
klass.should_receive(:foo).with(:bazbar).and_return(:okay)
|
@@ -23,5 +24,31 @@ module RSpec
|
|
23
24
|
klass.foo(:yeah).should equal(:original_value)
|
24
25
|
end
|
25
26
|
end
|
27
|
+
|
28
|
+
describe "when a class method is aliased on a subclass and the method is mocked" do
|
29
|
+
let(:klass) do
|
30
|
+
Class.new do
|
31
|
+
class << self
|
32
|
+
alias alternate_new new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "restores the original aliased public method" do
|
38
|
+
klass = Class.new do
|
39
|
+
class << self
|
40
|
+
alias alternate_new new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
klass.should_receive(:alternate_new)
|
45
|
+
expect(klass.alternate_new).to be_nil
|
46
|
+
|
47
|
+
klass.rspec_verify
|
48
|
+
|
49
|
+
klass.rspec_reset
|
50
|
+
expect(klass.alternate_new).to be_an_instance_of(klass)
|
51
|
+
end
|
52
|
+
end
|
26
53
|
end
|
27
54
|
end
|
metadata
CHANGED
@@ -1,71 +1,80 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-mocks
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 61
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 2
|
8
|
+
- 12
|
9
|
+
- 1
|
10
|
+
version: 2.12.1
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Steven Baker
|
9
14
|
- David Chelimsky
|
10
15
|
autorequire:
|
11
16
|
bindir: bin
|
12
17
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
|
19
|
+
date: 2012-12-21 00:00:00 Z
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
18
23
|
none: false
|
19
|
-
requirements:
|
24
|
+
requirements:
|
20
25
|
- - ~>
|
21
|
-
- !ruby/object:Gem::Version
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 79
|
28
|
+
segments:
|
29
|
+
- 10
|
30
|
+
- 0
|
31
|
+
- 0
|
22
32
|
version: 10.0.0
|
23
|
-
type: :development
|
24
33
|
prerelease: false
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
|
-
requirements:
|
28
|
-
- - ~>
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: 10.0.0
|
31
|
-
- !ruby/object:Gem::Dependency
|
32
|
-
name: cucumber
|
33
|
-
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
|
-
requirements:
|
36
|
-
- - ~>
|
37
|
-
- !ruby/object:Gem::Version
|
38
|
-
version: 1.1.9
|
39
34
|
type: :development
|
40
|
-
|
41
|
-
|
35
|
+
name: rake
|
36
|
+
requirement: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
42
39
|
none: false
|
43
|
-
requirements:
|
40
|
+
requirements:
|
44
41
|
- - ~>
|
45
|
-
- !ruby/object:Gem::Version
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 1
|
44
|
+
segments:
|
45
|
+
- 1
|
46
|
+
- 1
|
47
|
+
- 9
|
46
48
|
version: 1.1.9
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: aruba
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
|
-
requirements:
|
52
|
-
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 0.4.11
|
55
|
-
type: :development
|
56
49
|
prerelease: false
|
57
|
-
|
50
|
+
type: :development
|
51
|
+
name: cucumber
|
52
|
+
requirement: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
58
55
|
none: false
|
59
|
-
requirements:
|
56
|
+
requirements:
|
60
57
|
- - ~>
|
61
|
-
- !ruby/object:Gem::Version
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 25
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
- 4
|
63
|
+
- 11
|
62
64
|
version: 0.4.11
|
65
|
+
prerelease: false
|
66
|
+
type: :development
|
67
|
+
name: aruba
|
68
|
+
requirement: *id003
|
63
69
|
description: RSpec's 'test double' framework, with support for stubbing and mocking
|
64
70
|
email: rspec-users@rubyforge.org
|
65
71
|
executables: []
|
72
|
+
|
66
73
|
extensions: []
|
74
|
+
|
67
75
|
extra_rdoc_files: []
|
68
|
-
|
76
|
+
|
77
|
+
files:
|
69
78
|
- lib/rspec/mocks.rb
|
70
79
|
- lib/rspec/mocks/any_instance.rb
|
71
80
|
- lib/rspec/mocks/any_instance/chain.rb
|
@@ -182,38 +191,39 @@ files:
|
|
182
191
|
- spec/rspec/mocks_spec.rb
|
183
192
|
- spec/spec_helper.rb
|
184
193
|
homepage: http://github.com/rspec/rspec-mocks
|
185
|
-
licenses:
|
194
|
+
licenses:
|
186
195
|
- MIT
|
187
196
|
post_install_message:
|
188
|
-
rdoc_options:
|
197
|
+
rdoc_options:
|
189
198
|
- --charset=UTF-8
|
190
|
-
require_paths:
|
199
|
+
require_paths:
|
191
200
|
- lib
|
192
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
201
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
193
202
|
none: false
|
194
|
-
requirements:
|
195
|
-
- -
|
196
|
-
- !ruby/object:Gem::Version
|
197
|
-
|
198
|
-
segments:
|
203
|
+
requirements:
|
204
|
+
- - ">="
|
205
|
+
- !ruby/object:Gem::Version
|
206
|
+
hash: 3
|
207
|
+
segments:
|
199
208
|
- 0
|
200
|
-
|
201
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
209
|
+
version: "0"
|
210
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
202
211
|
none: false
|
203
|
-
requirements:
|
204
|
-
- -
|
205
|
-
- !ruby/object:Gem::Version
|
206
|
-
|
207
|
-
segments:
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
hash: 3
|
216
|
+
segments:
|
208
217
|
- 0
|
209
|
-
|
218
|
+
version: "0"
|
210
219
|
requirements: []
|
220
|
+
|
211
221
|
rubyforge_project: rspec
|
212
222
|
rubygems_version: 1.8.24
|
213
223
|
signing_key:
|
214
224
|
specification_version: 3
|
215
|
-
summary: rspec-mocks-2.12.
|
216
|
-
test_files:
|
225
|
+
summary: rspec-mocks-2.12.1
|
226
|
+
test_files:
|
217
227
|
- features/README.md
|
218
228
|
- features/Scope.md
|
219
229
|
- features/Upgrade.md
|