rspec-mocks 2.6.0 → 2.7.0.rc1

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 (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
@@ -0,0 +1,49 @@
1
+ module RSpec
2
+ module Mocks
3
+ 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
+ end
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ class ExpectationChain < Chain
5
+ def initialize(*args, &block)
6
+ record(:should_receive, *args, &block)
7
+ @expectation_fulfilled = false
8
+ end
9
+
10
+ def invocation_order
11
+ @invocation_order ||= {
12
+ :should_receive => [nil],
13
+ :with => [:should_receive],
14
+ :and_return => [:with, :should_receive],
15
+ :and_raise => [:with, :should_receive]
16
+ }
17
+ end
18
+
19
+ def expectation_fulfilled!
20
+ @expectation_fulfilled = true
21
+ end
22
+
23
+ def expectation_fulfilled?
24
+ @expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
25
+ end
26
+
27
+ private
28
+ def verify_invocation_order(rspec_method_name, *args, &block)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,48 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ class MessageChains < Hash
5
+ def add(method_name, chain)
6
+ (self[method_name] ||= []) << chain
7
+ end
8
+
9
+ def remove_stub_chains_for!(method_name)
10
+ chains = self[method_name]
11
+ chains.reject! { |chain| chain.is_a?(StubChain) || chain.is_a?(StubChainChain) }
12
+ end
13
+
14
+ def has_expectation?(method_name)
15
+ !!self[method_name].find{|chain| chain.is_a?(ExpectationChain)}
16
+ end
17
+
18
+ def each_expectation_fulfilled?
19
+ self.all? do |method_name, chains|
20
+ chains.all? { |chain| chain.expectation_fulfilled? }
21
+ end
22
+ end
23
+
24
+ def unfulfilled_expectations
25
+ self.map do |method_name, chains|
26
+ method_name.to_s if chains.last.is_a?(ExpectationChain) unless chains.last.expectation_fulfilled?
27
+ end.compact
28
+ end
29
+
30
+ def received_expected_message!(method_name)
31
+ self[method_name].each do |chain|
32
+ chain.expectation_fulfilled!
33
+ end
34
+ end
35
+
36
+ def playback!(instance, method_name)
37
+ self[method_name].each do |chain|
38
+ @instance_with_expectation = instance if instance.is_a?(ExpectationChain) && !@instance_with_expectation
39
+ if instance.is_a?(ExpectationChain) && !@instance_with_expectation.equal?(instance)
40
+ raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
41
+ end
42
+ chain.playback!(instance)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,162 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ class Recorder
5
+ attr_reader :message_chains
6
+ def initialize(klass)
7
+ @message_chains = MessageChains.new
8
+ @observed_methods = []
9
+ @played_methods = {}
10
+ @klass = klass
11
+ @expectation_set = false
12
+ end
13
+
14
+ def unstub(method_name)
15
+ unless @observed_methods.include?(method_name.to_sym)
16
+ raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
17
+ end
18
+ message_chains.remove_stub_chains_for!(method_name)
19
+ stop_observing!(method_name) unless message_chains.has_expectation?(method_name)
20
+ end
21
+
22
+ def stub(method_name_or_method_map, *args, &block)
23
+ if method_name_or_method_map.is_a?(Hash)
24
+ method_map = method_name_or_method_map
25
+ method_map.each do |method_name, return_value|
26
+ observe!(method_name)
27
+ message_chains.add(method_name, chain = StubChain.new(method_name))
28
+ chain.and_return(return_value)
29
+ end
30
+ method_map
31
+ else
32
+ method_name = method_name_or_method_map
33
+ observe!(method_name)
34
+ message_chains.add(method_name, chain = StubChain.new(method_name, *args, &block))
35
+ chain
36
+ end
37
+ end
38
+
39
+ def stub_chain(method_name_or_string_chain, *args, &block)
40
+ if period_separated_method_chain?(method_name_or_string_chain)
41
+ first_method_name = method_name_or_string_chain.split('.').first.to_sym
42
+ else
43
+ first_method_name = method_name_or_string_chain
44
+ end
45
+ observe!(first_method_name)
46
+ message_chains.add(first_method_name, chain = StubChainChain.new(method_name_or_string_chain, *args, &block))
47
+ chain
48
+ end
49
+
50
+ def should_receive(method_name, *args, &block)
51
+ observe!(method_name)
52
+ @expectation_set = true
53
+ message_chains.add(method_name, chain = ExpectationChain.new(method_name, *args, &block))
54
+ chain
55
+ end
56
+
57
+ def stop_all_observation!
58
+ @observed_methods.each do |method_name|
59
+ restore_method!(method_name)
60
+ end
61
+ end
62
+
63
+ def playback!(instance, method_name)
64
+ RSpec::Mocks::space.add(instance)
65
+ message_chains.playback!(instance, method_name)
66
+ @played_methods[method_name] = instance
67
+ received_expected_message!(method_name) if message_chains.has_expectation?(method_name)
68
+ end
69
+
70
+ def instance_that_received(method_name)
71
+ @played_methods[method_name]
72
+ end
73
+
74
+ def verify
75
+ if @expectation_set && !message_chains.each_expectation_fulfilled?
76
+ raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
77
+ end
78
+ end
79
+
80
+ private
81
+ def period_separated_method_chain?(method_name)
82
+ method_name.is_a?(String) && method_name.include?('.')
83
+ end
84
+
85
+ def received_expected_message!(method_name)
86
+ message_chains.received_expected_message!(method_name)
87
+ restore_method!(method_name)
88
+ mark_invoked!(method_name)
89
+ end
90
+
91
+ def restore_method!(method_name)
92
+ if @klass.method_defined?(build_alias_method_name(method_name))
93
+ restore_original_method!(method_name)
94
+ else
95
+ remove_dummy_method!(method_name)
96
+ end
97
+ end
98
+
99
+ def build_alias_method_name(method_name)
100
+ "__#{method_name}_without_any_instance__"
101
+ end
102
+
103
+ def restore_original_method!(method_name)
104
+ alias_method_name = build_alias_method_name(method_name)
105
+ @klass.class_eval do
106
+ remove_method method_name
107
+ alias_method method_name, alias_method_name
108
+ remove_method alias_method_name
109
+ end
110
+ end
111
+
112
+ def remove_dummy_method!(method_name)
113
+ @klass.class_eval do
114
+ remove_method method_name
115
+ end
116
+ end
117
+
118
+ def backup_method!(method_name)
119
+ alias_method_name = build_alias_method_name(method_name)
120
+ @klass.class_eval do
121
+ if method_defined?(method_name)
122
+ alias_method alias_method_name, method_name
123
+ end
124
+ end
125
+ end
126
+
127
+ def stop_observing!(method_name)
128
+ restore_method!(method_name)
129
+ @observed_methods.delete(method_name)
130
+ end
131
+
132
+ def already_observing?(method_name)
133
+ @observed_methods.include?(method_name)
134
+ end
135
+
136
+ def observe!(method_name)
137
+ stop_observing!(method_name) if already_observing?(method_name)
138
+ @observed_methods << method_name
139
+ backup_method!(method_name)
140
+ @klass.class_eval(<<-EOM, __FILE__, __LINE__)
141
+ def #{method_name}(*args, &blk)
142
+ self.class.__recorder.playback!(self, :#{method_name})
143
+ self.__send__(:#{method_name}, *args, &blk)
144
+ end
145
+ EOM
146
+ end
147
+
148
+ def mark_invoked!(method_name)
149
+ backup_method!(method_name)
150
+ @klass.class_eval(<<-EOM, __FILE__, __LINE__)
151
+ def #{method_name}(*args, &blk)
152
+ method_name = :#{method_name}
153
+ current_instance = self
154
+ invoked_instance = self.class.__recorder.instance_that_received(method_name)
155
+ raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by \#{self.inspect} but has already been received by \#{invoked_instance}"
156
+ end
157
+ EOM
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,35 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ class StubChain < Chain
5
+ def initialize(*args, &block)
6
+ record(:stub, *args, &block)
7
+ end
8
+
9
+ def invocation_order
10
+ @invocation_order ||= {
11
+ :stub => [nil],
12
+ :with => [:stub],
13
+ :and_return => [:with, :stub],
14
+ :and_raise => [:with, :stub],
15
+ :and_yield => [:with, :stub]
16
+ }
17
+ end
18
+
19
+ def expectation_fulfilled?
20
+ true
21
+ end
22
+
23
+ def expectation_fulfilled!
24
+ end
25
+
26
+ private
27
+ def verify_invocation_order(rspec_method_name, *args, &block)
28
+ unless invocation_order[rspec_method_name].include?(last_message)
29
+ raise(NoMethodError, "Undefined method #{rspec_method_name}")
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ class StubChainChain < Chain
5
+ def initialize(*args, &block)
6
+ record(:stub_chain, *args, &block)
7
+ end
8
+
9
+ def invocation_order
10
+ @invocation_order ||= {
11
+ :stub_chain => [nil],
12
+ :and_return => [:stub_chain],
13
+ :and_raise => [:stub_chain],
14
+ :and_yield => [:stub_chain]
15
+ }
16
+ end
17
+
18
+ def expectation_fulfilled?
19
+ true
20
+ end
21
+
22
+ def expectation_fulfilled!
23
+ end
24
+
25
+ private
26
+ def verify_invocation_order(rspec_method_name, *args, &block)
27
+ unless invocation_order[rspec_method_name].include?(last_message)
28
+ raise(NoMethodError, "Undefined method #{rspec_method_name}")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -26,7 +26,7 @@ module RSpec
26
26
  end
27
27
 
28
28
  def is_matcher?(obj)
29
- !null_object?(obj) & obj.respond_to?(:matches?) & obj.respond_to?(:description)
29
+ !null_object?(obj) & obj.respond_to?(:matches?) & [:failure_message_for_should, :failure_message].any? { |m| obj.respond_to?(m) }
30
30
  end
31
31
 
32
32
  def args_match?(*args)
@@ -7,7 +7,7 @@ module Marshal
7
7
  mp = object.instance_variable_get(:@mock_proxy)
8
8
  return dump_without_mocks(*args.unshift(object)) unless mp.is_a?(::RSpec::Mocks::Proxy)
9
9
 
10
- object.send(:remove_instance_variable, :@mock_proxy)
10
+ object.__send__(:remove_instance_variable, :@mock_proxy)
11
11
 
12
12
  begin
13
13
  dump_without_mocks(*args.unshift(object.dup))
@@ -7,7 +7,7 @@ if defined?(Psych) && Psych.respond_to?(:dump)
7
7
  mp = object.instance_variable_get(:@mock_proxy)
8
8
  return dump_without_mocks(object, *args) unless mp.is_a?(::RSpec::Mocks::Proxy)
9
9
 
10
- object.send(:remove_instance_variable, :@mock_proxy)
10
+ object.__send__(:remove_instance_variable, :@mock_proxy)
11
11
 
12
12
  begin
13
13
  dump_without_mocks(object, *args)
@@ -11,7 +11,7 @@ module RSpec
11
11
 
12
12
  def stub(sym_or_hash, opts={}, &block)
13
13
  if Hash === sym_or_hash
14
- sym_or_hash.each {|method, value| stub!(method).and_return value }
14
+ sym_or_hash.each {|method, value| stub(method).and_return value }
15
15
  else
16
16
  __mock_proxy.add_stub(caller(1)[0], sym_or_hash.to_sym, opts, &block)
17
17
  end
@@ -35,7 +35,7 @@ module RSpec
35
35
  alias_method :to_str, :to_s
36
36
 
37
37
  def respond_to?(sym, incl_private=false)
38
- __mock_proxy.null_object? ? true : super
38
+ __mock_proxy.null_object? && sym != :to_ary ? true : super
39
39
  end
40
40
 
41
41
  private
@@ -70,7 +70,7 @@ module RSpec
70
70
 
71
71
  def assign_stubs(stubs)
72
72
  stubs.each_pair do |message, response|
73
- stub!(message).and_return(response)
73
+ stub(message).and_return(response)
74
74
  end
75
75
  end
76
76
  end
@@ -110,7 +110,7 @@ module RSpec
110
110
  stub.advise(*args)
111
111
  raise_unexpected_message_args_error(stub, *args)
112
112
  elsif @object.is_a?(Class)
113
- @object.superclass.send(method_name, *args, &block)
113
+ @object.superclass.__send__(method_name, *args, &block)
114
114
  else
115
115
  @object.__send__(:method_missing, method_name, *args, &block)
116
116
  end
@@ -1,7 +1,7 @@
1
1
  module RSpec # :nodoc:
2
2
  module Mocks # :nodoc:
3
3
  module Version # :nodoc:
4
- STRING = '2.6.0'
4
+ STRING = '2.7.0.rc1'
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe RSpec::Mocks::AnyInstance::MessageChains do
4
+ let(:chains) { RSpec::Mocks::AnyInstance::MessageChains.new }
5
+ let(:stub_chain) { RSpec::Mocks::AnyInstance::StubChain.new }
6
+ let(:expectation_chain) { RSpec::Mocks::AnyInstance::ExpectationChain.new }
7
+
8
+ it "knows if a method does not have an expectation set on it" do
9
+ chains.add(:method_name, stub_chain)
10
+ chains.has_expectation?(:method_name).should be_false
11
+ end
12
+
13
+ it "knows if a method has an expectation set on it" do
14
+ chains.add(:method_name, stub_chain)
15
+ chains.add(:method_name, expectation_chain)
16
+ chains.has_expectation?(:method_name).should be_true
17
+ end
18
+
19
+ it "can remove all stub chains" do
20
+ chains.add(:method_name, stub_chain)
21
+ chains.add(:method_name, expectation_chain)
22
+ chains.add(:method_name, another_stub_chain = RSpec::Mocks::AnyInstance::StubChain.new)
23
+
24
+ chains.remove_stub_chains_for!(:method_name)
25
+ chains[:method_name].should eq([expectation_chain])
26
+ end
27
+
28
+ context "creating stub chains" do
29
+ it "understands how to add a stub chain for a method" do
30
+ chains.add(:method_name, stub_chain)
31
+ chains[:method_name].should eq([stub_chain])
32
+ end
33
+
34
+ it "allows multiple stub chains for a method" do
35
+ chains.add(:method_name, stub_chain)
36
+ chains.add(:method_name, another_stub_chain = RSpec::Mocks::AnyInstance::StubChain.new)
37
+ chains[:method_name].should eq([stub_chain, another_stub_chain])
38
+ end
39
+ end
40
+ end