rspec-mocks 2.6.0 → 2.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/features/Scope.md +17 -0
  2. data/features/argument_matchers/README.md +27 -0
  3. data/features/argument_matchers/explicit.feature +60 -0
  4. data/features/argument_matchers/general_matchers.feature +85 -0
  5. data/features/argument_matchers/type_matchers.feature +25 -0
  6. data/features/message_expectations/any_instance.feature +2 -0
  7. data/features/message_expectations/receive_counts.feature +209 -0
  8. data/features/method_stubs/README.md +6 -10
  9. data/features/method_stubs/any_instance.feature +111 -1
  10. data/features/method_stubs/simple_return_value.feature +34 -25
  11. data/features/method_stubs/stub_implementation.feature +1 -1
  12. data/features/method_stubs/to_ary.feature +12 -10
  13. data/features/support/env.rb +1 -1
  14. data/lib/rspec/mocks.rb +1 -1
  15. data/lib/rspec/mocks/any_instance.rb +8 -235
  16. data/lib/rspec/mocks/any_instance/chain.rb +49 -0
  17. data/lib/rspec/mocks/any_instance/expectation_chain.rb +33 -0
  18. data/lib/rspec/mocks/any_instance/message_chains.rb +48 -0
  19. data/lib/rspec/mocks/any_instance/recorder.rb +162 -0
  20. data/lib/rspec/mocks/any_instance/stub_chain.rb +35 -0
  21. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +34 -0
  22. data/lib/rspec/mocks/argument_expectation.rb +1 -1
  23. data/lib/rspec/mocks/extensions/marshal.rb +1 -1
  24. data/lib/rspec/mocks/extensions/psych.rb +1 -1
  25. data/lib/rspec/mocks/methods.rb +1 -1
  26. data/lib/rspec/mocks/mock.rb +2 -2
  27. data/lib/rspec/mocks/proxy.rb +1 -1
  28. data/lib/rspec/mocks/version.rb +1 -1
  29. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +40 -0
  30. data/spec/rspec/mocks/any_instance_spec.rb +147 -2
  31. data/spec/rspec/mocks/any_number_of_times_spec.rb +2 -2
  32. data/spec/rspec/mocks/argument_expectation_spec.rb +15 -3
  33. data/spec/rspec/mocks/at_least_spec.rb +1 -1
  34. data/spec/rspec/mocks/at_most_spec.rb +1 -1
  35. data/spec/rspec/mocks/bug_report_10263_spec.rb +1 -1
  36. data/spec/rspec/mocks/bug_report_7611_spec.rb +1 -1
  37. data/spec/rspec/mocks/bug_report_8165_spec.rb +2 -2
  38. data/spec/rspec/mocks/bug_report_957_spec.rb +2 -2
  39. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +1 -1
  40. data/spec/rspec/mocks/hash_including_matcher_spec.rb +11 -11
  41. data/spec/rspec/mocks/hash_not_including_matcher_spec.rb +6 -6
  42. data/spec/rspec/mocks/mock_spec.rb +49 -43
  43. data/spec/rspec/mocks/multiple_return_value_spec.rb +26 -26
  44. data/spec/rspec/mocks/partial_mock_spec.rb +3 -2
  45. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +1 -0
  46. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +3 -3
  47. data/spec/rspec/mocks/precise_counts_spec.rb +1 -1
  48. data/spec/rspec/mocks/serialization_spec.rb +7 -2
  49. data/spec/rspec/mocks/stub_implementation_spec.rb +6 -6
  50. data/spec/rspec/mocks/stub_spec.rb +6 -6
  51. data/spec/rspec/mocks/to_ary_spec.rb +9 -0
  52. metadata +54 -47
  53. data/.autotest +0 -7
  54. data/.document +0 -5
  55. data/.gitignore +0 -10
  56. data/.travis.yml +0 -7
  57. data/Gemfile +0 -40
  58. data/Guardfile +0 -8
  59. data/License.txt +0 -22
  60. data/Rakefile +0 -75
  61. data/autotest/discover.rb +0 -1
  62. data/cucumber.yml +0 -2
  63. data/features/.nav +0 -17
  64. data/features/Changelog.md +0 -80
  65. data/rspec-mocks.gemspec +0 -25
  66. data/specs.watchr +0 -57
@@ -6,7 +6,7 @@ Feature: stub on any instance of a class
6
6
 
7
7
  Messages can be stubbed on any class, including those in Ruby's core library.
8
8
 
9
- Scenario: simple any_instance stub with a single return value
9
+ Scenario: any_instance stub with a single return value
10
10
  Given a file named "example_spec.rb" with:
