mocha-macruby 0.9.8.20100129120100

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 (172) hide show
  1. data/COPYING +3 -0
  2. data/MIT-LICENSE +7 -0
  3. data/README +39 -0
  4. data/RELEASE +294 -0
  5. data/Rakefile +214 -0
  6. data/examples/misc.rb +43 -0
  7. data/examples/mocha.rb +25 -0
  8. data/examples/stubba.rb +64 -0
  9. data/lib/mocha.rb +3 -0
  10. data/lib/mocha/any_instance_method.rb +59 -0
  11. data/lib/mocha/api.rb +173 -0
  12. data/lib/mocha/argument_iterator.rb +21 -0
  13. data/lib/mocha/backtrace_filter.rb +17 -0
  14. data/lib/mocha/cardinality.rb +95 -0
  15. data/lib/mocha/central.rb +27 -0
  16. data/lib/mocha/change_state_side_effect.rb +19 -0
  17. data/lib/mocha/class_method.rb +117 -0
  18. data/lib/mocha/configuration.rb +79 -0
  19. data/lib/mocha/deprecation.rb +22 -0
  20. data/lib/mocha/exception_raiser.rb +17 -0
  21. data/lib/mocha/expectation.rb +476 -0
  22. data/lib/mocha/expectation_error.rb +15 -0
  23. data/lib/mocha/expectation_list.rb +50 -0
  24. data/lib/mocha/in_state_ordering_constraint.rb +19 -0
  25. data/lib/mocha/inspect.rb +67 -0
  26. data/lib/mocha/instance_method.rb +16 -0
  27. data/lib/mocha/integration.rb +38 -0
  28. data/lib/mocha/integration/mini_test.rb +21 -0
  29. data/lib/mocha/integration/mini_test/assertion_counter.rb +23 -0
  30. data/lib/mocha/integration/mini_test/version_131_and_above.rb +50 -0
  31. data/lib/mocha/integration/test_unit.rb +40 -0
  32. data/lib/mocha/integration/test_unit/assertion_counter.rb +23 -0
  33. data/lib/mocha/integration/test_unit/gem_version_200.rb +49 -0
  34. data/lib/mocha/integration/test_unit/gem_version_201_and_above.rb +49 -0
  35. data/lib/mocha/integration/test_unit/ruby_version_185_and_below.rb +48 -0
  36. data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +50 -0
  37. data/lib/mocha/is_a.rb +9 -0
  38. data/lib/mocha/logger.rb +15 -0
  39. data/lib/mocha/metaclass.rb +13 -0
  40. data/lib/mocha/method_matcher.rb +21 -0
  41. data/lib/mocha/mock.rb +200 -0
  42. data/lib/mocha/mockery.rb +181 -0
  43. data/lib/mocha/module_method.rb +16 -0
  44. data/lib/mocha/multiple_yields.rb +20 -0
  45. data/lib/mocha/names.rb +53 -0
  46. data/lib/mocha/no_yields.rb +11 -0
  47. data/lib/mocha/object.rb +187 -0
  48. data/lib/mocha/parameter_matchers.rb +27 -0
  49. data/lib/mocha/parameter_matchers/all_of.rb +42 -0
  50. data/lib/mocha/parameter_matchers/any_of.rb +47 -0
  51. data/lib/mocha/parameter_matchers/any_parameters.rb +40 -0
  52. data/lib/mocha/parameter_matchers/anything.rb +33 -0
  53. data/lib/mocha/parameter_matchers/base.rb +15 -0
  54. data/lib/mocha/parameter_matchers/equals.rb +42 -0
  55. data/lib/mocha/parameter_matchers/has_entries.rb +45 -0
  56. data/lib/mocha/parameter_matchers/has_entry.rb +57 -0
  57. data/lib/mocha/parameter_matchers/has_key.rb +43 -0
  58. data/lib/mocha/parameter_matchers/has_value.rb +43 -0
  59. data/lib/mocha/parameter_matchers/includes.rb +41 -0
  60. data/lib/mocha/parameter_matchers/instance_of.rb +42 -0
  61. data/lib/mocha/parameter_matchers/is_a.rb +42 -0
  62. data/lib/mocha/parameter_matchers/kind_of.rb +42 -0
  63. data/lib/mocha/parameter_matchers/not.rb +42 -0
  64. data/lib/mocha/parameter_matchers/object.rb +15 -0
  65. data/lib/mocha/parameter_matchers/optionally.rb +55 -0
  66. data/lib/mocha/parameter_matchers/regexp_matches.rb +44 -0
  67. data/lib/mocha/parameter_matchers/responds_with.rb +43 -0
  68. data/lib/mocha/parameter_matchers/yaml_equivalent.rb +43 -0
  69. data/lib/mocha/parameters_matcher.rb +37 -0
  70. data/lib/mocha/pretty_parameters.rb +28 -0
  71. data/lib/mocha/return_values.rb +31 -0
  72. data/lib/mocha/sequence.rb +42 -0
  73. data/lib/mocha/single_return_value.rb +17 -0
  74. data/lib/mocha/single_yield.rb +18 -0
  75. data/lib/mocha/standalone.rb +1 -0
  76. data/lib/mocha/state_machine.rb +91 -0
  77. data/lib/mocha/stubbing_error.rb +16 -0
  78. data/lib/mocha/unexpected_invocation.rb +18 -0
  79. data/lib/mocha/yield_parameters.rb +31 -0
  80. data/lib/mocha_standalone.rb +2 -0
  81. data/lib/stubba.rb +4 -0
  82. data/test/acceptance/acceptance_test_helper.rb +38 -0
  83. data/test/acceptance/api_test.rb +139 -0
  84. data/test/acceptance/bug_18914_test.rb +43 -0
  85. data/test/acceptance/bug_21465_test.rb +34 -0
  86. data/test/acceptance/bug_21563_test.rb +25 -0
  87. data/test/acceptance/expected_invocation_count_test.rb +196 -0
  88. data/test/acceptance/failure_messages_test.rb +64 -0
  89. data/test/acceptance/minitest_test.rb +153 -0
  90. data/test/acceptance/mocha_example_test.rb +98 -0
  91. data/test/acceptance/mocha_test_result_test.rb +84 -0
  92. data/test/acceptance/mock_test.rb +100 -0
  93. data/test/acceptance/mock_with_initializer_block_test.rb +51 -0
  94. data/test/acceptance/mocked_methods_dispatch_test.rb +78 -0
  95. data/test/acceptance/optional_parameters_test.rb +70 -0
  96. data/test/acceptance/parameter_matcher_test.rb +209 -0
  97. data/test/acceptance/partial_mocks_test.rb +47 -0
  98. data/test/acceptance/return_value_test.rb +52 -0
  99. data/test/acceptance/sequence_test.rb +186 -0
  100. data/test/acceptance/states_test.rb +70 -0
  101. data/test/acceptance/stub_any_instance_method_test.rb +195 -0
  102. data/test/acceptance/stub_class_method_test.rb +203 -0
  103. data/test/acceptance/stub_everything_test.rb +56 -0
  104. data/test/acceptance/stub_instance_method_test.rb +203 -0
  105. data/test/acceptance/stub_module_method_test.rb +163 -0
  106. data/test/acceptance/stub_test.rb +52 -0
  107. data/test/acceptance/stubba_example_test.rb +102 -0
  108. data/test/acceptance/stubba_test.rb +15 -0
  109. data/test/acceptance/stubba_test_result_test.rb +66 -0
  110. data/test/acceptance/stubbing_error_backtrace_test.rb +64 -0
  111. data/test/acceptance/stubbing_method_unnecessarily_test.rb +65 -0
  112. data/test/acceptance/stubbing_non_existent_any_instance_method_test.rb +130 -0
  113. data/test/acceptance/stubbing_non_existent_class_method_test.rb +157 -0
  114. data/test/acceptance/stubbing_non_existent_instance_method_test.rb +147 -0
  115. data/test/acceptance/stubbing_non_public_any_instance_method_test.rb +130 -0
  116. data/test/acceptance/stubbing_non_public_class_method_test.rb +163 -0
  117. data/test/acceptance/stubbing_non_public_instance_method_test.rb +143 -0
  118. data/test/acceptance/stubbing_on_non_mock_object_test.rb +64 -0
  119. data/test/deprecation_disabler.rb +15 -0
  120. data/test/execution_point.rb +36 -0
  121. data/test/method_definer.rb +24 -0
  122. data/test/simple_counter.rb +13 -0
  123. data/test/test_helper.rb +25 -0
  124. data/test/test_runner.rb +33 -0
  125. data/test/unit/any_instance_method_test.rb +126 -0
  126. data/test/unit/array_inspect_test.rb +16 -0
  127. data/test/unit/backtrace_filter_test.rb +19 -0
  128. data/test/unit/cardinality_test.rb +56 -0
  129. data/test/unit/central_test.rb +65 -0
  130. data/test/unit/change_state_side_effect_test.rb +41 -0
  131. data/test/unit/class_method_test.rb +295 -0
  132. data/test/unit/configuration_test.rb +38 -0
  133. data/test/unit/date_time_inspect_test.rb +21 -0
  134. data/test/unit/exception_raiser_test.rb +42 -0
  135. data/test/unit/expectation_list_test.rb +57 -0
  136. data/test/unit/expectation_test.rb +480 -0
  137. data/test/unit/hash_inspect_test.rb +16 -0
  138. data/test/unit/in_state_ordering_constraint_test.rb +43 -0
  139. data/test/unit/metaclass_test.rb +22 -0
  140. data/test/unit/method_matcher_test.rb +23 -0
  141. data/test/unit/mock_test.rb +302 -0
  142. data/test/unit/mockery_test.rb +149 -0
  143. data/test/unit/multiple_yields_test.rb +18 -0
  144. data/test/unit/no_yields_test.rb +18 -0
  145. data/test/unit/object_inspect_test.rb +37 -0
  146. data/test/unit/object_test.rb +82 -0
  147. data/test/unit/parameter_matchers/all_of_test.rb +26 -0
  148. data/test/unit/parameter_matchers/any_of_test.rb +26 -0
  149. data/test/unit/parameter_matchers/anything_test.rb +21 -0
  150. data/test/unit/parameter_matchers/equals_test.rb +25 -0
  151. data/test/unit/parameter_matchers/has_entries_test.rb +51 -0
  152. data/test/unit/parameter_matchers/has_entry_test.rb +82 -0
  153. data/test/unit/parameter_matchers/has_key_test.rb +55 -0
  154. data/test/unit/parameter_matchers/has_value_test.rb +57 -0
  155. data/test/unit/parameter_matchers/includes_test.rb +44 -0
  156. data/test/unit/parameter_matchers/instance_of_test.rb +25 -0
  157. data/test/unit/parameter_matchers/is_a_test.rb +25 -0
  158. data/test/unit/parameter_matchers/kind_of_test.rb +25 -0
  159. data/test/unit/parameter_matchers/not_test.rb +26 -0
  160. data/test/unit/parameter_matchers/regexp_matches_test.rb +46 -0
  161. data/test/unit/parameter_matchers/responds_with_test.rb +25 -0
  162. data/test/unit/parameter_matchers/stub_matcher.rb +27 -0
  163. data/test/unit/parameter_matchers/yaml_equivalent_test.rb +25 -0
  164. data/test/unit/parameters_matcher_test.rb +121 -0
  165. data/test/unit/return_values_test.rb +63 -0
  166. data/test/unit/sequence_test.rb +104 -0
  167. data/test/unit/single_return_value_test.rb +14 -0
  168. data/test/unit/single_yield_test.rb +18 -0
  169. data/test/unit/state_machine_test.rb +98 -0
  170. data/test/unit/string_inspect_test.rb +11 -0
  171. data/test/unit/yield_parameters_test.rb +93 -0
  172. metadata +240 -0
