rspec-mocks 2.0.0.a1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.document +5 -0
  2. data/.gitignore +6 -0
  3. data/License.txt +22 -0
  4. data/README.markdown +8 -0
  5. data/Rakefile +50 -0
  6. data/VERSION +1 -0
  7. data/VERSION.yml +5 -0
  8. data/lib/rspec/mocks.rb +201 -0
  9. data/lib/rspec/mocks/argument_expectation.rb +51 -0
  10. data/lib/rspec/mocks/argument_matchers.rb +233 -0
  11. data/lib/rspec/mocks/error_generator.rb +81 -0
  12. data/lib/rspec/mocks/errors.rb +10 -0
  13. data/lib/rspec/mocks/extensions.rb +1 -0
  14. data/lib/rspec/mocks/extensions/object.rb +3 -0
  15. data/lib/rspec/mocks/framework.rb +15 -0
  16. data/lib/rspec/mocks/message_expectation.rb +326 -0
  17. data/lib/rspec/mocks/methods.rb +63 -0
  18. data/lib/rspec/mocks/mock.rb +65 -0
  19. data/lib/rspec/mocks/order_group.rb +29 -0
  20. data/lib/rspec/mocks/proxy.rb +230 -0
  21. data/lib/rspec/mocks/space.rb +28 -0
  22. data/lib/rspec/mocks/spec_methods.rb +39 -0
  23. data/lib/spec/mocks.rb +1 -0
  24. data/spec/rspec/mocks/any_number_of_times_spec.rb +36 -0
  25. data/spec/rspec/mocks/argument_expectation_spec.rb +23 -0
  26. data/spec/rspec/mocks/at_least_spec.rb +97 -0
  27. data/spec/rspec/mocks/at_most_spec.rb +93 -0
  28. data/spec/rspec/mocks/bug_report_10260_spec.rb +8 -0
  29. data/spec/rspec/mocks/bug_report_10263_spec.rb +27 -0
  30. data/spec/rspec/mocks/bug_report_11545_spec.rb +32 -0
  31. data/spec/rspec/mocks/bug_report_15719_spec.rb +29 -0
  32. data/spec/rspec/mocks/bug_report_496_spec.rb +17 -0
  33. data/spec/rspec/mocks/bug_report_600_spec.rb +22 -0
  34. data/spec/rspec/mocks/bug_report_7611_spec.rb +19 -0
  35. data/spec/rspec/mocks/bug_report_7805_spec.rb +22 -0
  36. data/spec/rspec/mocks/bug_report_8165_spec.rb +31 -0
  37. data/spec/rspec/mocks/bug_report_8302_spec.rb +26 -0
  38. data/spec/rspec/mocks/bug_report_830_spec.rb +21 -0
  39. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +95 -0
  40. data/spec/rspec/mocks/hash_including_matcher_spec.rb +90 -0
  41. data/spec/rspec/mocks/hash_not_including_matcher_spec.rb +67 -0
  42. data/spec/rspec/mocks/mock_ordering_spec.rb +94 -0
  43. data/spec/rspec/mocks/mock_space_spec.rb +54 -0
  44. data/spec/rspec/mocks/mock_spec.rb +583 -0
  45. data/spec/rspec/mocks/multiple_return_value_spec.rb +113 -0
  46. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +63 -0
  47. data/spec/rspec/mocks/null_object_mock_spec.rb +54 -0
  48. data/spec/rspec/mocks/once_counts_spec.rb +53 -0
  49. data/spec/rspec/mocks/options_hash_spec.rb +35 -0
  50. data/spec/rspec/mocks/partial_mock_spec.rb +164 -0
  51. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +66 -0
  52. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +145 -0
  53. data/spec/rspec/mocks/precise_counts_spec.rb +52 -0
  54. data/spec/rspec/mocks/record_messages_spec.rb +26 -0
  55. data/spec/rspec/mocks/stub_chain_spec.rb +34 -0
  56. data/spec/rspec/mocks/stub_implementation_spec.rb +31 -0
  57. data/spec/rspec/mocks/stub_spec.rb +198 -0
  58. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +26 -0
  59. data/spec/rspec/mocks/twice_counts_spec.rb +67 -0
  60. data/spec/spec_helper.rb +52 -0
  61. data/spec/support/macros.rb +29 -0
  62. metadata +172 -0