11
11
  """
12
12
  describe "any_instance.stub" do
@@ -20,4 +20,114 @@ Feature: stub on any instance of a class
20
20
  """
21
21
  When I run `rspec example_spec.rb`
22
22
  Then the examples should all pass
23
+
24
+ Scenario: any_instance stub with a hash
25
+ Given a file named "example_spec.rb" with:
26
+ """
27
+ describe "any_instance.stub" do
28
+ context "with a hash" do
29
+ it "returns the hash values on any instance of the class" do
30
+ Object.any_instance.stub(:foo => 'foo', :bar => 'bar')
31
+
32
+ o = Object.new
33
+ o.foo.should eq('foo')
34
+ o.bar.should eq('bar')
35
+ end
36
+ end
37
+ end
38
+ """
39
+ When I run `rspec example_spec.rb`
40
+ Then the examples should all pass
41
+
42
+ Scenario: any_instance stub with specific arguments matchers
43
+ Given a file named "example_spec.rb" with:
44
+ """
45
+ describe "any_instance.stub" do
46
+ context "with arguments" do
47
+ it "returns the stubbed value when arguments match" do
48
+ Object.any_instance.stub(:foo).with(:param_one, :param_two).and_return(:result_one)
49
+ Object.any_instance.stub(:foo).with(:param_three, :param_four).and_return(:result_two)
50
+
51
+ o = Object.new
52
+ o.foo(:param_one, :param_two).should eq(:result_one)
53
+ o.foo(:param_three, :param_four).should eq(:result_two)
54
+ end
55
+ end
56
+ end
57
+ """
58
+ When I run `rspec example_spec.rb`
59
+ Then the examples should all pass
60
+
61
+ Scenario: any_instance unstub
62
+ Given a file named "example_spec.rb" with:
63
+ """
64
+ describe "any_instance.unstub" do
65
+ it "unstubs a stubbed method" do
66
+ class Object
67
+ def foo
68
+ :foo
69
+ end
70
+ end
71
+
72
+ Object.any_instance.stub(:foo)
73
+ Object.any_instance.unstub(:foo)
74
+
75
+ Object.new.foo.should eq(:foo)
76
+ end
77
+ end
78
+ """
79
+ When I run `rspec example_spec.rb`
80
+ Then the examples should all pass
81
+
82
+ Scenario: any_instance unstub
83
+ Given a file named "example_spec.rb" with:
84
+ """
85
+ describe "any_instance.unstub" do
86
+ context "with both an expectation and a stub already set" do
87
+ it "only removes the stub" do
88
+ class Object
89
+ def foo
90
+ :foo
91
+ end
92
+ end
93
+ Object.any_instance.should_receive(:foo).and_return(:bar)
94
+ Object.any_instance.stub(:foo)
95
+ Object.any_instance.unstub(:foo)
96
+
97
+ Object.new.foo.should eq(:bar)
98
+ end
99
+ end
100
+ end
101
+ """
102
+ When I run `rspec example_spec.rb`
103
+ Then the examples should all pass
104
+
105
+ Scenario: stub a chain of methods an any instance
106
+ Given a file named "stub_chain_spec.rb" with:
107
+ """
108
+ describe "stubbing a chain of methods" do
109
+ context "given symbols representing methods" do
110
+ it "returns the correct value" do
111
+ Object.any_instance.stub_chain(:one, :two, :three).and_return(:four)
112
+ Object.new.one.two.three.should eq(:four)
113
+ end
114
+ end
115
+
116
+ context "given a hash at the end" do
117
+ it "returns the correct value" do
118
+ Object.any_instance.stub_chain(:one, :two, :three => :four)
119
+ Object.new.one.two.three.should eq(:four)
120
+ end
121
+ end
23
122
 
123
+ context "given a string of methods separated by dots" do
124
+ it "returns the correct value" do
125
+ Object.any_instance.stub_chain("one.two.three").and_return(:four)
126
+ Object.new.one.two.three.should eq(:four)
127
+ end
128
+ end
129
+ end
130
+ """
131
+ When I run `rspec stub_chain_spec.rb`
132
+ Then the examples should all pass
133
+
@@ -1,55 +1,64 @@
1
1
  Feature: stub with a simple return value
2
2
 
3
3
  Use the `stub` method on a test double or a real object to tell the object to
4
- return a value (or values) in response to a given message. If the message is
5
- never received, nothing happens.
4
+ return a value (or values) in response to a given message. Nothing happens if
5
+ the message is never received.
6
6
 
