rspec-mocks 2.10.1 → 2.11.0
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 +26 -1
- data/README.md +14 -2
- data/features/stubbing_constants/README.md +62 -0
- data/features/stubbing_constants/stub_defined_constant.feature +79 -0
- data/features/stubbing_constants/stub_undefined_constant.feature +50 -0
- data/lib/rspec/mocks/any_instance.rb +37 -1
- data/lib/rspec/mocks/any_instance/chain.rb +0 -81
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +57 -0
- data/lib/rspec/mocks/any_instance/recorder.rb +6 -1
- data/lib/rspec/mocks/any_instance/stub_chain.rb +37 -0
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +25 -0
- data/lib/rspec/mocks/argument_list_matcher.rb +93 -0
- data/lib/rspec/mocks/argument_matchers.rb +39 -31
- data/lib/rspec/mocks/error_generator.rb +7 -0
- data/lib/rspec/mocks/example_methods.rb +41 -0
- data/lib/rspec/mocks/framework.rb +2 -1
- data/lib/rspec/mocks/message_expectation.rb +21 -13
- data/lib/rspec/mocks/methods.rb +4 -0
- data/lib/rspec/mocks/proxy.rb +10 -4
- data/lib/rspec/mocks/stub_const.rb +280 -0
- data/lib/rspec/mocks/test_double.rb +3 -2
- data/lib/rspec/mocks/version.rb +1 -1
- data/spec/rspec/mocks/any_instance/message_chains_spec.rb +1 -1
- data/spec/rspec/mocks/any_instance_spec.rb +66 -26
- data/spec/rspec/mocks/argument_expectation_spec.rb +7 -7
- data/spec/rspec/mocks/at_least_spec.rb +14 -0
- data/spec/rspec/mocks/block_return_value_spec.rb +8 -0
- data/spec/rspec/mocks/mock_spec.rb +33 -20
- data/spec/rspec/mocks/multiple_return_value_spec.rb +2 -2
- data/spec/rspec/mocks/null_object_mock_spec.rb +22 -0
- data/spec/rspec/mocks/stub_chain_spec.rb +45 -45
- data/spec/rspec/mocks/stub_const_spec.rb +309 -0
- data/spec/rspec/mocks/stub_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -40
- metadata +18 -6
- data/lib/rspec/mocks/argument_expectation.rb +0 -52
data/Changelog.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
### 2.11.0 / 2012-07-07
|
2
|
+
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.1...v2.11.0)
|
3
|
+
|
4
|
+
Enhancements
|
5
|
+
|
6
|
+
* expose ArgumentListMatcher as a formal API
|
7
|
+
* supports use by 3rd party mock frameworks like Surrogate
|
8
|
+
* Add `stub_const` API to stub constants for the duration of an
|
9
|
+
example (Myron Marston).
|
10
|
+
|
11
|
+
Bug fixes
|
12
|
+
|
13
|
+
* Fix regression of edge case behavior. `double.should_receive(:foo) { a }`
|
14
|
+
was causing a NoMethodError when `double.stub(:foo).and_return(a, b)`
|
15
|
+
had been setup before (Myron Marston).
|
16
|
+
* Infinite loop generated by using `any_instance` and `dup`. (Sidu Ponnappa @kaiwren)
|
17
|
+
* `double.should_receive(:foo).at_least(:once).and_return(a)` always returns a
|
18
|
+
even if `:foo` is already stubbed.
|
19
|
+
* Prevent infinite loop when interpolating a null double into a string
|
20
|
+
as an integer (`"%i" % double.as_null_object`). (Myron Marston)
|
21
|
+
* Fix `should_receive` so that null object behavior (e.g. returning
|
22
|
+
self) is preserved if no implementation is given (Myron Marston).
|
23
|
+
* Fix `and_raise` so that it raises `RuntimeError` rather than
|
24
|
+
`Exception` by default, just like ruby does. (Andrew Marshall)
|
25
|
+
|
1
26
|
### 2.10.1 / 2012-05-05
|
2
27
|
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.10.0...v2.10.1)
|
3
28
|
|
@@ -13,7 +38,7 @@ Bug fixes
|
|
13
38
|
|
14
39
|
Bug fixes
|
15
40
|
|
16
|
-
* fail fast when an `exactly` or `at_most` expectation is exceeded
|
41
|
+
* fail fast when an `exactly` or `at_most` expectation is exceeded
|
17
42
|
|
18
43
|
### 2.9.0 / 2012-03-17
|
19
44
|
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.8.0...v2.9.0)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# RSpec Mocks
|
1
|
+
# RSpec Mocks [](https://codeclimate.com/github/rspec/rspec-mocks)
|
2
2
|
|
3
3
|
rspec-mocks is a test-double framework for rspec with support for method stubs,
|
4
4
|
fakes, and message expectations on generated test-doubles and real objects
|
@@ -156,7 +156,7 @@ double.should_receive(:msg).with(1, kind_of(Numeric), "b") #2nd argument can any
|
|
156
156
|
double.should_receive(:msg).with(1, boolean(), "b") #2nd argument can true or false
|
157
157
|
double.should_receive(:msg).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp
|
158
158
|
double.should_receive(:msg).with(1, anything(), "b") #2nd argument can be anything at all
|
159
|
-
double.should_receive(:msg).with(1,
|
159
|
+
double.should_receive(:msg).with(1, duck_type(:abs, :div), "b")
|
160
160
|
#2nd argument can be object that responds to #abs and #div
|
161
161
|
```
|
162
162
|
|
@@ -246,6 +246,18 @@ While this is a good thing when you really need it, you probably don't really
|
|
246
246
|
need it! Take care to specify only the things that matter to the behavior of
|
247
247
|
your code.
|
248
248
|
|
249
|
+
## Stubbing Constants
|
250
|
+
|
251
|
+
See the [stubbing constants
|
252
|
+
README](https://github.com/rspec/rspec-mocks/blob/master/features/stubbing_constants/README.md)
|
253
|
+
for info on this feature.
|
254
|
+
|
255
|
+
## Use `before(:each)`, not `before(:all)`
|
256
|
+
|
257
|
+
Stubs in `before(:all)` are not supported. The reason is that all stubs and mocks get cleared out after each example, so any stub that is set in `before(:all)` would work in the first example that happens to run in that group, but not for any others.
|
258
|
+
|
259
|
+
Instead of `before(:all)`, use `before(:each)`.
|
260
|
+
|
249
261
|
## Further Reading
|
250
262
|
|
251
263
|
There are many different viewpoints about the meaning of mocks and stubs. If
|
@@ -0,0 +1,62 @@
|
|
1
|
+
## Stubbing Constants
|
2
|
+
|
3
|
+
Support is provided for stubbing constants. Like with method stubs, the
|
4
|
+
stubbed constants will be restored to their original state when an
|
5
|
+
example completes.
|
6
|
+
|
7
|
+
``` ruby
|
8
|
+
stub_const("Foo", fake_foo)
|
9
|
+
Foo # => fake_foo
|
10
|
+
```
|
11
|
+
|
12
|
+
Stubbed constant names must be fully qualified; the current module
|
13
|
+
nesting is not considered.
|
14
|
+
|
15
|
+
``` ruby
|
16
|
+
module MyGem
|
17
|
+
class SomeClass; end
|
18
|
+
end
|
19
|
+
|
20
|
+
module MyGem
|
21
|
+
describe "Something" do
|
22
|
+
let(:fake_class) { Class.new }
|
23
|
+
|
24
|
+
it "accidentally stubs the wrong constant" do
|
25
|
+
# this stubs ::SomeClass (in the top-level namespace),
|
26
|
+
# not MyGem::SomeClass like you probably mean.
|
27
|
+
stub_const("SomeClass", fake_class)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "stubs the right constant" do
|
31
|
+
stub_const("MyGem::SomeClass", fake_class)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
When you stub a constant that is a module or a class, nested
|
38
|
+
constants on the original module or class are not transferred
|
39
|
+
by default, but you can use the `:transfer_nested_constants`
|
40
|
+
option to tell rspec-mocks to transfer them:
|
41
|
+
|
42
|
+
``` ruby
|
43
|
+
class CardDeck
|
44
|
+
SUITS = [:Spades, :Diamonds, :Clubs, :Hearts]
|
45
|
+
NUM_CARDS = 52
|
46
|
+
end
|
47
|
+
|
48
|
+
fake_class = Class.new
|
49
|
+
stub_const("CardDeck", fake_class)
|
50
|
+
CardDeck # => fake_class
|
51
|
+
CardDeck::SUITS # => raises uninitialized constant error
|
52
|
+
CardDeck::NUM_CARDS # => raises uninitialized constant error
|
53
|
+
|
54
|
+
stub_const("CardDeck", fake_class, :transfer_nested_constants => true)
|
55
|
+
CardDeck::SUITS # => [:Spades, :Diamonds, :Clubs, :Hearts]
|
56
|
+
CardDeck::NUM_CARDS # => 52
|
57
|
+
|
58
|
+
stub_const("CardDeck", fake_class, :transfer_nested_constants => [:SUITS])
|
59
|
+
CardDeck::SUITS # => [:Spades, :Diamonds, :Clubs, :Hearts]
|
60
|
+
CardDeck::NUM_CARDS # => raises uninitialized constant error
|
61
|
+
```
|
62
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
Feature: Stub Defined Constant
|
2
|
+
|
3
|
+
Use `stub_const` to stub constants. When the constant is already defined,
|
4
|
+
the stubbed value will replace the original value for the duration of the
|
5
|
+
example.
|
6
|
+
|
7
|
+
Scenario: Stub top-level constant
|
8
|
+
Given a file named "stub_const_spec.rb" with:
|
9
|
+
"""ruby
|
10
|
+
FOO = 7
|
11
|
+
|
12
|
+
describe "stubbing FOO" do
|
13
|
+
it "can stub FOO with a different value" do
|
14
|
+
stub_const("FOO", 5)
|
15
|
+
FOO.should eq(5)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "restores the stubbed constant when the example completes" do
|
19
|
+
FOO.should eq(7)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
"""
|
23
|
+
When I run `rspec stub_const_spec.rb`
|
24
|
+
Then the examples should all pass
|
25
|
+
|
26
|
+
Scenario: Stub nested constant
|
27
|
+
Given a file named "stub_const_spec.rb" with:
|
28
|
+
"""ruby
|
29
|
+
module MyGem
|
30
|
+
class SomeClass
|
31
|
+
FOO = 7
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module MyGem
|
36
|
+
describe SomeClass do
|
37
|
+
it "stubs the nested constant when it is fully qualified" do
|
38
|
+
stub_const("MyGem::SomeClass::FOO", 5)
|
39
|
+
SomeClass::FOO.should eq(5)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
"""
|
44
|
+
When I run `rspec stub_const_spec.rb`
|
45
|
+
Then the examples should all pass
|
46
|
+
|
47
|
+
Scenario: Transfer nested constants
|
48
|
+
Given a file named "stub_const_spec.rb" with:
|
49
|
+
"""ruby
|
50
|
+
module MyGem
|
51
|
+
class SomeClass
|
52
|
+
FOO = 7
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module MyGem
|
57
|
+
describe SomeClass do
|
58
|
+
let(:fake_class) { Class.new }
|
59
|
+
|
60
|
+
it "does not transfer nested constants by default" do
|
61
|
+
stub_const("MyGem::SomeClass", fake_class)
|
62
|
+
expect { SomeClass::FOO }.to raise_error(NameError)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "transfers nested constants when using :transfer_nested_constants => true" do
|
66
|
+
stub_const("MyGem::SomeClass", fake_class, :transfer_nested_constants => true)
|
67
|
+
SomeClass::FOO.should eq(7)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "can specify a list of nested constants to transfer" do
|
71
|
+
stub_const("MyGem::SomeClass", fake_class, :transfer_nested_constants => [:FOO])
|
72
|
+
SomeClass::FOO.should eq(7)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
"""
|
77
|
+
When I run `rspec stub_const_spec.rb`
|
78
|
+
Then the examples should all pass
|
79
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
Feature: Stub Undefined Constant
|
2
|
+
|
3
|
+
Use `stub_const` to stub constants. When the constant is not already defined,
|
4
|
+
all the necessary intermediary modules will be dynamically created. When the
|
5
|
+
example completes, the intermediary module constants will be removed to return
|
6
|
+
the constant state to how it started.
|
7
|
+
|
8
|
+
Scenario: Stub top-level constant
|
9
|
+
Given a file named "stub_const_spec.rb" with:
|
10
|
+
"""ruby
|
11
|
+
describe "stubbing FOO" do
|
12
|
+
it "can stub undefined constant FOO" do
|
13
|
+
stub_const("FOO", 5)
|
14
|
+
FOO.should eq(5)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "undefines the constant when the example completes" do
|
18
|
+
expect { FOO }.to raise_error(NameError)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
"""
|
22
|
+
When I run `rspec stub_const_spec.rb`
|
23
|
+
Then the examples should all pass
|
24
|
+
|
25
|
+
Scenario: Stub nested constant
|
26
|
+
Given a file named "stub_const_spec.rb" with:
|
27
|
+
"""ruby
|
28
|
+
module MyGem
|
29
|
+
class SomeClass
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module MyGem
|
34
|
+
describe SomeClass do
|
35
|
+
it "can stub an arbitrarily deep constant that is undefined" do
|
36
|
+
defined?(SomeClass::A).should be_false
|
37
|
+
stub_const("MyGem::SomeClass::A::B::C", 3)
|
38
|
+
SomeClass::A::B::C.should eq(3)
|
39
|
+
SomeClass::A.should be_a(Module)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'undefines the intermediary constants that were dynamically created' do
|
43
|
+
defined?(SomeClass).should be_true
|
44
|
+
defined?(SomeClass::A).should be_false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
"""
|
49
|
+
When I run `rspec stub_const_spec.rb`
|
50
|
+
Then the examples should all pass
|
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'rspec/mocks/any_instance/chain'
|
2
|
+
require 'rspec/mocks/any_instance/stub_chain'
|
3
|
+
require 'rspec/mocks/any_instance/stub_chain_chain'
|
4
|
+
require 'rspec/mocks/any_instance/expectation_chain'
|
2
5
|
require 'rspec/mocks/any_instance/message_chains'
|
3
6
|
require 'rspec/mocks/any_instance/recorder'
|
4
7
|
|
@@ -24,6 +27,7 @@ module RSpec
|
|
24
27
|
# @return [Recorder]
|
25
28
|
def any_instance
|
26
29
|
RSpec::Mocks::space.add(self)
|
30
|
+
modify_dup_to_remove_mock_proxy_when_invoked
|
27
31
|
__recorder
|
28
32
|
end
|
29
33
|
|
@@ -33,13 +37,45 @@ module RSpec
|
|
33
37
|
super
|
34
38
|
ensure
|
35
39
|
__recorder.stop_all_observation!
|
40
|
+
restore_dup
|
36
41
|
@__recorder = nil
|
37
42
|
end
|
38
|
-
|
43
|
+
|
44
|
+
# @private
|
45
|
+
def rspec_reset
|
46
|
+
restore_dup
|
47
|
+
__mock_proxy.reset
|
48
|
+
end
|
49
|
+
|
39
50
|
# @private
|
40
51
|
def __recorder
|
41
52
|
@__recorder ||= AnyInstance::Recorder.new(self)
|
42
53
|
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def modify_dup_to_remove_mock_proxy_when_invoked
|
57
|
+
unless self.method_defined?(:__rspec_original_dup)
|
58
|
+
self.class_eval do
|
59
|
+
def __rspec_dup
|
60
|
+
__remove_mock_proxy
|
61
|
+
__rspec_original_dup
|
62
|
+
end
|
63
|
+
|
64
|
+
alias_method :__rspec_original_dup, :dup
|
65
|
+
alias_method :dup, :__rspec_dup
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def restore_dup
|
71
|
+
if self.method_defined?(:__rspec_original_dup)
|
72
|
+
self.class_eval do
|
73
|
+
alias_method :dup, :__rspec_original_dup
|
74
|
+
remove_method :__rspec_original_dup
|
75
|
+
remove_method :__rspec_dup
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
43
79
|
end
|
44
80
|
end
|
45
81
|
end
|
@@ -72,87 +72,6 @@ module RSpec
|
|
72
72
|
self
|
73
73
|
end
|
74
74
|
end
|
75
|
-
|
76
|
-
# @private
|
77
|
-
class ExpectationChain < Chain
|
78
|
-
|
79
|
-
# @private
|
80
|
-
def initialize(*args, &block)
|
81
|
-
record(:should_receive, *args, &block)
|
82
|
-
@expectation_fulfilled = false
|
83
|
-
end
|
84
|
-
|
85
|
-
# @private
|
86
|
-
def expectation_fulfilled?
|
87
|
-
@expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
|
88
|
-
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
def verify_invocation_order(rspec_method_name, *args, &block)
|
93
|
-
end
|
94
|
-
|
95
|
-
def invocation_order
|
96
|
-
@invocation_order ||= {
|
97
|
-
:should_receive => [nil],
|
98
|
-
:with => [:should_receive],
|
99
|
-
:and_return => [:with, :should_receive],
|
100
|
-
:and_raise => [:with, :should_receive]
|
101
|
-
}
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# @private
|
106
|
-
class StubChain < Chain
|
107
|
-
|
108
|
-
# @private
|
109
|
-
def initialize(*args, &block)
|
110
|
-
record(:stub, *args, &block)
|
111
|
-
end
|
112
|
-
|
113
|
-
# @private
|
114
|
-
def expectation_fulfilled?
|
115
|
-
true
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
def invocation_order
|
121
|
-
@invocation_order ||= {
|
122
|
-
:stub => [nil],
|
123
|
-
:with => [:stub],
|
124
|
-
:and_return => [:with, :stub],
|
125
|
-
:and_raise => [:with, :stub],
|
126
|
-
:and_yield => [:with, :stub]
|
127
|
-
}
|
128
|
-
end
|
129
|
-
|
130
|
-
def verify_invocation_order(rspec_method_name, *args, &block)
|
131
|
-
unless invocation_order[rspec_method_name].include?(last_message)
|
132
|
-
raise(NoMethodError, "Undefined method #{rspec_method_name}")
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# @private
|
138
|
-
class StubChainChain < StubChain
|
139
|
-
|
140
|
-
# @private
|
141
|
-
def initialize(*args, &block)
|
142
|
-
record(:stub_chain, *args, &block)
|
143
|
-
end
|
144
|
-
|
145
|
-
private
|
146
|
-
|
147
|
-
def invocation_order
|
148
|
-
@invocation_order ||= {
|
149
|
-
:stub_chain => [nil],
|
150
|
-
:and_return => [:stub_chain],
|
151
|
-
:and_raise => [:stub_chain],
|
152
|
-
:and_yield => [:stub_chain]
|
153
|
-
}
|
154
|
-
end
|
155
|
-
end
|
156
75
|
end
|
157
76
|
end
|
158
77
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
module AnyInstance
|
4
|
+
# @api private
|
5
|
+
class ExpectationChain < Chain
|
6
|
+
def expectation_fulfilled?
|
7
|
+
@expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(*args, &block)
|
11
|
+
record(*args, &block)
|
12
|
+
@expectation_fulfilled = false
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def verify_invocation_order(rspec_method_name, *args, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
class PositiveExpectationChain < ExpectationChain
|
22
|
+
def initialize(*args, &block)
|
23
|
+
super(:should_receive, *args, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def invocation_order
|
29
|
+
@invocation_order ||= {
|
30
|
+
:should_receive => [nil],
|
31
|
+
:with => [:should_receive],
|
32
|
+
:and_return => [:with, :should_receive],
|
33
|
+
:and_raise => [:with, :should_receive]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @api private
|
39
|
+
class NegativeExpectationChain < ExpectationChain
|
40
|
+
def initialize(*args, &block)
|
41
|
+
super(:should_not_receive, *args, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def invocation_order
|
47
|
+
@invocation_order ||= {
|
48
|
+
:should_not_receive => [nil],
|
49
|
+
:with => [:should_receive],
|
50
|
+
:and_return => [:with, :should_receive],
|
51
|
+
:and_raise => [:with, :should_receive]
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|