rspec-mocks 2.0.0.beta.4 → 2.0.0.beta.5

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.
Files changed (43) hide show
  1. data/.autotest +3 -2
  2. data/VERSION +1 -1
  3. data/features/mocks/block_local_expectations.feature +0 -14
  4. data/features/mocks/mix_stubs_and_mocks.feature +2 -2
  5. data/features/mocks/warn_when_expectation_is_set_on_nil.feature +50 -0
  6. data/lib/rspec/mocks.rb +9 -9
  7. data/lib/rspec/mocks/argument_matchers.rb +1 -1
  8. data/lib/rspec/mocks/error_generator.rb +24 -8
  9. data/lib/rspec/mocks/framework.rb +1 -0
  10. data/lib/rspec/mocks/message_expectation.rb +1 -1
  11. data/lib/rspec/mocks/method_double.rb +156 -0
  12. data/lib/rspec/mocks/methods.rb +2 -2
  13. data/lib/rspec/mocks/mock.rb +17 -10
  14. data/lib/rspec/mocks/proxy.rb +71 -158
  15. data/lib/rspec/mocks/spec_methods.rb +19 -3
  16. data/rspec-mocks.gemspec +14 -10
  17. data/spec/rspec/mocks/any_number_of_times_spec.rb +1 -1
  18. data/spec/rspec/mocks/argument_expectation_spec.rb +2 -2
  19. data/spec/rspec/mocks/bug_report_10260_spec.rb +1 -1
  20. data/spec/rspec/mocks/bug_report_10263_spec.rb +8 -8
  21. data/spec/rspec/mocks/bug_report_15719_spec.rb +12 -12
  22. data/spec/rspec/mocks/bug_report_7611_spec.rb +1 -1
  23. data/spec/rspec/mocks/bug_report_7805_spec.rb +1 -1
  24. data/spec/rspec/mocks/bug_report_8165_spec.rb +1 -1
  25. data/spec/rspec/mocks/bug_report_957_spec.rb +1 -1
  26. data/spec/rspec/mocks/double_spec.rb +12 -0
  27. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +7 -6
  28. data/spec/rspec/mocks/mock_ordering_spec.rb +54 -54
  29. data/spec/rspec/mocks/mock_space_spec.rb +4 -4
  30. data/spec/rspec/mocks/mock_spec.rb +55 -51
  31. data/spec/rspec/mocks/multiple_return_value_spec.rb +29 -7
  32. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +8 -8
  33. data/spec/rspec/mocks/null_object_mock_spec.rb +2 -2
  34. data/spec/rspec/mocks/once_counts_spec.rb +1 -1
  35. data/spec/rspec/mocks/options_hash_spec.rb +1 -1
  36. data/spec/rspec/mocks/partial_mock_spec.rb +13 -7
  37. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +1 -1
  38. data/spec/rspec/mocks/precise_counts_spec.rb +1 -1
  39. data/spec/rspec/mocks/record_messages_spec.rb +1 -1
  40. data/spec/rspec/mocks/stub_spec.rb +22 -22
  41. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +12 -12
  42. data/spec/rspec/mocks/twice_counts_spec.rb +1 -1
  43. metadata +13 -9
data/.autotest CHANGED
@@ -1,6 +1,7 @@
1
1
  Autotest.add_hook :initialize do |at|
