rspec-mocks 2.12.0 → 2.12.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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...2.12.0)
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 [stubbing constants
277
- README](https://github.com/rspec/rspec-mocks/blob/master/features/stubbing_constants/README.md)
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
- @klass.method_defined?(@method) || @klass.private_method_defined?(@method)
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 == @klass
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) { |mod, name| get_const_defined_on(mod, name) }
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)
@@ -1,7 +1,7 @@
1
1
  module RSpec
2
2
  module Mocks
3
3
  module Version
4
- STRING = '2.12.0'
4
+ STRING = '2.12.1'
5
5
  end
6
6
  end
7
7
  end
@@ -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
@@ -94,7 +94,7 @@ module RSpec
94
94
 
95
95
  it "includes the class name in the error when mocking a class method that is called an extra time with the wrong args" do
96
96
  klass = Class.new do
97
- def self.to_s
97
+ def self.inspect
98
98
  "MyClass"
99
99
  end
100
100
  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
- version: 2.12.0
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
- date: 2012-11-13 00:00:00.000000000 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: rake
17
- requirement: !ruby/object:Gem::Requirement
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
- prerelease: false
41
- version_requirements: !ruby/object:Gem::Requirement
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
- version_requirements: !ruby/object:Gem::Requirement
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
- files:
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
- version: '0'
198
- segments:
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ hash: 3
207
+ segments:
199
208
  - 0
200
- hash: -450868354806899778
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
- version: '0'
207
- segments:
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ hash: 3
216
+ segments:
208
217
  - 0
209
- hash: -450868354806899778
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.0
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