@@ -0,0 +1,65 @@
1
+ module Rspec
2
+ module Mocks
3
+ class Mock
4
+ include Methods
5
+
6
+ # Creates a new mock with a +name+ (that will be used in error messages
7
+ # only) == Options:
8
+ # * <tt>:null_object</tt> - if true, the mock object acts as a forgiving
9
+ # null object allowing any message to be sent to it.
10
+ def initialize(name='mock', stubs_and_options={})
11
+ if name.is_a?(Hash) && stubs_and_options.empty?
12
+ stubs_and_options = name
13
+ build_name_from_options stubs_and_options
14
+ else
15
+ @name = name
16
+ end
17
+ @options = parse_options(stubs_and_options)
18
+ assign_stubs(stubs_and_options)
19
+ end
20
+
21
+ # This allows for comparing the mock to other objects that proxy such as
22
+ # ActiveRecords belongs_to proxy objects. By making the other object run
23
+ # the comparison, we're sure the call gets delegated to the proxy
24
+ # target.
25
+ def ==(other)
26
+ other == __mock_proxy
27
+ end
28
+
29
+ def inspect
30
+ "#<#{self.class}:#{sprintf '0x%x', self.object_id} @name=#{@name.inspect}>"
31
+ end
32
+
33
+ def to_s
34
+ inspect.gsub('<','[').gsub('>',']')
35
+ end
36
+
37
+ private
38
+
39
+ def method_missing(sym, *args, &block)
40
+ __mock_proxy.record_message_received(sym, args, block)
41
+ begin
42
+ return self if __mock_proxy.null_object?
43
+ super(sym, *args, &block)
44
+ rescue NameError
45
+ __mock_proxy.raise_unexpected_message_error sym, *args
46
+ end
47
+ end
48
+
49
+ def parse_options(options)
50
+ options.has_key?(:null_object) ? {:null_object => options.delete(:null_object)} : {}
51
+ end
52
+
53
+ def assign_stubs(stubs)
54
+ stubs.each_pair do |message, response|
55
+ stub!(message).and_return(response)
56
+ end
57
+ end
58
+
59
+ def build_name_from_options(options)
60
+ vals = options.inject([]) {|coll, pair| coll << "#{pair.first}: #{pair.last.inspect}"}
61
+ @name = '{' + vals.join(', ') + '}'
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,29 @@
1
+ module Rspec
2
+ module Mocks
3
+ class OrderGroup
4
+ def initialize error_generator
5
+ @error_generator = error_generator
6
+ @ordering = Array.new
7
+ end
8
+
9
+ def register(expectation)
10
+ @ordering << expectation
11
+ end
12
+
13
+ def ready_for?(expectation)
14
+ return @ordering.first == expectation
15
+ end
16
+
17
+ def consume
18
+ @ordering.shift
19
+ end
20
+
21
+ def handle_order_constraint expectation
22
+ return unless @ordering.include? expectation
23
+ return consume if ready_for?(expectation)
24
+ @error_generator.raise_out_of_order_error expectation.sym
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,230 @@
1
+ module Rspec
2
+ module Mocks
3
+ class Proxy
4
+ DEFAULT_OPTIONS = {
5
+ :null_object => false,
6
+ }
7
+
8
+ @@warn_about_expectations_on_nil = true
9
+
10
+ def self.allow_message_expectations_on_nil
11
+ @@warn_about_expectations_on_nil = false
12
+
13
+ # ensure nil.rspec_verify is called even if an expectation is not set in the example
14
+ # otherwise the allowance would effect subsequent examples
15
+ $rspec_mocks.add(nil) unless $rspec_mocks.nil?
16
+ end
17
+
18
+ def initialize(target, name=nil, options={})
19
+ @target = target
20
+ @name = name
21
+ @error_generator = ErrorGenerator.new target, name
22
+ @expectation_ordering = OrderGroup.new @error_generator
23
+ @expectations = []
24
+ @messages_received = []
25
+ @stubs = []
26
+ @proxied_methods = []
27
+ @options = options ? DEFAULT_OPTIONS.dup.merge(options) : DEFAULT_OPTIONS
28
+ @already_proxied_respond_to = false
29
+ end
30
+
31
+ def null_object?
32
+ @options[:null_object]
33
+ end
34
+
35
+ def as_null_object
36
+ @options[:null_object] = true
37
+ @target
38
+ end
39
+
40
+ def add_message_expectation(expected_from, sym, opts={}, &block)
41
+ __add sym
42
+ warn_if_nil_class sym
43
+ if existing_stub = @stubs.detect {|s| s.sym == sym }
44
+ expectation = existing_stub.build_child(expected_from, block_given?? block : nil, 1, opts)
45
+ else
46
+ expectation = MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil, 1, opts)
47
+ end
48
+ @expectations << expectation
49
+ @expectations.last
50
+ end
51
+
52
+ def add_negative_message_expectation(expected_from, sym, &block)
53
+ __add sym
54
+ warn_if_nil_class sym
55
+ @expectations << NegativeMessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil)
56
+ @expectations.last
57
+ end
58
+
59
+ def add_stub(expected_from, sym, opts={}, &implementation)
60
+ __add sym
61
+ @stubs.unshift MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, nil, :any, opts, &implementation)
62
+ @stubs.first
63
+ end
64
+
65
+ def verify #:nodoc:
66
+ verify_expectations
67
+ ensure
68
+ reset
69
+ end
70
+
71
+ def reset
72
+ clear_expectations
73
+ clear_stubs
74
+ reset_proxied_methods
75
+ clear_proxied_methods
76
+ reset_nil_expectations_warning
77
+ end
78
+
79
+ def received_message?(sym, *args, &block)
80
+ @messages_received.any? {|array| array == [sym, args, block]}
81
+ end
82
+
83
+ def has_negative_expectation?(sym)
84
+ @expectations.detect {|expectation| expectation.negative_expectation_for?(sym)}
85
+ end
86
+
87
+ def record_message_received(sym, args, block)
88
+ @messages_received << [sym, args, block]
89
+ end
90
+
91
+ def message_received(sym, *args, &block)
92
+ expectation = find_matching_expectation(sym, *args)
93
+ stub = find_matching_method_stub(sym, *args)
94
+
95
+ if (stub && expectation && expectation.called_max_times?) || (stub && !expectation)
96
+ if expectation = find_almost_matching_expectation(sym, *args)
97
+ expectation.advise(args, block) unless expectation.expected_messages_received?
98
+ end
99
+ stub.invoke(args, block)
100
+ elsif expectation
101
+ expectation.invoke(args, block)
102
+ elsif expectation = find_almost_matching_expectation(sym, *args)
103
+ expectation.advise(args, block) if null_object? unless expectation.expected_messages_received?
104
+ raise_unexpected_message_args_error(expectation, *args) unless (has_negative_expectation?(sym) or null_object?)
105
+ else
106
+ @target.__send__ :method_missing, sym, *args, &block
107
+ end
108
+ end
109
+
110
+ def raise_unexpected_message_args_error(expectation, *args)
111
+ @error_generator.raise_unexpected_message_args_error expectation, *args
112
+ end
113
+
114
+ def raise_unexpected_message_error(sym, *args)
115
+ @error_generator.raise_unexpected_message_error sym, *args
116
+ end
117
+
118
+ private
119
+
120
+ def __add(sym)
121
+ $rspec_mocks.add(@target) unless $rspec_mocks.nil?
122
+ define_expected_method(sym)
123
+ end
124
+
125
+ def warn_if_nil_class(sym)
126
+ if proxy_for_nil_class? & @@warn_about_expectations_on_nil
127
+ Kernel.warn("An expectation of :#{sym} was set on nil. Called from #{caller[2]}. Use allow_message_expectations_on_nil to disable warnings.")
128
+ end
129
+ end
130
+
131
+ def define_expected_method(sym)
132
+ unless @proxied_methods.include?(sym)
133
+ visibility_string = "#{visibility(sym)} :#{sym}"
134
+ if target_responds_to?(sym)
135
+ munged_sym = munge(sym)
136
+ target_metaclass.instance_eval do
137
+ alias_method munged_sym, sym if method_defined?(sym)
138
+ end
139
+ @proxied_methods << sym
140
+ end
141
+ target_metaclass.class_eval(<<-EOF, __FILE__, __LINE__)
142
+ def #{sym}(*args, &block)
143
+ __mock_proxy.message_received :#{sym}, *args, &block
144
+ end
145
+ #{visibility_string}
146
+ EOF
147
+ end
148
+ end
149
+
150
+ def target_responds_to?(sym)
151
+ return @target.__send__(munge(:respond_to?),sym) if @already_proxied_respond_to
152
+ return @already_proxied_respond_to = true if sym == :respond_to?
153
+ return @target.respond_to?(sym, true)
154
+ end
155
+
156
+ def visibility(sym)
157
+ if Mock === @target
158
+ 'public'
159
+ elsif target_metaclass.private_method_defined?(sym)
160
+ 'private'
161
+ elsif target_metaclass.protected_method_defined?(sym)
162
+ 'protected'
163
+ else
164
+ 'public'
165
+ end
166
+ end
167
+
168
+ def munge(sym)
169
+ "proxied_by_rspec__#{sym}"
170
+ end
171
+
172
+ def clear_expectations
173
+ @expectations.clear
174
+ end
175
+
176
+ def clear_stubs
177
+ @stubs.clear
178
+ end
179
+
180
+ def clear_proxied_methods
181
+ @proxied_methods.clear
182
+ end
183
+
184
+ def target_metaclass
185
+ class << @target; self; end
186
+ end
187
+
188
+ def verify_expectations
189
+ @expectations.each do |expectation|
190
+ expectation.verify_messages_received
191
+ end
192
+ end
193
+
194
+ def reset_proxied_methods
195
+ @proxied_methods.each do |sym|
196
+ munged_sym = munge(sym)
197
+ target_metaclass.instance_eval do
198
+ remove_method sym
199
+ if method_defined?(munged_sym)
200
+ alias_method sym, munged_sym
201
+ remove_method munged_sym
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ def proxy_for_nil_class?
208
+ @target.nil?
209
+ end
210
+
211
+ def reset_nil_expectations_warning
212
+ @@warn_about_expectations_on_nil = true if proxy_for_nil_class?
213
+ end
214
+
215
+ def find_matching_expectation(sym, *args)
216
+ @expectations.find {|expectation| expectation.matches(sym, args) && !expectation.called_max_times?} ||
217
+ @expectations.find {|expectation| expectation.matches(sym, args)}
218
+ end
219
+
220
+ def find_almost_matching_expectation(sym, *args)
221
+ @expectations.find {|expectation| expectation.matches_name_but_not_args(sym, args)}
222
+ end
223
+
224
+ def find_matching_method_stub(sym, *args)
225
+ @stubs.find {|stub| stub.matches(sym, args)}
226
+ end
227
+
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,28 @@
1
+ module Rspec
2
+ module Mocks
3
+ class Space
4
+ def add(obj)
5
+ mocks << obj unless mocks.detect {|m| m.equal? obj}
6
+ end
7
+
8
+ def verify_all
9
+ mocks.each do |mock|
10
+ mock.rspec_verify
11
+ end
12
+ end
13
+
14
+ def reset_all
15
+ mocks.each do |mock|
16
+ mock.rspec_reset
17
+ end
18
+ mocks.clear
19
+ end
20
+
21
+ private
22
+
23
+ def mocks
24
+ @mocks ||= []
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Rspec
2
+ module Mocks
3
+ module ExampleMethods
4
+ include Rspec::Mocks::ArgumentMatchers
5
+
6
+ # Shortcut for creating an instance of Rspec::Mocks::Mock.
7
+ #
8
+ # +name+ is used for failure reporting, so you should use the
9
+ # role that the mock is playing in the example.
10
+ #
11
+ # +stubs_and_options+ lets you assign options and stub values
12
+ # at the same time. The only option available is :null_object.
13
+ # Anything else is treated as a stub value.
14
+ #
15
+ # == Examples
16
+ #
17
+ # stub_thing = mock("thing", :a => "A")
18
+ # stub_thing.a == "A" => true
19
+ #
20
+ # stub_person = stub("thing", :name => "Joe", :email => "joe@domain.com")
21
+ # stub_person.name => "Joe"
22
+ # stub_person.email => "joe@domain.com"
23
+ def mock(*args)
24
+ Rspec::Mocks::Mock.new(*args)
25
+ end
26
+
27
+ alias :stub :mock
28
+
29
+ # Disables warning messages about expectations being set on nil.
30
+ #
31
+ # By default warning messages are issued when expectations are set on nil. This is to
32
+ # prevent false-positives and to catch potential bugs early on.
33
+ def allow_message_expectations_on_nil
34
+ Proxy.allow_message_expectations_on_nil
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1 @@
1
+ require 'rspec/mocks'
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ module Rspec
4
+ module Mocks
5
+
6
+ describe "AnyNumberOfTimes" do
7
+ before(:each) do
8
+ @mock = Mock.new("test mock")
9
+ end
10
+
11
+ it "should pass if any number of times method is called many times" do
12
+ @mock.should_receive(:random_call).any_number_of_times
13
+ (1..10).each do
14
+ @mock.random_call
15
+ end
16
+ end
17
+
18
+ it "should pass if any number of times method is called once" do
19
+ @mock.should_receive(:random_call).any_number_of_times
20
+ @mock.random_call
21
+ end
22
+
23
+ it "should pass if any number of times method is not called" do
24
+ @mock.should_receive(:random_call).any_number_of_times
25
+ end
26
+
27
+ it "should return the mocked value when called after a similar stub" do
28
+ @mock.stub!(:message).and_return :stub_value
29
+ @mock.should_receive(:message).any_number_of_times.and_return(:mock_value)
30
+ @mock.message.should == :mock_value
31
+ @mock.message.should == :mock_value
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ module Rspec
4
+ module Mocks
5
+ describe ArgumentExpectation do
6
+ it "should consider an object that responds to #matches? and #description to be a matcher" do
7
+ argument_expecatation = Rspec::Mocks::ArgumentExpectation.new([])
8
+ obj = mock("matcher")
9
+ obj.should_receive(:respond_to?).with(:matches?).and_return(true)
10
+ obj.should_receive(:respond_to?).with(:description).and_return(true)
11
+ argument_expecatation.is_matcher?(obj).should be_true
12
+ end
13
+
14
+ it "should NOT consider an object that only responds to #matches? to be a matcher" do
15
+ argument_expecatation = Rspec::Mocks::ArgumentExpectation.new([])
16
+ obj = mock("matcher")
17
+ obj.should_receive(:respond_to?).with(:matches?).and_return(true)
18
+ obj.should_receive(:respond_to?).with(:description).and_return(false)
19
+ argument_expecatation.is_matcher?(obj).should be_false
20
+ end
21
+ end
22
+ end
23
+ end