2
- at.add_mapping(%r%^lib/.*\.rb$%) {
3
- at.files_matching %r%^spec/.*\_spec.rb$%
2
+ at.clear_mappings
3
+ at.add_mapping(%r%\.rb$%) {
4
+ at.files_matching %r%^spec/.*_spec\.rb$%
4
5
  }
5
6
  end
6
7
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0.beta.4
1
+ 2.0.0.beta.5
@@ -1,9 +1,5 @@
1
1
  Feature: block local expectations
2
2
 
3
- In order to set message expectations on ...
4
- As an RSpec user
5
- I want to configure the evaluation context
6
-
7
3
  Background:
8
4
  Given a file named "account.rb" with:
9
5
  """
@@ -22,13 +18,8 @@ Feature: block local expectations
22
18
  """
23
19
  require 'account'
24
20
 
25
- Rspec.configure do |config|
26
- config.mock_framework = :rspec
27
- end
28
-
29
21
  describe "account DSL" do
30
22
  it "it succeeds when the block local receives the given call" do
31
- account = Account.new
32
23
  Account.should_receive(:create).and_yield do |account|
33
24
  account.should_receive(:opening_balance).with(100, :USD)
34
25
  end
@@ -47,13 +38,8 @@ Feature: block local expectations
47
38
  """
48
39
  require 'account'
49
40
 
50
- Rspec.configure do |config|
51
- config.mock_framework = :rspec
52
- end
53
-
54
41
  describe "account DSL" do
55
42
  it "fails when the block local does not receive the expected call" do
56
- account = Account.new
57
43
  Account.should_receive(:create).and_yield do |account|
58
44
  account.should_receive(:opening_balance).with(100, :USD)
59
45
  end
@@ -14,7 +14,7 @@ Feature: Spec and test together
14
14
 
15
15
  describe "a stub in before" do
16
16
  before(:each) do
17
- @messenger = mock('messenger').as_null_object
17
+ @messenger = double('messenger').as_null_object
18
18
  end
19
19
 
20
20
  it "a" do
@@ -25,4 +25,4 @@ Feature: Spec and test together
25
25
  end
26
26
  """
27
27
  When I run "rspec stub_and_mocks_spec.rb -fs"
28
- Then I should see "'messenger' expected :foo with"
28
+ Then I should see "received :foo with unexpected arguments"
@@ -0,0 +1,50 @@
1
+ Feature: warn when expectation is set on nil
2
+
3
+ Scenario: nil instance variable
4
+ Given a file named "example_spec.rb" with:
5
+ """
6
+ Rspec.configure {|c| c.mock_with :rspec}
7
+ describe "something" do
8
+ it "does something" do
9
+ @i_do_not_exist.should_receive(:foo)
10
+ @i_do_not_exist.foo
11
+ end
12
+ end
13
+ """
14
+ When I run "rspec example_spec.rb"
15
+ Then I should see "An expectation of :foo was set on nil"
16
+
17
+ Scenario: allow
18
+ Given a file named "example_spec.rb" with:
19
+ """
20
+ Rspec.configure {|c| c.mock_with :rspec}
21
+ describe "something" do
22
+ it "does something" do
23
+ allow_message_expectations_on_nil
24
+ nil.should_receive(:foo)
25
+ nil.foo
26
+ end
27
+ end
28
+ """
29
+ When I run "rspec example_spec.rb"
30
+ Then I should not see "An expectation"
31
+
32
+ Scenario: allow in one example, but not on another
33
+ Given a file named "example_spec.rb" with:
34
+ """
35
+ Rspec.configure {|c| c.mock_with :rspec}
36
+ describe "something" do
37
+ it "does something (foo)" do
38
+ allow_message_expectations_on_nil
39
+ nil.should_receive(:foo)
40
+ nil.foo
41
+ end
42
+ it "does something (bar)" do
43
+ nil.should_receive(:bar)
44
+ nil.bar
45
+ end
46
+ end
47
+ """
48
+ When I run "rspec example_spec.rb"
49
+ Then I should see "An expectation of :bar"
50
+ And I should not see "An expectation of :foo"
@@ -68,13 +68,13 @@ module Rspec
68
68
  #
69
69
  # You can create a mock in any specification (or setup) using:
70
70
  #
71
- # mock(name, options={})
71
+ # double(name, options={})
72
72
  #
73
73
  # The optional +options+ argument is a +Hash+. Currently the only supported
74
74
  # option is +:null_object+. Setting this to true instructs the mock to ignore
75
75
  # any messages it hasn’t been told to expect – and quietly return itself. For example:
76
76
  #
77
- # mock("person", :null_object => true)
77
+ # double("person", :null_object => true)
78
78
  #
79
79
  # == Creating a Stub
80
80
  #
@@ -93,7 +93,7 @@ module Rspec
93
93
  # mock expectations to existing classes and objects:
94
94
  #
95
95
  # Factory.should_receive(:find).with(id).and_return(value)
96
- # obj.stub!(:to_i).and_return(3)
96
+ # obj.stub(:to_i).and_return(3)
97
97
  # etc ...
98
98
  #
99
99
  # == Expecting Messages
@@ -168,12 +168,12 @@ module Rspec
168
168
  # not support any qualifiers about the message received (i.e. you can't specify arguments
169
169
  # or receive counts):
170
170
  #
171
- # my_mock.stub!(:sym).and_return(value)
172
- # my_mock.stub!(:sym).and_return(value1, value2, value3)
173
- # my_mock.stub!(:sym).and_raise(error)
174
- # my_mock.stub!(:sym).and_throw(:sym)
175
- # my_mock.stub!(:sym).and_yield(values,to,yield)
176
- # my_mock.stub!(:sym).and_yield(values,to,yield).and_yield(some,other,values,this,time)
171
+ # my_mock.stub(:sym).and_return(value)
172
+ # my_mock.stub(:sym).and_return(value1, value2, value3)
173
+ # my_mock.stub(:sym).and_raise(error)
174
+ # my_mock.stub(:sym).and_throw(:sym)
175
+ # my_mock.stub(:sym).and_yield(values,to,yield)
176
+ # my_mock.stub(:sym).and_yield(values,to,yield).and_yield(some,other,values,this,time)
177
177
  #
178
178
  # == Arbitrary Handling
179
179
  #
@@ -172,7 +172,7 @@ module Rspec
172
172
  # == Examples
173
173
  #
174
174
  # array = []
175
- # display = mock('display')
175
+ # display = double('display')
176
176
  # display.should_receive(:present_names).with(duck_type(:length, :each))
177
177
  # => passes
178
178
  def duck_type(*args)
@@ -3,7 +3,8 @@ module Rspec
3
3
  class ErrorGenerator
4
4
  attr_writer :opts
5
5
 
6
- def initialize(target, name)
6
+ def initialize(target, name, options={})
7
+ @declared_as = options[:__declared_as] || 'Mock'
7
8
  @target = target
8
9
  @name = name
9
10
  end
@@ -18,12 +19,18 @@ module Rspec
18
19
 
19
20
  def raise_unexpected_message_args_error(expectation, *args)
20
21
  expected_args = format_args(*expectation.expected_args)
21
- actual_args = args.empty? ? "(no args)" : format_args(*args)
22
- __raise "#{intro} expected #{expectation.sym.inspect} with #{expected_args} but received it with #{actual_args}"
22
+ actual_args = format_args(*args)
23
+ __raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
24
+ end
25
+
26
+ def raise_similar_message_args_error(expectation, *args)
27
+ expected_args = format_args(*expectation.expected_args)
28
+ actual_args = args.collect {|a| format_args(*a)}.join(", ")
29
+ __raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
23
30
  end
24
31
 
25
32
  def raise_expectation_error(sym, expected_received_count, actual_received_count, *args)
26
- __raise "#{intro} expected :#{sym}#{arg_message(*args)} #{count_message(expected_received_count)}, but received it #{count_message(actual_received_count)}"
33
+ __raise "(#{intro}).#{sym}#{format_args(*args)}\n expected: #{count_message(expected_received_count)}\n received: #{count_message(actual_received_count)}"
27
34
  end
28
35
 
29
36
  def raise_out_of_order_error(sym)
@@ -45,7 +52,17 @@ module Rspec
45
52
  private
46
53
 
47
54
  def intro
48
- @name ? "Mock '#{@name}'" : @target.class == Class ? "<#{@target.inspect} (class)>" : (@target.nil? ? "nil" : @target)
55
+ if @name
56
+ "#{@declared_as} #{@name.inspect}"
57
+ elsif Mock === @target
58
+ @declared_as
59
+ elsif Class === @target
60
+ "<#{@target.inspect} (class)>"
61
+ elsif @target
62
+ @target
63
+ else
64
+ "nil"
65
+ end
49
66
  end
50
67
 
51
68
  def __raise(message)
@@ -71,11 +88,10 @@ module Rspec
71
88
  end
72
89
 
73
90
  def pretty_print(count)
74
- return "once" if count == 1
75
- return "twice" if count == 2
76
- return "#{count} times"
91
+ "#{count} time#{count == 1 ? '' : 's'}"
77
92
  end
78
93
 
79
94
  end
80
95
  end
81
96
  end
97
+
@@ -2,6 +2,7 @@
2
2
  # supports wrapping rspec's mocking functionality without invading every
3
3
  # object in the system.
4
4
 
5
+ require 'rspec/mocks/method_double'
5
6
  require 'rspec/mocks/methods'
6
7
  require 'rspec/mocks/argument_matchers'
7
8
  require 'rspec/mocks/spec_methods'
@@ -244,7 +244,7 @@ module Rspec
244
244
  if similar_messages.empty?
245
245
  @error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *@args_expectation.args)