7
- Scenario: simple stub with no return value
7
+ Scenario: stub with no return value
8
8
  Given a file named "example_spec.rb" with:
9
9
  """
10
- describe "a simple stub with no return value specified" do
11
- let(:receiver) { double("receiver") }
10
+ describe "a stub with no return value specified" do
11
+ let(:collaborator) { double("collaborator") }
12
12
 
13
13
  it "returns nil" do
14
- receiver.stub(:message)
15
- receiver.message.should be(nil)
16
- end
17
-
18
- it "quietly carries on when not called" do
19
- receiver.stub(:message)
14
+ collaborator.stub(:message)
15
+ collaborator.message.should be(nil)
20
16
  end
21
17
  end
22
18
  """
23
19
  When I run `rspec example_spec.rb`
24
- Then the output should contain "0 failures"
20
+ Then the examples should all pass
25
21
 
26
- Scenario: single return value
22
+ Scenario: stubs with return values
27
23
  Given a file named "example_spec.rb" with:
28
24
  """
29
- describe "a simple stub with a return value" do
25
+ describe "a stub with a return value" do
30
26
  context "specified in a block" do
31
27
  it "returns the specified value" do
32
- receiver = double("receiver")
33
- receiver.stub(:message) { :return_value }
34
- receiver.message.should eq(:return_value)
28
+ collaborator = double("collaborator")
29
+ collaborator.stub(:message) { :value }
30
+ collaborator.message.should eq(:value)
31
+ end
32
+ end
33
+
34
+ context "specified with #and_return" do
35
+ it "returns the specified value" do
36
+ collaborator = double("collaborator")
37
+ collaborator.stub(:message).and_return(:value)
38
+ collaborator.message.should eq(:value)
35
39
  end
36
40
  end
37
41
 
38
- context "specified in the double declaration" do
42
+ context "specified with a hash passed to #stub" do
39
43
  it "returns the specified value" do
40
- receiver = double("receiver", :message => :return_value)
41
- receiver.message.should eq(:return_value)
44
+ collaborator = double("collaborator")
45
+ collaborator.stub(:message_1 => :value_1, :message_2 => :value_2)
46
+ collaborator.message_1.should eq(:value_1)
47
+ collaborator.message_2.should eq(:value_2)
42
48
  end
43
49
  end
44
50
 
45
- context "specified with and_return" do
51
+ context "specified with a hash passed to #double" do
46
52
  it "returns the specified value" do
47
- receiver = double("receiver")
48
- receiver.stub(:message).and_return(:return_value)
49
- receiver.message.should eq(:return_value)
53
+ collaborator = double("collaborator",
54
+ :message_1 => :value_1,
55
+ :message_2 => :value_2
56
+ )
57
+ collaborator.message_1.should eq(:value_1)
58
+ collaborator.message_2.should eq(:value_2)
50
59
  end
51
60
  end
52
61
  end
53
62
  """
54
63
  When I run `rspec example_spec.rb`
55
- Then the output should contain "0 failures"
64
+ Then the examples should all pass
@@ -1,7 +1,7 @@
1
1
  Feature: stub with substitute implementation
2
2
 
3
3
  You can stub an implementation of a method (a.k.a. fake) by passing a block
4
- to the stub() method.
4
+ to the `stub` method.
5
5
 
6
6
  Scenario: stub implementation
7
7
  Given a file named "stub_implementation_spec.rb" with:
@@ -1,22 +1,24 @@
1
1
  Feature: double handling to_ary
2
2
 
3
- Ruby implicitly sends to_ary to any objects in an Array when the array
4
- receives `flatten`:
3
+ Ruby implicitly sends `to_ary` to all of the objects in an `Array` when the
4
+ array receives `flatten`:
5
5
 
6
6
  [obj].flatten # => obj.to_ary
7
7
 
8
+ If `to_ary` raises a `NoMethodError`, Ruby sees that the object can not be coerced
9
+ into an array and moves on to the next object.
10
+
8
11
  To support this, an RSpec double will raise a NoMethodError when it receives
9
- `to_ary`, unless that method is explicitly stubbed.
12
+ `to_ary` _even if it is set `as_null_object`_, unless `to_ary` is explicitly
13
+ stubbed.
10
14
 
11
15
  Scenario: double receiving to_ary
12
16
  Given a file named "example.rb" with:
13
17
  """
14
- describe "a double receiving to_ary" do
18
+ describe "#to_ary" do
15
19
  shared_examples "to_ary" do
16
- it "returns nil" do
17
- expect do
18
- obj.to_ary.should be_nil
19
- end.to raise_error(NoMethodError)
20
+ it "raises a NoMethodError" do
21
+ expect { obj.to_ary }.to raise_error(NoMethodError)
20
22
  end
21
23
 
22
24
  it "can be overridden with a stub" do
@@ -30,12 +32,12 @@ Feature: double handling to_ary
30
32
  end
31
33
  end
32
34
 
33
- context "double as_null_object" do
35
+ context "sent to a double as_null_object" do
34
36
  let(:obj) { double('obj').as_null_object }
35
37
  include_examples "to_ary"
36
38
  end
37
39
 
38
- context "double without as_null_object" do
40
+ context "sent to a double without as_null_object" do
39
41
  let(:obj) { double('obj') }
40
42
  include_examples "to_ary"
41
43
  end
@@ -2,5 +2,5 @@ require 'aruba/cucumber'
2
2
  require 'rspec/expectations'
3
3
 
4
4
  Before do
5
- @aruba_timeout_seconds = 3
5
+ RUBY_PLATFORM =~ /java/ ? @aruba_timeout_seconds = 60 : @aruba_timeout_seconds = 5
6
6
  end
@@ -152,7 +152,7 @@ module RSpec
152
152
  #
153
153
  # double.should_receive(:msg) do |arg|
154
154
  # arg.should be_an_istance_of(Array)
155
- # arg.length.should == 7
155
+ # arg.length.should eq 7
156
156
  # end
157
157
  #
158
158
  # == Combining Expectation Details
@@ -1,245 +1,18 @@
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'
5
+ require 'rspec/mocks/any_instance/message_chains'
6
+ require 'rspec/mocks/any_instance/recorder'
7
+
1
8
  module RSpec
2
9
  module Mocks
3
10
  module AnyInstance
4
- class Chain
5
- [
6
- :with, :and_return, :and_raise, :and_yield,
7
- :once, :twice, :any_number_of_times,
8
- :exactly, :times, :never,
9
- :at_least, :at_most
10
- ].each do |method_name|
11
- class_eval(<<-EOM, __FILE__, __LINE__)
12
- def #{method_name}(*args, &block)
13
- record(:#{method_name}, *args, &block)
14
- end
15
- EOM
16
- end
17
-
18
- def playback!(instance)
19
- messages.inject(instance) do |instance, message|
20
- instance.send(*message.first, &message.last)
21
- end
22
- end
23
-
24
- def constrained_to_any_of?(*constraints)
25
- constraints.any? do |constraint|
26
- messages.any? do |message|
27
- message.first.first == constraint
28
- end
29
- end
30
- end
31
-
32
- private
33
- def messages
34
- @messages ||= []
35
- end
36
-
37
- def last_message
38
- messages.last.first.first unless messages.empty?
39
- end
40
-
41
- def record(rspec_method_name, *args, &block)
42
- verify_invocation_order(rspec_method_name, *args, &block)
43
- messages << [args.unshift(rspec_method_name), block]
44
- self
45
- end
46
- end
47
-
48
- class StubChain < Chain
49
- def initialize(*args, &block)
50
- record(:stub, *args, &block)
51
- end
52
-
53
- def invocation_order
54
- @invocation_order ||= {
55
- :stub => [nil],
56
- :with => [:stub],
57
- :and_return => [:with, :stub],
58
- :and_raise => [:with, :stub],
59
- :and_yield => [:with, :stub]
60
- }
61
- end
62
-
63
- def expectation_filfilled?
64
- true
65
- end
66
-
67
- private
68
- def verify_invocation_order(rspec_method_name, *args, &block)
69
- unless invocation_order[rspec_method_name].include?(last_message)
70
- raise(NoMethodError, "Undefined method #{rspec_method_name}")
71
- end
72
- end
73
- end
74
-
75
- class ExpectationChain < Chain
76
- def initialize(*args, &block)
77
- record(:should_receive, *args, &block)
78
- @expectation_fulfilled = false
79
- end
80
-
81
- def invocation_order
82
- @invocation_order ||= {
83
- :should_receive => [nil],
84
- :with => [:should_receive],
85
- :and_return => [:with, :should_receive],
86
- :and_raise => [:with, :should_receive]
87
- }
88
- end
89
-
90
- def expectation_fulfilled!
91
- @expectation_fulfilled = true
92
- end
93
-
94
- def expectation_filfilled?
95
- @expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
96
- end
97
-
98
- private
99
- def verify_invocation_order(rspec_method_name, *args, &block)
100
- end
101
- end
102
-
103
- class Recorder
104
- def initialize(klass)
105
- @message_chains = {}
106
- @observed_methods = []
107
- @played_methods = {}
108
- @klass = klass
109
- @expectation_set = false
110
- end
111
-
112
- def stub(method_name, *args, &block)
113
- observe!(method_name)
114
- @message_chains[method_name] = StubChain.new(method_name, *args, &block)
115
- end
116
-
117
- def should_receive(method_name, *args, &block)
118
- observe!(method_name)
119
- @expectation_set = true
120
- @message_chains[method_name] = ExpectationChain.new(method_name, *args, &block)
121
- end
122
-
123
- def stop_all_observation!
124
- @observed_methods.each do |method_name|
125
- restore_method!(method_name)
126
- end
127
- end
128
-
129
- def playback!(instance, method_name)
130
- RSpec::Mocks::space.add(instance)
131
- @message_chains[method_name].playback!(instance)
132
- @played_methods[method_name] = instance
133
- received_expected_message!(method_name) if has_expectation?(method_name)
134
- end
135
-
136
- def instance_that_received(method_name)
137
- @played_methods[method_name]
138
- end
139
-
140
- def verify
141
- if @expectation_set && !each_expectation_filfilled?
142
- raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
143
- end
144
- end
145
-
146
- private
147
- def each_expectation_filfilled?
148
- @message_chains.all? do |method_name, chain|
149
- chain.expectation_filfilled?
150
- end
151
- end
152
-
153
- def has_expectation?(method_name)
154
- @message_chains[method_name].is_a?(ExpectationChain)
155
- end
156
-
157
- def unfulfilled_expectations
158
- @message_chains.map do |method_name, chain|
159
- method_name.to_s if chain.is_a?(ExpectationChain) unless chain.expectation_filfilled?
160
- end.compact
161
- end
162
-
163
- def received_expected_message!(method_name)
164
- @message_chains[method_name].expectation_fulfilled!
165
- restore_method!(method_name)
166
- mark_invoked!(method_name)
167
- end
168
-
169
- def restore_method!(method_name)
170
- if @klass.method_defined?(build_alias_method_name(method_name))
171
- restore_original_method!(method_name)
172
- else
173
- remove_dummy_method!(method_name)
174
- end
175
- end
176
-
177
- def build_alias_method_name(method_name)
178
- "__#{method_name}_without_any_instance__"
179
- end
180
-
181
- def restore_original_method!(method_name)
182
- alias_method_name = build_alias_method_name(method_name)
183
- @klass.class_eval do
184
- alias_method method_name, alias_method_name
185
- remove_method alias_method_name
186
- end
187
- end
188
-
189
- def remove_dummy_method!(method_name)
190
- @klass.class_eval do
191
- remove_method method_name
192
- end
193
- end
194
-
195
- def backup_method!(method_name)
196
- alias_method_name = build_alias_method_name(method_name)
197
- @klass.class_eval do
198
- if method_defined?(method_name)
199
- alias_method alias_method_name, method_name
200
- end
201
- end
202
- end
203
-
204
- def stop_observing!(method_name)
205
- restore_method!(method_name)
206
- @observed_methods.delete(method_name)
207
- end
208
-
209
- def already_observing?(method_name)
210
- @observed_methods.include?(method_name)
211
- end
212
-
213
- def observe!(method_name)
214
- stop_observing!(method_name) if already_observing?(method_name)
215
- @observed_methods << method_name
216
- backup_method!(method_name)
217
- @klass.class_eval(<<-EOM, __FILE__, __LINE__)
218
- def #{method_name}(*args, &blk)
219
- self.class.__recorder.playback!(self, :#{method_name})
220
- self.send(:#{method_name}, *args, &blk)
221
- end
222
- EOM
223
- end
224
-
225
- def mark_invoked!(method_name)
226
- backup_method!(method_name)
227
- @klass.class_eval(<<-EOM, __FILE__, __LINE__)
228
- def #{method_name}(*args, &blk)
229
- method_name = :#{method_name}
230
- current_instance = self
231
- invoked_instance = self.class.__recorder.instance_that_received(method_name)
232
- raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by \#{self.inspect} but has already been received by \#{invoked_instance}"
233
- end
234
- EOM
235
- end
236
- end
237
-
238
11
  def any_instance
239
12
  RSpec::Mocks::space.add(self)
240
13
  __recorder
241
14
  end
242
-
15
+
243
16
  def rspec_verify
244
17
  __recorder.verify
245
18
  super