redinger-rr 0.10.3
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/CHANGES +221 -0
- data/README.rdoc +343 -0
- data/Rakefile +88 -0
- data/VERSION.yml +4 -0
- data/lib/rr.rb +88 -0
- data/lib/rr/adapters/rr_methods.rb +122 -0
- data/lib/rr/adapters/rspec.rb +59 -0
- data/lib/rr/adapters/test_unit.rb +29 -0
- data/lib/rr/double.rb +152 -0
- data/lib/rr/double_definitions/child_double_definition_creator.rb +27 -0
- data/lib/rr/double_definitions/double_definition.rb +348 -0
- data/lib/rr/double_definitions/double_definition_creator.rb +167 -0
- data/lib/rr/double_definitions/double_definition_creator_proxy.rb +37 -0
- data/lib/rr/double_definitions/strategies/implementation/implementation_strategy.rb +15 -0
- data/lib/rr/double_definitions/strategies/implementation/proxy.rb +62 -0
- data/lib/rr/double_definitions/strategies/implementation/reimplementation.rb +14 -0
- data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +17 -0
- data/lib/rr/double_definitions/strategies/scope/instance.rb +15 -0
- data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +50 -0
- data/lib/rr/double_definitions/strategies/scope/scope_strategy.rb +15 -0
- data/lib/rr/double_definitions/strategies/strategy.rb +70 -0
- data/lib/rr/double_definitions/strategies/verification/dont_allow.rb +34 -0
- data/lib/rr/double_definitions/strategies/verification/mock.rb +44 -0
- data/lib/rr/double_definitions/strategies/verification/stub.rb +45 -0
- data/lib/rr/double_definitions/strategies/verification/verification_strategy.rb +15 -0
- data/lib/rr/double_injection.rb +180 -0
- data/lib/rr/double_matches.rb +51 -0
- data/lib/rr/errors/argument_equality_error.rb +6 -0
- data/lib/rr/errors/double_definition_error.rb +6 -0
- data/lib/rr/errors/double_not_found_error.rb +6 -0
- data/lib/rr/errors/double_order_error.rb +6 -0
- data/lib/rr/errors/rr_error.rb +20 -0
- data/lib/rr/errors/spy_verification_errors/double_injection_not_found_error.rb +8 -0
- data/lib/rr/errors/spy_verification_errors/invocation_count_error.rb +8 -0
- data/lib/rr/errors/spy_verification_errors/spy_verification_error.rb +8 -0
- data/lib/rr/errors/subject_does_not_implement_method_error.rb +6 -0
- data/lib/rr/errors/subject_has_different_arity_error.rb +6 -0
- data/lib/rr/errors/times_called_error.rb +6 -0
- data/lib/rr/expectations/any_argument_expectation.rb +21 -0
- data/lib/rr/expectations/argument_equality_expectation.rb +41 -0
- data/lib/rr/expectations/times_called_expectation.rb +57 -0
- data/lib/rr/hash_with_object_id_key.rb +44 -0
- data/lib/rr/method_dispatches/base_method_dispatch.rb +108 -0
- data/lib/rr/method_dispatches/method_dispatch.rb +61 -0
- data/lib/rr/method_dispatches/method_missing_dispatch.rb +49 -0
- data/lib/rr/proc_from_block.rb +7 -0
- data/lib/rr/recorded_calls.rb +103 -0
- data/lib/rr/space.rb +123 -0
- data/lib/rr/spy_verification.rb +48 -0
- data/lib/rr/spy_verification_proxy.rb +18 -0
- data/lib/rr/times_called_matchers/any_times_matcher.rb +18 -0
- data/lib/rr/times_called_matchers/at_least_matcher.rb +15 -0
- data/lib/rr/times_called_matchers/at_most_matcher.rb +23 -0
- data/lib/rr/times_called_matchers/integer_matcher.rb +19 -0
- data/lib/rr/times_called_matchers/non_terminal.rb +27 -0
- data/lib/rr/times_called_matchers/proc_matcher.rb +11 -0
- data/lib/rr/times_called_matchers/range_matcher.rb +21 -0
- data/lib/rr/times_called_matchers/terminal.rb +20 -0
- data/lib/rr/times_called_matchers/times_called_matcher.rb +44 -0
- data/lib/rr/wildcard_matchers.rb +158 -0
- data/lib/rr/wildcard_matchers/anything.rb +18 -0
- data/lib/rr/wildcard_matchers/boolean.rb +23 -0
- data/lib/rr/wildcard_matchers/duck_type.rb +32 -0
- data/lib/rr/wildcard_matchers/hash_including.rb +29 -0
- data/lib/rr/wildcard_matchers/is_a.rb +25 -0
- data/lib/rr/wildcard_matchers/numeric.rb +13 -0
- data/lib/rr/wildcard_matchers/range.rb +7 -0
- data/lib/rr/wildcard_matchers/regexp.rb +7 -0
- data/lib/rr/wildcard_matchers/satisfy.rb +26 -0
- data/spec/core_spec_suite.rb +19 -0
- data/spec/environment_fixture_setup.rb +7 -0
- data/spec/high_level_spec.rb +398 -0
- data/spec/proc_from_block_spec.rb +14 -0
- data/spec/rr/adapters/rr_methods_argument_matcher_spec.rb +67 -0
- data/spec/rr/adapters/rr_methods_creator_spec.rb +149 -0
- data/spec/rr/adapters/rr_methods_space_spec.rb +115 -0
- data/spec/rr/adapters/rr_methods_spec_helper.rb +11 -0
- data/spec/rr/adapters/rr_methods_times_matcher_spec.rb +17 -0
- data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +112 -0
- data/spec/rr/double_definitions/double_definition_creator_proxy_spec.rb +155 -0
- data/spec/rr/double_definitions/double_definition_creator_spec.rb +502 -0
- data/spec/rr/double_definitions/double_definition_spec.rb +1165 -0
- data/spec/rr/double_injection/double_injection_spec.rb +339 -0
- data/spec/rr/double_injection/double_injection_verify_spec.rb +29 -0
- data/spec/rr/double_spec.rb +352 -0
- data/spec/rr/errors/rr_error_spec.rb +67 -0
- data/spec/rr/expectations/any_argument_expectation_spec.rb +47 -0
- data/spec/rr/expectations/anything_argument_equality_expectation_spec.rb +14 -0
- data/spec/rr/expectations/argument_equality_expectation_spec.rb +135 -0
- data/spec/rr/expectations/boolean_argument_equality_expectation_spec.rb +34 -0
- data/spec/rr/expectations/hash_including_argument_equality_expectation_spec.rb +82 -0
- data/spec/rr/expectations/hash_including_spec.rb +17 -0
- data/spec/rr/expectations/satisfy_argument_equality_expectation_spec.rb +59 -0
- data/spec/rr/expectations/satisfy_spec.rb +14 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_any_times_spec.rb +46 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_least_spec.rb +69 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_most_spec.rb +71 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +23 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_integer_spec.rb +104 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_proc_spec.rb +81 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_range_spec.rb +83 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_spec.rb +38 -0
- data/spec/rr/rspec/invocation_matcher_spec.rb +279 -0
- data/spec/rr/rspec/rspec_adapter_spec.rb +66 -0
- data/spec/rr/rspec/rspec_backtrace_tweaking_spec.rb +31 -0
- data/spec/rr/rspec/rspec_backtrace_tweaking_spec_fixture.rb +11 -0
- data/spec/rr/rspec/rspec_usage_spec.rb +86 -0
- data/spec/rr/space/hash_with_object_id_key_spec.rb +88 -0
- data/spec/rr/space/space_spec.rb +550 -0
- data/spec/rr/test_unit/test_helper.rb +7 -0
- data/spec/rr/test_unit/test_unit_backtrace_test.rb +36 -0
- data/spec/rr/test_unit/test_unit_integration_test.rb +57 -0
- data/spec/rr/times_called_matchers/any_times_matcher_spec.rb +47 -0
- data/spec/rr/times_called_matchers/at_least_matcher_spec.rb +55 -0
- data/spec/rr/times_called_matchers/at_most_matcher_spec.rb +70 -0
- data/spec/rr/times_called_matchers/integer_matcher_spec.rb +70 -0
- data/spec/rr/times_called_matchers/proc_matcher_spec.rb +55 -0
- data/spec/rr/times_called_matchers/range_matcher_spec.rb +76 -0
- data/spec/rr/times_called_matchers/times_called_matcher_spec.rb +118 -0
- data/spec/rr/wildcard_matchers/anything_spec.rb +24 -0
- data/spec/rr/wildcard_matchers/boolean_spec.rb +36 -0
- data/spec/rr/wildcard_matchers/duck_type_spec.rb +52 -0
- data/spec/rr/wildcard_matchers/is_a_spec.rb +32 -0
- data/spec/rr/wildcard_matchers/numeric_spec.rb +32 -0
- data/spec/rr/wildcard_matchers/range_spec.rb +35 -0
- data/spec/rr/wildcard_matchers/regexp_spec.rb +43 -0
- data/spec/rr_spec.rb +28 -0
- data/spec/rspec_spec_suite.rb +17 -0
- data/spec/spec_helper.rb +109 -0
- data/spec/spec_suite.rb +31 -0
- data/spec/spy_verification_spec.rb +129 -0
- data/spec/test_unit_spec_suite.rb +21 -0
- metadata +193 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module RR
|
2
|
+
# TODO: Refactor to a side-effect-free strategy.
|
3
|
+
class HashWithObjectIdKey < ::Hash #:nodoc:
|
4
|
+
def initialize
|
5
|
+
@keys = {}
|
6
|
+
super do |hash, subject_object|
|
7
|
+
hash.set_with_object_id(subject_object, {})
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method :get_with_object_id, :[]
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
@keys[key.__id__] = key
|
15
|
+
super(key.__id__)
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :set_with_object_id, :[]=
|
19
|
+
|
20
|
+
def []=(key, value)
|
21
|
+
@keys[key.__id__] = key
|
22
|
+
super(key.__id__, value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def each
|
26
|
+
super do |object_id, value|
|
27
|
+
yield @keys[object_id], value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete(key)
|
32
|
+
@keys.delete(key.__id__)
|
33
|
+
super(key.__id__)
|
34
|
+
end
|
35
|
+
|
36
|
+
def keys
|
37
|
+
@keys.values
|
38
|
+
end
|
39
|
+
|
40
|
+
def include?(key)
|
41
|
+
super(key.__id__)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module RR
|
2
|
+
module MethodDispatches
|
3
|
+
class BaseMethodDispatch
|
4
|
+
include Space::Reader
|
5
|
+
|
6
|
+
attr_reader :double_injection, :args, :block, :double
|
7
|
+
|
8
|
+
def call
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
def find_double_to_attempt
|
14
|
+
matches = DoubleMatches.new(doubles).find_all_matches(args)
|
15
|
+
|
16
|
+
unless matches.exact_terminal_doubles_to_attempt.empty?
|
17
|
+
return matches.exact_terminal_doubles_to_attempt.first
|
18
|
+
end
|
19
|
+
|
20
|
+
unless matches.exact_non_terminal_doubles_to_attempt.empty?
|
21
|
+
return matches.exact_non_terminal_doubles_to_attempt.last
|
22
|
+
end
|
23
|
+
|
24
|
+
unless matches.wildcard_terminal_doubles_to_attempt.empty?
|
25
|
+
return matches.wildcard_terminal_doubles_to_attempt.first
|
26
|
+
end
|
27
|
+
|
28
|
+
unless matches.wildcard_non_terminal_doubles_to_attempt.empty?
|
29
|
+
return matches.wildcard_non_terminal_doubles_to_attempt.last
|
30
|
+
end
|
31
|
+
|
32
|
+
unless matches.matching_doubles.empty?
|
33
|
+
return matches.matching_doubles.first # This will raise a TimesCalledError
|
34
|
+
end
|
35
|
+
|
36
|
+
return nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def call_yields
|
40
|
+
if definition.yields_value
|
41
|
+
if block
|
42
|
+
block.call(*definition.yields_value)
|
43
|
+
else
|
44
|
+
raise ArgumentError, "A Block must be passed into the method call when using yields"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def call_implementation
|
50
|
+
if implementation_is_original_method?
|
51
|
+
call_original_method
|
52
|
+
else
|
53
|
+
if implementation
|
54
|
+
if implementation.is_a?(Method)
|
55
|
+
implementation.call(*args, &block)
|
56
|
+
else
|
57
|
+
call_args = block ? args + [ProcFromBlock.new(&block)] : args
|
58
|
+
implementation.call(*call_args)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def call_original_method_missing
|
67
|
+
subject.__send__(original_method_missing_alias_name, method_name, *args, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def implementation_is_original_method?
|
71
|
+
double.implementation_is_original_method?
|
72
|
+
end
|
73
|
+
|
74
|
+
def extract_subject_from_return_value(return_value)
|
75
|
+
case return_value
|
76
|
+
when DoubleDefinitions::DoubleDefinition
|
77
|
+
return_value.root_subject
|
78
|
+
when DoubleDefinitions::DoubleDefinitionCreatorProxy
|
79
|
+
return_value.__creator__.root_subject
|
80
|
+
else
|
81
|
+
return_value
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def double_not_found_error
|
86
|
+
message =
|
87
|
+
"On subject #{subject},\n" <<
|
88
|
+
"unexpected method invocation:\n" <<
|
89
|
+
" #{Double.formatted_name(method_name, args)}\n" <<
|
90
|
+
"expected invocations:\n" <<
|
91
|
+
Double.list_message_part(doubles)
|
92
|
+
raise Errors::DoubleNotFoundError, message
|
93
|
+
end
|
94
|
+
|
95
|
+
def after_call_proc
|
96
|
+
definition.after_call_proc
|
97
|
+
end
|
98
|
+
|
99
|
+
def definition
|
100
|
+
double.definition
|
101
|
+
end
|
102
|
+
|
103
|
+
def doubles
|
104
|
+
double_injection.doubles
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module RR
|
2
|
+
module MethodDispatches
|
3
|
+
class MethodDispatch < BaseMethodDispatch
|
4
|
+
def initialize(double_injection, args, block)
|
5
|
+
@double_injection, @args, @block = double_injection, args, block
|
6
|
+
@double = find_double_to_attempt
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
space.record_call(subject, method_name, args, block)
|
11
|
+
if double
|
12
|
+
double.method_call(args)
|
13
|
+
call_yields
|
14
|
+
return_value = extract_subject_from_return_value(call_implementation)
|
15
|
+
if after_call_proc
|
16
|
+
extract_subject_from_return_value(after_call_proc.call(return_value))
|
17
|
+
else
|
18
|
+
return_value
|
19
|
+
end
|
20
|
+
else
|
21
|
+
double_not_found_error
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def call_original_method
|
26
|
+
if subject_has_original_method?
|
27
|
+
subject.__send__(original_method_alias_name, *args, &block)
|
28
|
+
elsif subject_has_original_method_missing?
|
29
|
+
call_original_method_missing
|
30
|
+
else
|
31
|
+
subject.__send__(:method_missing, method_name, *args, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
def implementation
|
37
|
+
definition.implementation
|
38
|
+
end
|
39
|
+
|
40
|
+
def subject_has_original_method?
|
41
|
+
double_injection.subject_has_original_method?
|
42
|
+
end
|
43
|
+
|
44
|
+
def subject_has_original_method_missing?
|
45
|
+
double_injection.subject_has_original_method_missing?
|
46
|
+
end
|
47
|
+
|
48
|
+
def subject
|
49
|
+
double_injection.subject
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_name
|
53
|
+
double_injection.method_name
|
54
|
+
end
|
55
|
+
|
56
|
+
def original_method_alias_name
|
57
|
+
double_injection.original_method_alias_name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module RR
|
2
|
+
module MethodDispatches
|
3
|
+
class MethodMissingDispatch < BaseMethodDispatch
|
4
|
+
attr_reader :method_name
|
5
|
+
def initialize(double_injection, method_name, args, block)
|
6
|
+
@double_injection, @args, @block = double_injection, args, block
|
7
|
+
@method_name = method_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
if double_injection.method_name == method_name
|
12
|
+
space.record_call(subject, method_name, args, block)
|
13
|
+
@double = find_double_to_attempt
|
14
|
+
|
15
|
+
if double
|
16
|
+
double.method_call(args)
|
17
|
+
call_yields
|
18
|
+
return_value = extract_subject_from_return_value(call_implementation)
|
19
|
+
if after_call_proc
|
20
|
+
extract_subject_from_return_value(after_call_proc.call(return_value))
|
21
|
+
else
|
22
|
+
return_value
|
23
|
+
end
|
24
|
+
else
|
25
|
+
double_not_found_error
|
26
|
+
end
|
27
|
+
else
|
28
|
+
call_original_method
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def call_original_method
|
33
|
+
double_injection.bypass_bound_method do
|
34
|
+
call_original_method_missing
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def original_method_missing_alias_name
|
41
|
+
double_injection.original_method_missing_alias_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def subject
|
45
|
+
double_injection.subject
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module RR
|
2
|
+
class RecordedCalls
|
3
|
+
include RR::Space::Reader
|
4
|
+
|
5
|
+
def initialize(recorded_calls=[])
|
6
|
+
@recorded_calls = recorded_calls
|
7
|
+
@ordered_index = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :recorded_calls
|
11
|
+
|
12
|
+
def clear
|
13
|
+
self.ordered_index = 0
|
14
|
+
recorded_calls.clear
|
15
|
+
end
|
16
|
+
|
17
|
+
def <<(recorded_call)
|
18
|
+
recorded_calls << recorded_call
|
19
|
+
end
|
20
|
+
|
21
|
+
def any?(&block)
|
22
|
+
recorded_calls.any?(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
recorded_calls == other.recorded_calls
|
27
|
+
end
|
28
|
+
|
29
|
+
def match_error(spy_verification)
|
30
|
+
double_injection_exists_error(spy_verification) || begin
|
31
|
+
if spy_verification.ordered?
|
32
|
+
ordered_match_error(spy_verification)
|
33
|
+
else
|
34
|
+
unordered_match_error(spy_verification)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
attr_accessor :ordered_index
|
41
|
+
|
42
|
+
def double_injection_exists_error(spy_verification)
|
43
|
+
unless space.double_injection_exists?(spy_verification.subject, spy_verification.method_name)
|
44
|
+
RR::Errors::SpyVerificationErrors::DoubleInjectionNotFoundError.new(
|
45
|
+
"A Double Injection for the subject and method call:\n" <<
|
46
|
+
"#{spy_verification.subject.inspect}\n" <<
|
47
|
+
"#{spy_verification.method_name}\ndoes not exist in:\n" <<
|
48
|
+
"\t#{recorded_calls.map {|call| call.inspect}.join("\n\t")}"
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def ordered_match_error(spy_verification)
|
54
|
+
memoized_matching_recorded_calls = matching_recorded_calls(spy_verification)
|
55
|
+
|
56
|
+
if memoized_matching_recorded_calls.last
|
57
|
+
self.ordered_index = recorded_calls.index(memoized_matching_recorded_calls.last)
|
58
|
+
end
|
59
|
+
(0..memoized_matching_recorded_calls.size).to_a.any? do |i|
|
60
|
+
spy_verification.times_matcher.matches?(i)
|
61
|
+
end ? nil : invocation_count_error(spy_verification, memoized_matching_recorded_calls)
|
62
|
+
end
|
63
|
+
|
64
|
+
def unordered_match_error(spy_verification)
|
65
|
+
memoized_matching_recorded_calls = matching_recorded_calls(spy_verification)
|
66
|
+
|
67
|
+
spy_verification.times_matcher.matches?(
|
68
|
+
memoized_matching_recorded_calls.size
|
69
|
+
) ? nil : invocation_count_error(spy_verification, memoized_matching_recorded_calls)
|
70
|
+
end
|
71
|
+
|
72
|
+
def matching_recorded_calls(spy_verification)
|
73
|
+
recorded_calls[ordered_index..-1].
|
74
|
+
select(&match_double_injection(spy_verification)).
|
75
|
+
select(&match_argument_expectation(spy_verification))
|
76
|
+
end
|
77
|
+
|
78
|
+
def match_double_injection(spy_verification)
|
79
|
+
lambda do |recorded_call|
|
80
|
+
recorded_call[0] == spy_verification.subject &&
|
81
|
+
recorded_call[1] == spy_verification.method_name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def match_argument_expectation(spy_verification)
|
86
|
+
lambda do |recorded_call|
|
87
|
+
spy_verification.argument_expectation.exact_match?(*recorded_call[2]) ||
|
88
|
+
spy_verification.argument_expectation.wildcard_match?(*recorded_call[2])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def invocation_count_error(spy_verification, matching_recorded_calls)
|
93
|
+
RR::Errors::SpyVerificationErrors::InvocationCountError.new(
|
94
|
+
"On subject #{spy_verification.subject.inspect}\n" <<
|
95
|
+
"Expected #{Double.formatted_name(spy_verification.method_name, spy_verification.argument_expectation.expected_arguments)}\n" <<
|
96
|
+
"to be called #{spy_verification.times_matcher.expected_times_message},\n" <<
|
97
|
+
"but was called #{matching_recorded_calls.size} times.\n" <<
|
98
|
+
"All of the method calls related to Doubles are:\n" <<
|
99
|
+
"\t#{recorded_calls.map {|call| call.inspect}.join("\n\t")}"
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/rr/space.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module RR
|
2
|
+
# RR::Space.instance is the global state subject for the RR framework.
|
3
|
+
class Space
|
4
|
+
module Reader
|
5
|
+
def space
|
6
|
+
RR::Space.instance
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def instance
|
12
|
+
@instance ||= new
|
13
|
+
end
|
14
|
+
attr_writer :instance
|
15
|
+
|
16
|
+
protected
|
17
|
+
def method_missing(method_name, *args, &block)
|
18
|
+
instance.__send__(method_name, *args, &block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :double_injections, :ordered_doubles, :recorded_calls
|
23
|
+
attr_accessor :trim_backtrace
|
24
|
+
def initialize
|
25
|
+
@double_injections = HashWithObjectIdKey.new
|
26
|
+
@ordered_doubles = []
|
27
|
+
@trim_backtrace = false
|
28
|
+
@recorded_calls = RR::RecordedCalls.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Reuses or creates, if none exists, a DoubleInjection for the passed
|
32
|
+
# in subject and method_name.
|
33
|
+
# When a DoubleInjection is created, it binds the dispatcher to the
|
34
|
+
# subject.
|
35
|
+
def double_injection(subject, method_name)
|
36
|
+
@double_injections[subject][method_name.to_sym] ||= begin
|
37
|
+
DoubleInjection.new(subject, method_name.to_sym, (class << subject; self; end)).bind
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def double_injection_exists?(subject, method_name)
|
42
|
+
@double_injections.include?(subject) && @double_injections[subject].include?(method_name.to_sym)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Registers the ordered Double to be verified.
|
46
|
+
def register_ordered_double(double)
|
47
|
+
@ordered_doubles << double unless ordered_doubles.include?(double)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Verifies that the passed in ordered Double is being called
|
51
|
+
# in the correct position.
|
52
|
+
def verify_ordered_double(double)
|
53
|
+
unless double.terminal?
|
54
|
+
raise Errors::DoubleOrderError,
|
55
|
+
"Ordered Doubles cannot have a NonTerminal TimesCalledExpectation"
|
56
|
+
end
|
57
|
+
unless @ordered_doubles.first == double
|
58
|
+
message = Double.formatted_name(double.method_name, double.expected_arguments)
|
59
|
+
message << " called out of order in list\n"
|
60
|
+
message << Double.list_message_part(@ordered_doubles)
|
61
|
+
raise Errors::DoubleOrderError, message
|
62
|
+
end
|
63
|
+
@ordered_doubles.shift unless double.attempt?
|
64
|
+
double
|
65
|
+
end
|
66
|
+
|
67
|
+
# Verifies all the DoubleInjection objects have met their
|
68
|
+
# TimesCalledExpectations.
|
69
|
+
def verify_doubles(*objects)
|
70
|
+
objects = @double_injections.keys if objects.empty?
|
71
|
+
objects.each do |subject|
|
72
|
+
@double_injections[subject].keys.each do |method_name|
|
73
|
+
verify_double(subject, method_name)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
alias_method :verify, :verify_doubles
|
78
|
+
|
79
|
+
# Resets the registered Doubles and ordered Doubles
|
80
|
+
def reset
|
81
|
+
reset_ordered_doubles
|
82
|
+
reset_double_injections
|
83
|
+
reset_recorded_calls
|
84
|
+
end
|
85
|
+
|
86
|
+
# Verifies the DoubleInjection for the passed in subject and method_name.
|
87
|
+
def verify_double(subject, method_name)
|
88
|
+
@double_injections[subject][method_name].verify
|
89
|
+
ensure
|
90
|
+
reset_double subject, method_name
|
91
|
+
end
|
92
|
+
|
93
|
+
# Resets the DoubleInjection for the passed in subject and method_name.
|
94
|
+
def reset_double(subject, method_name)
|
95
|
+
double_injection = @double_injections[subject].delete(method_name)
|
96
|
+
@double_injections.delete(subject) if @double_injections[subject].empty?
|
97
|
+
double_injection.reset
|
98
|
+
end
|
99
|
+
|
100
|
+
def record_call(subject, method_name, arguments, block)
|
101
|
+
@recorded_calls << [subject, method_name, arguments, block]
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
# Removes the ordered Doubles from the list
|
106
|
+
def reset_ordered_doubles
|
107
|
+
@ordered_doubles.clear
|
108
|
+
end
|
109
|
+
|
110
|
+
# Resets the registered Doubles for the next test run.
|
111
|
+
def reset_double_injections
|
112
|
+
@double_injections.each do |subject, method_double_map|
|
113
|
+
method_double_map.keys.each do |method_name|
|
114
|
+
reset_double(subject, method_name)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def reset_recorded_calls
|
120
|
+
@recorded_calls.clear
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|