246
246
  else
247
- @error_generator.raise_unexpected_message_args_error(self, *@similar_messages)
247
+ @error_generator.raise_similar_message_args_error(self, *@similar_messages)
248
248
  end
249
249
  end
250
250
 
@@ -0,0 +1,156 @@
1
+ module Rspec
2
+ module Mocks
3
+ class MethodDouble < Hash
4
+ attr_reader :method_name
5
+
6
+ def initialize(object, method_name, proxy)
7
+ @method_name = method_name
8
+ @object = object
9
+ @proxy = proxy
10
+ @stashed = false
11
+ store(:expectations, [])
12
+ store(:stubs, [])
13
+ end
14
+
15
+ def expectations
16
+ self[:expectations]
17
+ end
18
+
19
+ def stubs
20
+ self[:stubs]
21
+ end
22
+
23
+ def visibility
24
+ if Mock === @object
25
+ 'public'
26
+ elsif object_singleton_class.private_method_defined?(@method_name)
27
+ 'private'
28
+ elsif object_singleton_class.protected_method_defined?(@method_name)
29
+ 'protected'
30
+ else
31
+ 'public'
32
+ end
33
+ end
34
+
35
+ def object_singleton_class
36
+ class << @object; self; end
37
+ end
38
+
39
+ def obfuscate(method_name)
40
+ "obfuscated_by_rspec_mocks__#{method_name}"
41
+ end
42
+
43
+ def stashed_method_name
44
+ obfuscate(method_name)
45
+ end
46
+
47
+ def object_responds_to?(method_name)
48
+ if @proxy.already_proxied_respond_to?
49
+ @object.__send__(obfuscate(:respond_to?), method_name)
50
+ elsif method_name == :respond_to?
51
+ @proxy.already_proxied_respond_to
52
+ else
53
+ @object.respond_to?(method_name, true)
54
+ end
55
+ end
56
+
57
+ def configure_method
58
+ $rspec_mocks.add(@object) if $rspec_mocks
59
+ warn_if_nil_class
60
+ stash_original_method
61
+ define_proxy_method
62
+ end
63
+
64
+ def stash_original_method
65
+ if object_responds_to?(@method_name)
66
+ stashed = stashed_method_name
67
+ orig = @method_name
68
+ object_singleton_class.class_eval do
69
+ alias_method(stashed, orig) if method_defined?(orig)
70
+ end
71
+ @stashed = true
72
+ end
73
+ end
74
+
75
+ def define_proxy_method
76
+ method_name = @method_name
77
+ visibility_for_method = "#{visibility} :#{method_name}"
78
+ object_singleton_class.class_eval(<<-EOF, __FILE__, __LINE__)
79
+ def #{method_name}(*args, &block)
80
+ __mock_proxy.message_received :#{method_name}, *args, &block
81
+ end
82
+ #{visibility_for_method}
83
+ EOF
84
+ end
85
+
86
+ def restore_original_method
87
+ if @stashed
88
+ method_name = @method_name
89
+ stashed_method_name = self.stashed_method_name
90
+ object_singleton_class.instance_eval do
91
+ remove_method method_name
92
+ if method_defined?(stashed_method_name)
93
+ alias_method method_name, stashed_method_name
94
+ remove_method stashed_method_name
95
+ end
96
+ end
97
+ @stashed = false
98
+ end
99
+ end
100
+
101
+ def verify
102
+ expectations.each {|e| e.verify_messages_received}
103
+ end
104
+
105
+ def reset
106
+ reset_nil_expectations_warning
107
+ restore_original_method
108
+ clear
109
+ end
110
+
111
+ def clear
112
+ expectations.clear
113
+ stubs.clear
114
+ end
115
+
116
+ def add_expectation(error_generator, expectation_ordering, expected_from, opts, &block)
117
+ configure_method
118
+ expectation = if existing_stub = stubs.first
119
+ existing_stub.build_child(expected_from, block, 1, opts)
120
+ else
121
+ MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, block, 1, opts)
122
+ end
123
+ expectations << expectation
124
+ expectation
125
+ end
126
+
127
+ def add_negative_expectation(error_generator, expectation_ordering, expected_from, &implementation)
128
+ configure_method
129
+ expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, implementation)
130
+ expectations << expectation
131
+ expectation
132
+ end
133
+
134
+ def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
135
+ configure_method
136
+ stub = MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, nil, :any, opts, &implementation)
137
+ stubs << stub
138
+ stub
139
+ end
140
+
141
+ def proxy_for_nil_class?
142
+ @object.nil?
143
+ end
144
+
145
+ def warn_if_nil_class
146
+ if proxy_for_nil_class? & Rspec::Mocks::Proxy.warn_about_expectations_on_nil
147
+ Kernel.warn("An expectation of :#{@method_name} was set on nil. Called from #{caller[4]}. Use allow_message_expectations_on_nil to disable warnings.")
148
+ end
149
+ end
150
+
151
+ def reset_nil_expectations_warning
152
+ Rspec::Mocks::Proxy.warn_about_expectations_on_nil = true if proxy_for_nil_class?
153
+ end
154
+ end
155
+ end
156
+ end
@@ -9,7 +9,7 @@ module Rspec
9
9
  __mock_proxy.add_negative_message_expectation(caller(1)[0], sym.to_sym, &block)
