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

Sign up to get free protection for your applications and to get access to all the features.
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
+