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.
- data/COPYING +3 -0
- data/MIT-LICENSE +7 -0
- data/README +39 -0
- data/RELEASE +294 -0
- data/Rakefile +214 -0
- data/examples/misc.rb +43 -0
- data/examples/mocha.rb +25 -0
- data/examples/stubba.rb +64 -0
- data/lib/mocha.rb +3 -0
- data/lib/mocha/any_instance_method.rb +59 -0
- data/lib/mocha/api.rb +173 -0
- data/lib/mocha/argument_iterator.rb +21 -0
- data/lib/mocha/backtrace_filter.rb +17 -0
- data/lib/mocha/cardinality.rb +95 -0
- data/lib/mocha/central.rb +27 -0
- data/lib/mocha/change_state_side_effect.rb +19 -0
- data/lib/mocha/class_method.rb +117 -0
- data/lib/mocha/configuration.rb +79 -0
- data/lib/mocha/deprecation.rb +22 -0
- data/lib/mocha/exception_raiser.rb +17 -0
- data/lib/mocha/expectation.rb +476 -0
- data/lib/mocha/expectation_error.rb +15 -0
- data/lib/mocha/expectation_list.rb +50 -0
- data/lib/mocha/in_state_ordering_constraint.rb +19 -0
- data/lib/mocha/inspect.rb +67 -0
- data/lib/mocha/instance_method.rb +16 -0
- data/lib/mocha/integration.rb +38 -0
- data/lib/mocha/integration/mini_test.rb +21 -0
- data/lib/mocha/integration/mini_test/assertion_counter.rb +23 -0
- data/lib/mocha/integration/mini_test/version_131_and_above.rb +50 -0
- data/lib/mocha/integration/test_unit.rb +40 -0
- data/lib/mocha/integration/test_unit/assertion_counter.rb +23 -0
- data/lib/mocha/integration/test_unit/gem_version_200.rb +49 -0
- data/lib/mocha/integration/test_unit/gem_version_201_and_above.rb +49 -0
- data/lib/mocha/integration/test_unit/ruby_version_185_and_below.rb +48 -0
- data/lib/mocha/integration/test_unit/ruby_version_186_and_above.rb +50 -0
- data/lib/mocha/is_a.rb +9 -0
- data/lib/mocha/logger.rb +15 -0
- data/lib/mocha/metaclass.rb +13 -0
- data/lib/mocha/method_matcher.rb +21 -0
- data/lib/mocha/mock.rb +200 -0
- data/lib/mocha/mockery.rb +181 -0
- data/lib/mocha/module_method.rb +16 -0
- data/lib/mocha/multiple_yields.rb +20 -0
- data/lib/mocha/names.rb +53 -0
- data/lib/mocha/no_yields.rb +11 -0
- data/lib/mocha/object.rb +187 -0
- data/lib/mocha/parameter_matchers.rb +27 -0
- data/lib/mocha/parameter_matchers/all_of.rb +42 -0
- data/lib/mocha/parameter_matchers/any_of.rb +47 -0
- data/lib/mocha/parameter_matchers/any_parameters.rb +40 -0
- data/lib/mocha/parameter_matchers/anything.rb +33 -0
- data/lib/mocha/parameter_matchers/base.rb +15 -0
- data/lib/mocha/parameter_matchers/equals.rb +42 -0
- data/lib/mocha/parameter_matchers/has_entries.rb +45 -0
- data/lib/mocha/parameter_matchers/has_entry.rb +57 -0
- data/lib/mocha/parameter_matchers/has_key.rb +43 -0
- data/lib/mocha/parameter_matchers/has_value.rb +43 -0
- data/lib/mocha/parameter_matchers/includes.rb +41 -0
- data/lib/mocha/parameter_matchers/instance_of.rb +42 -0
- data/lib/mocha/parameter_matchers/is_a.rb +42 -0
- data/lib/mocha/parameter_matchers/kind_of.rb +42 -0
- data/lib/mocha/parameter_matchers/not.rb +42 -0
- data/lib/mocha/parameter_matchers/object.rb +15 -0
- data/lib/mocha/parameter_matchers/optionally.rb +55 -0
- data/lib/mocha/parameter_matchers/regexp_matches.rb +44 -0
- data/lib/mocha/parameter_matchers/responds_with.rb +43 -0
- data/lib/mocha/parameter_matchers/yaml_equivalent.rb +43 -0
- data/lib/mocha/parameters_matcher.rb +37 -0
- data/lib/mocha/pretty_parameters.rb +28 -0
- data/lib/mocha/return_values.rb +31 -0
- data/lib/mocha/sequence.rb +42 -0
- data/lib/mocha/single_return_value.rb +17 -0
- data/lib/mocha/single_yield.rb +18 -0
- data/lib/mocha/standalone.rb +1 -0
- data/lib/mocha/state_machine.rb +91 -0
- data/lib/mocha/stubbing_error.rb +16 -0
- data/lib/mocha/unexpected_invocation.rb +18 -0
- data/lib/mocha/yield_parameters.rb +31 -0
- data/lib/mocha_standalone.rb +2 -0
- data/lib/stubba.rb +4 -0
- data/test/acceptance/acceptance_test_helper.rb +38 -0
- data/test/acceptance/api_test.rb +139 -0
- data/test/acceptance/bug_18914_test.rb +43 -0
- data/test/acceptance/bug_21465_test.rb +34 -0
- data/test/acceptance/bug_21563_test.rb +25 -0
- data/test/acceptance/expected_invocation_count_test.rb +196 -0
- data/test/acceptance/failure_messages_test.rb +64 -0
- data/test/acceptance/minitest_test.rb +153 -0
- data/test/acceptance/mocha_example_test.rb +98 -0
- data/test/acceptance/mocha_test_result_test.rb +84 -0
- data/test/acceptance/mock_test.rb +100 -0
- data/test/acceptance/mock_with_initializer_block_test.rb +51 -0
- data/test/acceptance/mocked_methods_dispatch_test.rb +78 -0
- data/test/acceptance/optional_parameters_test.rb +70 -0
- data/test/acceptance/parameter_matcher_test.rb +209 -0
- data/test/acceptance/partial_mocks_test.rb +47 -0
- data/test/acceptance/return_value_test.rb +52 -0
- data/test/acceptance/sequence_test.rb +186 -0
- data/test/acceptance/states_test.rb +70 -0
- data/test/acceptance/stub_any_instance_method_test.rb +195 -0
- data/test/acceptance/stub_class_method_test.rb +203 -0
- data/test/acceptance/stub_everything_test.rb +56 -0
- data/test/acceptance/stub_instance_method_test.rb +203 -0
- data/test/acceptance/stub_module_method_test.rb +163 -0
- data/test/acceptance/stub_test.rb +52 -0
- data/test/acceptance/stubba_example_test.rb +102 -0
- data/test/acceptance/stubba_test.rb +15 -0
- data/test/acceptance/stubba_test_result_test.rb +66 -0
- data/test/acceptance/stubbing_error_backtrace_test.rb +64 -0
- data/test/acceptance/stubbing_method_unnecessarily_test.rb +65 -0
- data/test/acceptance/stubbing_non_existent_any_instance_method_test.rb +130 -0
- data/test/acceptance/stubbing_non_existent_class_method_test.rb +157 -0
- data/test/acceptance/stubbing_non_existent_instance_method_test.rb +147 -0
- data/test/acceptance/stubbing_non_public_any_instance_method_test.rb +130 -0
- data/test/acceptance/stubbing_non_public_class_method_test.rb +163 -0
- data/test/acceptance/stubbing_non_public_instance_method_test.rb +143 -0
- data/test/acceptance/stubbing_on_non_mock_object_test.rb +64 -0
- data/test/deprecation_disabler.rb +15 -0
- data/test/execution_point.rb +36 -0
- data/test/method_definer.rb +24 -0
- data/test/simple_counter.rb +13 -0
- data/test/test_helper.rb +25 -0
- data/test/test_runner.rb +33 -0
- data/test/unit/any_instance_method_test.rb +126 -0
- data/test/unit/array_inspect_test.rb +16 -0
- data/test/unit/backtrace_filter_test.rb +19 -0
- data/test/unit/cardinality_test.rb +56 -0
- data/test/unit/central_test.rb +65 -0
- data/test/unit/change_state_side_effect_test.rb +41 -0
- data/test/unit/class_method_test.rb +295 -0
- data/test/unit/configuration_test.rb +38 -0
- data/test/unit/date_time_inspect_test.rb +21 -0
- data/test/unit/exception_raiser_test.rb +42 -0
- data/test/unit/expectation_list_test.rb +57 -0
- data/test/unit/expectation_test.rb +480 -0
- data/test/unit/hash_inspect_test.rb +16 -0
- data/test/unit/in_state_ordering_constraint_test.rb +43 -0
- data/test/unit/metaclass_test.rb +22 -0
- data/test/unit/method_matcher_test.rb +23 -0
- data/test/unit/mock_test.rb +302 -0
- data/test/unit/mockery_test.rb +149 -0
- data/test/unit/multiple_yields_test.rb +18 -0
- data/test/unit/no_yields_test.rb +18 -0
- data/test/unit/object_inspect_test.rb +37 -0
- data/test/unit/object_test.rb +82 -0
- data/test/unit/parameter_matchers/all_of_test.rb +26 -0
- data/test/unit/parameter_matchers/any_of_test.rb +26 -0
- data/test/unit/parameter_matchers/anything_test.rb +21 -0
- data/test/unit/parameter_matchers/equals_test.rb +25 -0
- data/test/unit/parameter_matchers/has_entries_test.rb +51 -0
- data/test/unit/parameter_matchers/has_entry_test.rb +82 -0
- data/test/unit/parameter_matchers/has_key_test.rb +55 -0
- data/test/unit/parameter_matchers/has_value_test.rb +57 -0
- data/test/unit/parameter_matchers/includes_test.rb +44 -0
- data/test/unit/parameter_matchers/instance_of_test.rb +25 -0
- data/test/unit/parameter_matchers/is_a_test.rb +25 -0
- data/test/unit/parameter_matchers/kind_of_test.rb +25 -0
- data/test/unit/parameter_matchers/not_test.rb +26 -0
- data/test/unit/parameter_matchers/regexp_matches_test.rb +46 -0
- data/test/unit/parameter_matchers/responds_with_test.rb +25 -0
- data/test/unit/parameter_matchers/stub_matcher.rb +27 -0
- data/test/unit/parameter_matchers/yaml_equivalent_test.rb +25 -0
- data/test/unit/parameters_matcher_test.rb +121 -0
- data/test/unit/return_values_test.rb +63 -0
- data/test/unit/sequence_test.rb +104 -0
- data/test/unit/single_return_value_test.rb +14 -0
- data/test/unit/single_yield_test.rb +18 -0
- data/test/unit/state_machine_test.rb +98 -0
- data/test/unit/string_inspect_test.rb +11 -0
- data/test/unit/yield_parameters_test.rb +93 -0
- 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,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
|