rspec-mocks 2.10.1 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/Changelog.md +26 -1
  2. data/README.md +14 -2
  3. data/features/stubbing_constants/README.md +62 -0
  4. data/features/stubbing_constants/stub_defined_constant.feature +79 -0
  5. data/features/stubbing_constants/stub_undefined_constant.feature +50 -0
  6. data/lib/rspec/mocks/any_instance.rb +37 -1
  7. data/lib/rspec/mocks/any_instance/chain.rb +0 -81
  8. data/lib/rspec/mocks/any_instance/expectation_chain.rb +57 -0
  9. data/lib/rspec/mocks/any_instance/recorder.rb +6 -1
  10. data/lib/rspec/mocks/any_instance/stub_chain.rb +37 -0
  11. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +25 -0
  12. data/lib/rspec/mocks/argument_list_matcher.rb +93 -0
  13. data/lib/rspec/mocks/argument_matchers.rb +39 -31
  14. data/lib/rspec/mocks/error_generator.rb +7 -0
  15. data/lib/rspec/mocks/example_methods.rb +41 -0
  16. data/lib/rspec/mocks/framework.rb +2 -1
  17. data/lib/rspec/mocks/message_expectation.rb +21 -13
  18. data/lib/rspec/mocks/methods.rb +4 -0
  19. data/lib/rspec/mocks/proxy.rb +10 -4
  20. data/lib/rspec/mocks/stub_const.rb +280 -0
  21. data/lib/rspec/mocks/test_double.rb +3 -2
  22. data/lib/rspec/mocks/version.rb +1 -1
  23. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +1 -1
  24. data/spec/rspec/mocks/any_instance_spec.rb +66 -26
  25. data/spec/rspec/mocks/argument_expectation_spec.rb +7 -7
  26. data/spec/rspec/mocks/at_least_spec.rb +14 -0
  27. data/spec/rspec/mocks/block_return_value_spec.rb +8 -0
  28. data/spec/rspec/mocks/mock_spec.rb +33 -20
  29. data/spec/rspec/mocks/multiple_return_value_spec.rb +2 -2
  30. data/spec/rspec/mocks/null_object_mock_spec.rb +22 -0
  31. data/spec/rspec/mocks/stub_chain_spec.rb +45 -45
  32. data/spec/rspec/mocks/stub_const_spec.rb +309 -0
  33. data/spec/rspec/mocks/stub_spec.rb +2 -2
  34. data/spec/spec_helper.rb +0 -40
  35. metadata +18 -6
  36. data/lib/rspec/mocks/argument_expectation.rb +0 -52
@@ -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 [![Code Climate](https://codeclimate.com/badge.png)](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, ducktype(:abs, :div), "b")
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