10
10
  end
11
11
 
12
- def stub!(sym_or_hash, opts={}, &block)
12
+ def stub(sym_or_hash, opts={}, &block)
13
13
  if Hash === sym_or_hash
14
14
  sym_or_hash.each {|method, value| stub!(method).and_return value }
15
15
  else
@@ -17,7 +17,7 @@ module Rspec
17
17
  end
18
18
  end
19
19
 
20
- alias_method :stub, :stub!
20
+ alias_method :stub!, :stub
21
21
 
22
22
  def stub_chain(*methods)
23
23
  if methods.length > 1
@@ -7,14 +7,14 @@ module Rspec
7
7
  # only) == Options:
8
8
  # * <tt>:null_object</tt> - if true, the mock object acts as a forgiving
9
9
  # null object allowing any message to be sent to it.
10
- def initialize(name='mock', stubs_and_options={})
10
+ def initialize(name=nil, stubs_and_options={})
11
11
  if name.is_a?(Hash) && stubs_and_options.empty?
12
12
  stubs_and_options = name
13
- build_name_from_options stubs_and_options
13
+ @name = nil
14
14
  else
15
15
  @name = name
16
16
  end
17
- @options = parse_options(stubs_and_options)
17
+ @options = extract_options(stubs_and_options)
18
18
  assign_stubs(stubs_and_options)
19
19
  end
20
20
 
@@ -46,8 +46,19 @@ module Rspec
46
46
  end
47
47
  end
48
48
 
49
- def parse_options(options)
50
- options.has_key?(:null_object) ? {:null_object => options.delete(:null_object)} : {}
49
+ def extract_options(stubs_and_options)
50
+ options = {}
51
+ extract_option(stubs_and_options, options, :null_object)
52
+ extract_option(stubs_and_options, options, :__declared_as, 'Mock')
53
+ options
54
+ end
55
+
56
+ def extract_option(source, target, key, default=nil)
57
+ if source[key]
58
+ target[key] = source.delete(key)
59
+ elsif default
60
+ target[key] = default
61
+ end
51
62
  end
52
63
 
53
64
  def assign_stubs(stubs)
@@ -55,11 +66,7 @@ module Rspec
55
66
  stub!(message).and_return(response)
56
67
  end
57
68
  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
69
  end
64
70
  end
65
71
  end
72
+