@@ -0,0 +1,27 @@
1
+ module Mocha
2
+
3
+ class Central
4
+
5
+ attr_accessor :stubba_methods
6
+
7
+ def initialize
8
+ self.stubba_methods = []
9
+ end
10
+
11
+ def stub(method)
12
+ unless stubba_methods.include?(method)
13
+ method.stub
14
+ stubba_methods.push(method)
15
+ end
16
+ end
17
+
18
+ def unstub_all
19
+ while stubba_methods.length > 0
20
+ method = stubba_methods.pop
21
+ method.unstub
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,19 @@
1
+ module Mocha
2
+
3
+ class ChangeStateSideEffect
4
+
5
+ def initialize(state)
6
+ @state = state
7
+ end
8
+
9
+ def perform
10
+ @state.activate
11
+ end
12
+
13
+ def mocha_inspect
14
+ "then #{@state.mocha_inspect}"
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,117 @@
1
+ require 'mocha/metaclass'
2
+
3
+ module Mocha
4
+
5
+ class ClassMethod
6
+
7
+ attr_reader :stubbee, :method
8
+
9
+ def initialize(stubbee, method)
10
+ @stubbee = stubbee
11
+ @method = RUBY_VERSION < '1.9' ? method.to_s : method.to_sym
12
+ end
13
+
14
+ def stub
15
+ hide_original_method
16
+ define_new_method
17
+ end
18
+
19
+ def unstub
20
+ remove_new_method
21
+ restore_original_method
22
+ stubbee.reset_mocha
23
+ end
24
+
25
+ def mock
26
+ stubbee.mocha
27
+ end
28
+
29
+ def hide_original_method
30
+ if method_exists?(method)
31
+ begin
32
+ stubbee.__metaclass__.send(:alias_method, hidden_method, method)
33
+ rescue NameError
34
+ # deal with nasties like ActiveRecord::Associations::AssociationProxy
35
+ end
36
+ end
37
+ end
38
+
39
+ def define_new_method
40
+ body = if macruby_method?
41
+ macruby_method
42
+ else
43
+ %{
44
+ def #{method}(*args, &block)
45
+ mocha.method_missing(:#{method}, *args, &block)
46
+ end
47
+ }
48
+ end
49
+ stubbee.__metaclass__.class_eval(body, __FILE__, __LINE__)
50
+ end
51
+
52
+ def remove_new_method
53
+ stubbee.__metaclass__.send(:remove_method, method)
54
+ end
55
+
56
+ def restore_original_method
57
+ if method_exists?(hidden_method)
58
+ begin
59
+ stubbee.__metaclass__.send(:alias_method, method, hidden_method)
60
+ stubbee.__metaclass__.send(:remove_method, hidden_method)
61
+ rescue NameError
62
+ # deal with nasties like ActiveRecord::Associations::AssociationProxy
63
+ end
64
+ end
65
+ end
66
+
67
+ def hidden_method
68
+ if RUBY_VERSION < '1.9'
69
+ method_name = method.to_s.gsub(/\W/) { |s| "_substituted_character_#{s[0]}_" }
70
+ else
71
+ method_name = method.to_s.gsub(/\W/) { |s| "_substituted_character_#{s.ord}_" }
72
+ end
73
+ hidden_method = "__stubba__#{method_name}__stubba__"
74
+ RUBY_VERSION < '1.9' ? hidden_method.to_s : hidden_method.to_sym
75
+ end
76
+
77
+ def eql?(other)
78
+ return false unless (other.class == self.class)
79
+ (stubbee.object_id == other.stubbee.object_id) and (method == other.method)
80
+ end
81
+
82
+ alias_method :==, :eql?
83
+
84
+ def to_s
85
+ "#{stubbee}.#{method}"
86
+ end
87
+
88
+ def method_exists?(method)
89
+ symbol = method.to_sym
90
+ metaclass = stubbee.__metaclass__
91
+ metaclass.public_method_defined?(symbol) || metaclass.protected_method_defined?(symbol) || metaclass.private_method_defined?(symbol)
92
+ end
93
+
94
+ def macruby_method?
95
+ method.to_s.include?(':')
96
+ end
97
+
98
+ def macruby_method
99
+ parts = method.to_s.split(':')
100
+
101
+ signature = "#{parts.shift}(param0"
102
+ parts.each_with_index { |part, index| signature << ", #{part}: param#{index + 1}" }
103
+ signature << ", &block)"
104
+
105
+ %{
106
+ def #{signature} # def method(param0, withExtraParam: param1, &block)
107
+ mocha.method_missing( # mocha.method_missing(
108
+ '#{method}'.to_sym, # 'method:withExtraParam:'.to_sym,
109
+ #{Array.new(parts.length + 1) { |index| "param#{index}" }.join(', ')}, # param0, param1,
110
+ &block) # &block)
111
+ end # end
112
+ }
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,79 @@
1
+ module Mocha # :nodoc:
2
+
3
+ # Configuration settings
4
+ class Configuration
5
+
6
+ DEFAULTS = { :stubbing_method_unnecessarily => :allow, :stubbing_method_on_non_mock_object => :allow, :stubbing_non_existent_method => :allow, :stubbing_non_public_method => :allow }
7
+
8
+ class << self
9
+
10
+ # :call-seq: allow(action, &block)
11
+ #
12
+ # Allow the specified <tt>action</tt> (as a symbol).
13
+ # The <tt>actions</tt> currently available are <tt>:stubbing_method_unnecessarily, :stubbing_method_on_non_mock_object, :stubbing_non_existent_method, :stubbing_non_public_method</tt>.
14
+ # If given a block, the configuration for the action will only be changed for the duration of the block, and will then be restored to the previous value.
15
+ def allow(action, &block)
16
+ change_config action, :allow, &block
17
+ end
18
+
19
+ def allow?(action) # :nodoc:
20
+ configuration[action] == :allow
21
+ end
22
+
23
+ # :call-seq: warn_when(action, &block)
24
+ #
25
+ # Warn if the specified <tt>action</tt> (as a symbol) is attempted.
26
+ # The <tt>actions</tt> currently available are <tt>:stubbing_method_unnecessarily, :stubbing_method_on_non_mock_object, :stubbing_non_existent_method, :stubbing_non_public_method</tt>.
27
+ # If given a block, the configuration for the action will only be changed for the duration of the block, and will then be restored to the previous value.
28
+ def warn_when(action, &block)
29
+ change_config action, :warn, &block
30
+ end
31
+
32
+ def warn_when?(action) # :nodoc:
33
+ configuration[action] == :warn
34
+ end
35
+
36
+ # :call-seq: prevent(action, &block)
37
+ #
38
+ # Raise a StubbingError if the specified <tt>action</tt> (as a symbol) is attempted.
39
+ # The <tt>actions</tt> currently available are <tt>:stubbing_method_unnecessarily, :stubbing_method_on_non_mock_object, :stubbing_non_existent_method, :stubbing_non_public_method</tt>.
40
+ # If given a block, the configuration for the action will only be changed for the duration of the block, and will then be restored to the previous value.
41
+ def prevent(action, &block)
42
+ change_config action, :prevent, &block
43
+ end
44
+
45
+ def prevent?(action) # :nodoc:
46
+ configuration[action] == :prevent
47
+ end
48
+
49
+ def reset_configuration # :nodoc:
50
+ @configuration = nil
51
+ end
52
+
53
+ private
54
+
55
+ def configuration # :nodoc:
56
+ @configuration ||= DEFAULTS.dup
57
+ end
58
+
59
+ def change_config(action, new_value, &block) # :nodoc:
60
+ if block_given?
61
+ temporarily_change_config action, new_value, &block
62
+ else
63
+ configuration[action] = new_value
64
+ end
65
+ end
66
+
67
+ def temporarily_change_config(action, new_value, &block) # :nodoc:
68
+ original_value = configuration[action]
69
+ configuration[action] = new_value
70
+ yield
71
+ ensure
72
+ configuration[action] = original_value
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,22 @@
1
+ module Mocha
2
+
3
+ class Deprecation
4
+
5
+ class << self
6
+
7
+ attr_accessor :mode, :messages
8
+
9
+ def warning(message)
10
+ @messages << message
11
+ $stderr.puts "Mocha deprecation warning: #{message}" unless mode == :disabled
12
+ $stderr.puts caller.join("\n ") if mode == :debug
13
+ end
14
+
15
+ end
16
+
17
+ self.mode = :enabled
18
+ self.messages = []
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,17 @@
1
+ module Mocha # :nodoc:
2
+
3
+ class ExceptionRaiser # :nodoc:
4
+
5
+ def initialize(exception, message)
6
+ @exception, @message = exception, message
7
+ end
8
+
9
+ def evaluate
10
+ raise @exception, @exception.to_s if @exception.is_a?(Module) && @exception.ancestors.include?(Interrupt)
11
+ raise @exception, @message if @message
12
+ raise @exception
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,476 @@
1
+ require 'mocha/method_matcher'
2
+ require 'mocha/parameters_matcher'
3
+ require 'mocha/expectation_error'
4
+ require 'mocha/return_values'
5
+ require 'mocha/exception_raiser'
6
+ require 'mocha/yield_parameters'
7
+ require 'mocha/is_a'
8
+ require 'mocha/in_state_ordering_constraint'
9
+ require 'mocha/change_state_side_effect'
10
+ require 'mocha/cardinality'
11
+
12
+ module Mocha # :nodoc:
13
+
14
+ # Methods on expectations returned from Mock#expects, Mock#stubs, Object#expects and Object#stubs.
15
+ class Expectation
16
+
17
+ # :call-seq: times(range) -> expectation
18
+ #
19
+ # Modifies expectation so that the number of calls to the expected method must be within a specific +range+.
20
+ #
21
+ # +range+ can be specified as an exact integer or as a range of integers
22
+ # object = mock()
23
+ # object.expects(:expected_method).times(3)
24
+ # 3.times { object.expected_method }
25
+ # # => verify succeeds
26
+ #
27
+ # object = mock()
28
+ # object.expects(:expected_method).times(3)
29
+ # 2.times { object.expected_method }
30
+ # # => verify fails
31
+ #
32
+ # object = mock()
33
+ # object.expects(:expected_method).times(2..4)
34
+ # 3.times { object.expected_method }
35
+ # # => verify succeeds
36
+ #
37
+ # object = mock()
38
+ # object.expects(:expected_method).times(2..4)
39
+ # object.expected_method
40
+ # # => verify fails
41
+ def times(range)
42
+ @cardinality = Cardinality.times(range)
43
+ self
44
+ end
45
+
46
+ # :call-seq: twice() -> expectation
47
+ #
48
+ # Modifies expectation so that the expected method must be called exactly twice.
49
+ # object = mock()
50
+ # object.expects(:expected_method).twice
51
+ # object.expected_method
52
+ # object.expected_method
53
+ # # => verify succeeds
54
+ #
55
+ # object = mock()
56
+ # object.expects(:expected_method).twice
57
+ # object.expected_method
58
+ # object.expected_method
59
+ # object.expected_method
60
+ # # => verify fails
61
+ #
62
+ # object = mock()
63
+ # object.expects(:expected_method).twice
64
+ # object.expected_method
65
+ # # => verify fails
66
+ def twice
67
+ @cardinality = Cardinality.exactly(2)
68
+ self
69
+ end
70
+
71
+ # :call-seq: once() -> expectation
72
+ #
73
+ # Modifies expectation so that the expected method must be called exactly once.
74
+ # Note that this is the default behaviour for an expectation, but you may wish to use it for clarity/emphasis.
75
+ # object = mock()
76
+ # object.expects(:expected_method).once
77
+ # object.expected_method
78
+ # # => verify succeeds
79
+ #
80
+ # object = mock()
81
+ # object.expects(:expected_method).once
82
+ # object.expected_method
83
+ # object.expected_method
84
+ # # => verify fails
85
+ #
86
+ # object = mock()
87
+ # object.expects(:expected_method).once
88
+ # # => verify fails
89
+ def once
90
+ @cardinality = Cardinality.exactly(1)
91
+ self
92
+ end
93
+
94
+ # :call-seq: never() -> expectation
95
+ #
96
+ # Modifies expectation so that the expected method must never be called.
97
+ # object = mock()
98
+ # object.expects(:expected_method).never
99
+ # object.expected_method
100
+ # # => verify fails
101
+ #
102
+ # object = mock()
103
+ # object.expects(:expected_method).never
104
+ # object.expected_method
105
+ # # => verify succeeds
106
+ def never
107
+ @cardinality = Cardinality.exactly(0)
108
+ self
109
+ end
110
+
111
+ # :call-seq: at_least(minimum_number_of_times) -> expectation
112
+ #
113
+ # Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+.
114
+ # object = mock()
115
+ # object.expects(:expected_method).at_least(2)
116
+ # 3.times { object.expected_method }
117
+ # # => verify succeeds
118
+ #
119
+ # object = mock()
120
+ # object.expects(:expected_method).at_least(2)
121
+ # object.expected_method
122
+ # # => verify fails
123
+ def at_least(minimum_number_of_times)
124
+ @cardinality = Cardinality.at_least(minimum_number_of_times)
125
+ self
126
+ end
127
+
128
+ # :call-seq: at_least_once() -> expectation
129
+ #
130
+ # Modifies expectation so that the expected method must be called at least once.
131
+ # object = mock()
132
+ # object.expects(:expected_method).at_least_once
133
+ # object.expected_method
134
+ # # => verify succeeds
135
+ #
136
+ # object = mock()
137
+ # object.expects(:expected_method).at_least_once
138
+ # # => verify fails
139
+ def at_least_once
140
+ at_least(1)
141
+ self
142
+ end
143
+
144
+ # :call-seq: at_most(maximum_number_of_times) -> expectation
145
+ #
146
+ # Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+.
147
+ # object = mock()
148
+ # object.expects(:expected_method).at_most(2)
149
+ # 2.times { object.expected_method }
150
+ # # => verify succeeds
151
+ #
152
+ # object = mock()
153
+ # object.expects(:expected_method).at_most(2)
154
+ # 3.times { object.expected_method }
155
+ # # => verify fails
156
+ def at_most(maximum_number_of_times)
157
+ @cardinality = Cardinality.at_most(maximum_number_of_times)
158
+ self
159
+ end
160
+
161
+ # :call-seq: at_most_once() -> expectation
162
+ #
163
+ # Modifies expectation so that the expected method must be called at most once.
164
+ # object = mock()
165
+ # object.expects(:expected_method).at_most_once
166
+ # object.expected_method
167
+ # # => verify succeeds
168
+ #
169
+ # object = mock()
170
+ # object.expects(:expected_method).at_most_once
171
+ # 2.times { object.expected_method }
172
+ # # => verify fails
173
+ def at_most_once()
174
+ at_most(1)
175
+ self
176
+ end
177
+
178
+ # :call-seq: with(*expected_parameters, &matching_block) -> expectation
179
+ #
180
+ # Modifies expectation so that the expected method must be called with +expected_parameters+.
181
+ # object = mock()
182
+ # object.expects(:expected_method).with(:param1, :param2)
183
+ # object.expected_method(:param1, :param2)
184
+ # # => verify succeeds
185
+ #
186
+ # object = mock()
187
+ # object.expects(:expected_method).with(:param1, :param2)
188
+ # object.expected_method(:param3)
189
+ # # => verify fails
190
+ # May be used with parameter matchers in Mocha::ParameterMatchers.
191
+ #
192
+ # If a +matching_block+ is given, the block is called with the parameters passed to the expected method.
193
+ # The expectation is matched if the block evaluates to +true+.
194
+ # object = mock()
195
+ # object.expects(:expected_method).with() { |value| value % 4 == 0 }
196
+ # object.expected_method(16)
197
+ # # => verify succeeds
198
+ #
199
+ # object = mock()
200
+ # object.expects(:expected_method).with() { |value| value % 4 == 0 }
201
+ # object.expected_method(17)
202
+ # # => verify fails
203
+ def with(*expected_parameters, &matching_block)
204
+ @parameters_matcher = ParametersMatcher.new(expected_parameters, &matching_block)
205
+ self
206
+ end
207
+
208
+ # :call-seq: yields(*parameters) -> expectation
209
+ #
210
+ # Modifies expectation so that when the expected method is called, it yields with the specified +parameters+.
211
+ # object = mock()
212
+ # object.expects(:expected_method).yields('result')
213
+ # yielded_value = nil
214
+ # object.expected_method { |value| yielded_value = value }
215
+ # yielded_value # => 'result'
216
+ # May be called multiple times on the same expectation for consecutive invocations. Also see Expectation#then.
217
+ # object = mock()
218
+ # object.stubs(:expected_method).yields(1).then.yields(2)
219
+ # yielded_values_from_first_invocation = []
220
+ # yielded_values_from_second_invocation = []
221
+ # object.expected_method { |value| yielded_values_from_first_invocation << value } # first invocation
222
+ # object.expected_method { |value| yielded_values_from_second_invocation << value } # second invocation
223
+ # yielded_values_from_first_invocation # => [1]
224
+ # yielded_values_from_second_invocation # => [2]
225
+ def yields(*parameters)
226
+ @yield_parameters.add(*parameters)
227
+ self
228
+ end
229
+
230
+ # :call-seq: multiple_yields(*parameter_groups) -> expectation
231
+ #
232
+ # Modifies expectation so that when the expected method is called, it yields multiple times per invocation with the specified +parameter_groups+.
233
+ # object = mock()
234
+ # object.expects(:expected_method).multiple_yields(['result_1', 'result_2'], ['result_3'])
235
+ # yielded_values = []
236
+ # object.expected_method { |*values| yielded_values << values }
237
+ # yielded_values # => [['result_1', 'result_2'], ['result_3]]
238
+ # May be called multiple times on the same expectation for consecutive invocations. Also see Expectation#then.
239
+ # object = mock()
240
+ # object.stubs(:expected_method).multiple_yields([1, 2], [3]).then.multiple_yields([4], [5, 6])
241
+ # yielded_values_from_first_invocation = []
242
+ # yielded_values_from_second_invocation = []
243
+ # object.expected_method { |*values| yielded_values_from_first_invocation << values } # first invocation
244
+ # object.expected_method { |*values| yielded_values_from_second_invocation << values } # second invocation
245
+ # yielded_values_from_first_invocation # => [[1, 2], [3]]
246
+ # yielded_values_from_second_invocation # => [[4], [5, 6]]
247
+ def multiple_yields(*parameter_groups)
248
+ @yield_parameters.multiple_add(*parameter_groups)
249
+ self
250
+ end
251
+
252
+ # :call-seq: returns(value) -> expectation
253
+ # returns(*values) -> expectation
254
+ #
255
+ # Modifies expectation so that when the expected method is called, it returns the specified +value+.
256
+ # object = mock()
257
+ # object.stubs(:stubbed_method).returns('result')
258
+ # object.stubbed_method # => 'result'
259
+ # object.stubbed_method # => 'result'
260
+ # If multiple +values+ are given, these are returned in turn on consecutive calls to the method.
261
+ # object = mock()
262
+ # object.stubs(:stubbed_method).returns(1, 2)
263
+ # object.stubbed_method # => 1
264
+ # object.stubbed_method # => 2
265
+ # May be called multiple times on the same expectation. Also see Expectation#then.
266
+ # object = mock()
267
+ # object.stubs(:expected_method).returns(1, 2).then.returns(3)
268
+ # object.expected_method # => 1
269
+ # object.expected_method # => 2
270
+ # object.expected_method # => 3
271
+ # May be called in conjunction with Expectation#raises on the same expectation.
272
+ # object = mock()
273
+ # object.stubs(:expected_method).returns(1, 2).then.raises(Exception)
274
+ # object.expected_method # => 1
275
+ # object.expected_method # => 2
276
+ # object.expected_method # => raises exception of class Exception1
277
+ # Note that in Ruby a method returning multiple values is exactly equivalent to a method returning an Array of those values.
278
+ # object = mock()
279
+ # object.stubs(:expected_method).returns([1, 2])
280
+ # x, y = object.expected_method
281
+ # x # => 1
282
+ # y # => 2
283
+ def returns(*values)
284
+ @return_values += ReturnValues.build(*values)
285
+ self
286
+ end
287
+
288
+ # :call-seq: raises(exception = RuntimeError, message = nil) -> expectation
289
+ #
290
+ # Modifies expectation so that when the expected method is called, it raises the specified +exception+ with the specified +message+.
291
+ # object = mock()
292
+ # object.expects(:expected_method).raises(Exception, 'message')
293
+ # object.expected_method # => raises exception of class Exception and with message 'message'
294
+ # May be called multiple times on the same expectation. Also see Expectation#then.
295
+ # object = mock()
296
+ # object.stubs(:expected_method).raises(Exception1).then.raises(Exception2)
297
+ # object.expected_method # => raises exception of class Exception1
298
+ # object.expected_method # => raises exception of class Exception2
299
+ # May be called in conjunction with Expectation#returns on the same expectation.
300
+ # object = mock()
301
+ # object.stubs(:expected_method).raises(Exception).then.returns(2, 3)
302
+ # object.expected_method # => raises exception of class Exception1
303
+ # object.expected_method # => 2
304
+ # object.expected_method # => 3
305
+ def raises(exception = RuntimeError, message = nil)
306
+ @return_values += ReturnValues.new(ExceptionRaiser.new(exception, message))
307
+ self
308
+ end
309
+
310
+ # :call-seq: then() -> expectation
311
+ # then(state_machine.is(state)) -> expectation
312
+ #
313
+ # <tt>then()</tt> is used as syntactic sugar to improve readability. It has no effect on state of the expectation.
314
+ # object = mock()
315
+ # object.stubs(:expected_method).returns(1, 2).then.raises(Exception).then.returns(4)
316
+ # object.expected_method # => 1
317
+ # object.expected_method # => 2
318
+ # object.expected_method # => raises exception of class Exception
319
+ # object.expected_method # => 4
320
+ #
321
+ # <tt>then(state_machine.is(state))</tt> is used to change the +state_machine+ to the specified +state+ when the invocation occurs.
322
+ #
323
+ # See also API#states, StateMachine and Expectation#when.
324
+ # power = states('power').starts_as('off')
325
+ #
326
+ # radio = mock('radio')
327
+ # radio.expects(:switch_on).then(power.is('on'))
328
+ # radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
329
+ # radio.expects(:adjust_volume).with(+5).when(power.is('on'))
330
+ # radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
331
+ # radio.expects(:adjust_volume).with(-5).when(power.is('on'))
332
+ # radio.expects(:switch_off).then(power.is('off'))
333
+ def then(*parameters)
334
+ if parameters.length == 1
335
+ state = parameters.first
336
+ add_side_effect(ChangeStateSideEffect.new(state))
337
+ end
338
+ self
339
+ end
340
+
341
+ # :call-seq: when(state_machine.is(state)) -> exception
342
+ #
343
+ # Constrains the expectation to occur only when the +state_machine+ is in the named +state+.
344
+ #
345
+ # See also API#states, StateMachine#starts_as and Expectation#then.
346
+ # power = states('power').starts_as('off')
347
+ #
348
+ # radio = mock('radio')
349
+ # radio.expects(:switch_on).then(power.is('on'))
350
+ # radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
351
+ # radio.expects(:adjust_volume).with(+5).when(power.is('on'))
352
+ # radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
353
+ # radio.expects(:adjust_volume).with(-5).when(power.is('on'))
354
+ # radio.expects(:switch_off).then(power.is('off'))
355
+ def when(state_predicate)
356
+ add_ordering_constraint(InStateOrderingConstraint.new(state_predicate))
357
+ self
358
+ end
359
+
360
+ # :call-seq: in_sequence(*sequences) -> expectation
361
+ #
362
+ # Constrains this expectation so that it must be invoked at the current point in the sequence.
363
+ #
364
+ # To expect a sequence of invocations, write the expectations in order and add the in_sequence(sequence) clause to each one.
365
+ #
366
+ # Expectations in a sequence can have any invocation count.
367
+ #
368
+ # If an expectation in a sequence is stubbed, rather than expected, it can be skipped in the sequence.
369
+ #
370
+ # See also API#sequence.
371
+ # breakfast = sequence('breakfast')
372
+ #
373
+ # egg = mock('egg')
374
+ # egg.expects(:crack).in_sequence(breakfast)
375
+ # egg.expects(:fry).in_sequence(breakfast)
376
+ # egg.expects(:eat).in_sequence(breakfast)
377
+ def in_sequence(*sequences)
378
+ sequences.each { |sequence| add_in_sequence_ordering_constraint(sequence) }
379
+ self
380
+ end
381
+
382
+ # :stopdoc:
383
+
384
+ attr_reader :backtrace
385
+
386
+ def initialize(mock, expected_method_name, backtrace = nil)
387
+ @mock = mock
388
+ @method_matcher = MethodMatcher.new(expected_method_name.to_sym)
389
+ @parameters_matcher = ParametersMatcher.new
390
+ @ordering_constraints = []
391
+ @side_effects = []
392
+ @cardinality, @invocation_count = Cardinality.exactly(1), 0
393
+ @return_values = ReturnValues.new
394
+ @yield_parameters = YieldParameters.new
395
+ @backtrace = backtrace || caller
396
+ end
397
+
398
+ def add_ordering_constraint(ordering_constraint)
399
+ @ordering_constraints << ordering_constraint
400
+ end
401
+
402
+ def add_in_sequence_ordering_constraint(sequence)
403
+ sequence.constrain_as_next_in_sequence(self)
404
+ end
405
+
406
+ def add_side_effect(side_effect)
407
+ @side_effects << side_effect
408
+ end
409
+
410
+ def perform_side_effects
411
+ @side_effects.each { |side_effect| side_effect.perform }
412
+ end
413
+
414
+ def in_correct_order?
415
+ @ordering_constraints.all? { |ordering_constraint| ordering_constraint.allows_invocation_now? }
416
+ end
417
+
418
+ def matches_method?(method_name)
419
+ @method_matcher.match?(method_name)
420
+ end
421
+
422
+ def match?(actual_method_name, *actual_parameters)
423
+ @method_matcher.match?(actual_method_name) && @parameters_matcher.match?(actual_parameters) && in_correct_order?
424
+ end
425
+
426
+ def invocations_allowed?
427
+ @cardinality.invocations_allowed?(@invocation_count)
428
+ end
429
+
430
+ def satisfied?
431
+ @cardinality.satisfied?(@invocation_count)
432
+ end
433
+
434
+ def invoke
435
+ @invocation_count += 1
436
+ perform_side_effects()
437
+ if block_given? then
438
+ @yield_parameters.next_invocation.each do |yield_parameters|
439
+ yield(*yield_parameters)
440
+ end
441
+ end
442
+ @return_values.next
443
+ end
444
+
445
+ def verified?(assertion_counter = nil)
446
+ assertion_counter.increment if assertion_counter && @cardinality.needs_verifying?
447
+ @cardinality.verified?(@invocation_count)
448
+ end
449
+
450
+ def used?
451
+ @cardinality.used?(@invocation_count)
452
+ end
453
+
454
+ def mocha_inspect
455
+ message = "#{@cardinality.mocha_inspect}, "
456
+ message << case @invocation_count
457
+ when 0 then "not yet invoked"
458
+ when 1 then "already invoked once"
459
+ when 2 then "already invoked twice"
460
+ else "already invoked #{@invocation_count} times"
461
+ end
462
+ message << ": "
463
+ message << method_signature
464
+ message << "; #{@ordering_constraints.map { |oc| oc.mocha_inspect }.join("; ")}" unless @ordering_constraints.empty?
465
+ message
466
+ end
467
+
468
+ def method_signature
469
+ "#{@mock.mocha_inspect}.#{@method_matcher.mocha_inspect}#{@parameters_matcher.mocha_inspect}"
470
+ end
471
+
472
+ # :startdoc:
473
+
474
+ end
475
+
476
+ end