rr 0.1.0
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 +1 -0
- data/README +45 -0
- data/Rakefile +60 -0
- data/examples/environment_fixture_setup.rb +7 -0
- data/examples/example_helper.rb +8 -0
- data/examples/example_suite.rb +45 -0
- data/examples/high_level_example.rb +132 -0
- data/examples/rr/do_not_allow_creator_example.rb +90 -0
- data/examples/rr/double_bind_example.rb +32 -0
- data/examples/rr/double_dispatching_example.rb +167 -0
- data/examples/rr/double_example.rb +67 -0
- data/examples/rr/double_register_scenario_example.rb +25 -0
- data/examples/rr/double_reset_example.rb +60 -0
- data/examples/rr/double_verify_example.rb +24 -0
- data/examples/rr/expectations/any_argument_expectation_example.rb +43 -0
- data/examples/rr/expectations/anything_argument_equality_expectation_example.rb +39 -0
- data/examples/rr/expectations/argument_equality_expectation_example.rb +53 -0
- data/examples/rr/expectations/boolean_argument_equality_expectation_example.rb +49 -0
- data/examples/rr/expectations/duck_type_argument_equality_expectation_example.rb +68 -0
- data/examples/rr/expectations/is_a_argument_equality_expectation_example.rb +51 -0
- data/examples/rr/expectations/numeric_argument_equality_expectation_example.rb +47 -0
- data/examples/rr/expectations/range_argument_equality_expectation_example.rb +60 -0
- data/examples/rr/expectations/regexp_argument_equality_expectation_example.rb +68 -0
- data/examples/rr/expectations/times_called_expectation_example.rb +176 -0
- data/examples/rr/extensions/double_methods_example.rb +130 -0
- data/examples/rr/mock_creator_example.rb +65 -0
- data/examples/rr/probe_creator_example.rb +83 -0
- data/examples/rr/rspec/rspec_adapter_example.rb +64 -0
- data/examples/rr/rspec/rspec_usage_example.rb +38 -0
- data/examples/rr/scenario_example.rb +399 -0
- data/examples/rr/space_create_example.rb +185 -0
- data/examples/rr/space_example.rb +30 -0
- data/examples/rr/space_helper.rb +7 -0
- data/examples/rr/space_register_example.rb +33 -0
- data/examples/rr/space_reset_example.rb +73 -0
- data/examples/rr/space_verify_example.rb +146 -0
- data/examples/rr/stub_creator_example.rb +74 -0
- data/examples/rr/test_unit/test_helper.rb +9 -0
- data/examples/rr/test_unit/test_unit_integration_test.rb +39 -0
- data/examples/rspec_example_suite.rb +25 -0
- data/examples/test_unit_example_suite.rb +21 -0
- data/lib/rr.rb +27 -0
- data/lib/rr/adapters/rspec.rb +19 -0
- data/lib/rr/adapters/test_unit.rb +27 -0
- data/lib/rr/do_not_allow_creator.rb +39 -0
- data/lib/rr/double.rb +91 -0
- data/lib/rr/expectations/any_argument_expectation.rb +17 -0
- data/lib/rr/expectations/argument_equality_expectation.rb +35 -0
- data/lib/rr/expectations/times_called_expectation.rb +39 -0
- data/lib/rr/expectations/wildcard_matchers/anything.rb +15 -0
- data/lib/rr/expectations/wildcard_matchers/boolean.rb +20 -0
- data/lib/rr/expectations/wildcard_matchers/duck_type.rb +26 -0
- data/lib/rr/expectations/wildcard_matchers/is_a.rb +22 -0
- data/lib/rr/expectations/wildcard_matchers/numeric.rb +11 -0
- data/lib/rr/expectations/wildcard_matchers/range.rb +7 -0
- data/lib/rr/expectations/wildcard_matchers/regexp.rb +7 -0
- data/lib/rr/extensions/double_methods.rb +81 -0
- data/lib/rr/mock_creator.rb +32 -0
- data/lib/rr/probe_creator.rb +31 -0
- data/lib/rr/scenario.rb +184 -0
- data/lib/rr/scenario_not_found_error.rb +4 -0
- data/lib/rr/scenario_order_error.rb +4 -0
- data/lib/rr/space.rb +111 -0
- data/lib/rr/stub_creator.rb +36 -0
- metadata +113 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module RR
|
2
|
+
module Expectations
|
3
|
+
module WildcardMatchers
|
4
|
+
class Boolean
|
5
|
+
def wildcard_match?(other)
|
6
|
+
self == other || is_a_boolean?(other)
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(other)
|
10
|
+
other.is_a?(self.class)
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
def is_a_boolean?(subject)
|
15
|
+
subject.is_a?(TrueClass) || subject.is_a?(FalseClass)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RR
|
2
|
+
module Expectations
|
3
|
+
module WildcardMatchers
|
4
|
+
class DuckType
|
5
|
+
attr_accessor :required_methods
|
6
|
+
|
7
|
+
def initialize(*required_methods)
|
8
|
+
@required_methods = required_methods
|
9
|
+
end
|
10
|
+
|
11
|
+
def wildcard_match?(other)
|
12
|
+
return true if self == other
|
13
|
+
required_methods.each do |m|
|
14
|
+
return false unless other.respond_to?(m)
|
15
|
+
end
|
16
|
+
return true
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
return false unless other.is_a?(self.class)
|
21
|
+
self.required_methods == other.required_methods
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RR
|
2
|
+
module Expectations
|
3
|
+
module WildcardMatchers
|
4
|
+
class IsA
|
5
|
+
attr_reader :klass
|
6
|
+
|
7
|
+
def initialize(klass)
|
8
|
+
@klass = klass
|
9
|
+
end
|
10
|
+
|
11
|
+
def wildcard_match?(other)
|
12
|
+
self == other || other.is_a?(klass)
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
return false unless other.is_a?(self.class)
|
17
|
+
self.klass == other.klass
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module RR
|
2
|
+
module Extensions
|
3
|
+
module DoubleMethods
|
4
|
+
# Sets up a MockCreator that generates a Double Scenario that
|
5
|
+
# acts like a mock.
|
6
|
+
# mock(object).method_name(arg1, arg2) {return_value}
|
7
|
+
def mock(subject, &definition)
|
8
|
+
RR::Space.instance.create_mock_creator(subject, &definition)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sets up a StubCreator that generates a Double Scenario that
|
12
|
+
# acts like a stub.
|
13
|
+
# stub(object).method_name {return_value}
|
14
|
+
def stub(subject, &definition)
|
15
|
+
RR::Space.instance.create_stub_creator(subject, &definition)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Sets up a ProbeCreator that generates a Double Scenario that
|
19
|
+
# acts like a probe.
|
20
|
+
# probe(controller.template).render(:partial => "my/socks")
|
21
|
+
def probe(subject, &definition)
|
22
|
+
RR::Space.instance.create_probe_creator(subject, &definition)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sets up a DoNotAllowCreator that generates a Double Scenario that
|
26
|
+
# expects never to be called.
|
27
|
+
# do_not_allow(object).method_name
|
28
|
+
def do_not_allow(subject, &definition)
|
29
|
+
RR::Space.instance.create_do_not_allow_creator(subject, &definition)
|
30
|
+
end
|
31
|
+
alias_method :dont_allow, :do_not_allow
|
32
|
+
|
33
|
+
# Sets up an Anything wildcard ArgumentEqualityExpectation
|
34
|
+
# that succeeds when passed any argument.
|
35
|
+
# mock(object).method_name(anything) {return_value}
|
36
|
+
# object.method_name("an arbitrary value") # passes
|
37
|
+
def anything
|
38
|
+
RR::Expectations::WildcardMatchers::Anything.new
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sets up an IsA wildcard ArgumentEqualityExpectation
|
42
|
+
# that succeeds when passed an argument of a certain type.
|
43
|
+
# mock(object).method_name(is_a(String)) {return_value}
|
44
|
+
# object.method_name("A String") # passes
|
45
|
+
def is_a(klass)
|
46
|
+
RR::Expectations::WildcardMatchers::IsA.new(klass)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Sets up an Numeric wildcard ArgumentEqualityExpectation
|
50
|
+
# that succeeds when passed an argument that is ::Numeric.
|
51
|
+
# mock(object).method_name(numeric) {return_value}
|
52
|
+
# object.method_name(99) # passes
|
53
|
+
def numeric
|
54
|
+
RR::Expectations::WildcardMatchers::Numeric.new
|
55
|
+
end
|
56
|
+
|
57
|
+
# Sets up an Boolean wildcard ArgumentEqualityExpectation
|
58
|
+
# that succeeds when passed an argument that is a ::Boolean.
|
59
|
+
# mock(object).method_name(boolean) {return_value}
|
60
|
+
# object.method_name(false) # passes
|
61
|
+
def boolean
|
62
|
+
RR::Expectations::WildcardMatchers::Boolean.new
|
63
|
+
end
|
64
|
+
|
65
|
+
# Sets up a DuckType wildcard ArgumentEqualityExpectation
|
66
|
+
# that succeeds when passed the argument implements the methods.
|
67
|
+
# arg = Object.new
|
68
|
+
# def arg.foo; end
|
69
|
+
# def arg.bar; end
|
70
|
+
# mock(object).method_name(duck_type(:foo, :bar)) {return_value}
|
71
|
+
# object.method_name(arg) # passes
|
72
|
+
def duck_type(*args)
|
73
|
+
RR::Expectations::WildcardMatchers::DuckType.new(*args)
|
74
|
+
end
|
75
|
+
|
76
|
+
instance_methods.each do |name|
|
77
|
+
alias_method "rr_#{name}", name
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RR
|
2
|
+
# RR::MockCreator uses RR::MockCreator#method_missing to create
|
3
|
+
# a Scenario that acts like a mock.
|
4
|
+
#
|
5
|
+
# The following example mocks method_name with arg1 and arg2
|
6
|
+
# returning return_value.
|
7
|
+
#
|
8
|
+
# mock(subject).method_name(arg1, arg2) { return_value }
|
9
|
+
#
|
10
|
+
# The MockCreator also supports a block sytnax.
|
11
|
+
#
|
12
|
+
# mock(subject) do |m|
|
13
|
+
# m.method_name(arg1, arg2) { return_value }
|
14
|
+
# end
|
15
|
+
class MockCreator
|
16
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
|
17
|
+
|
18
|
+
def initialize(space, subject)
|
19
|
+
@space = space
|
20
|
+
@subject = subject
|
21
|
+
yield(self) if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
def method_missing(method_name, *args, &returns)
|
26
|
+
double = @space.create_double(@subject, method_name)
|
27
|
+
scenario = @space.create_scenario(double)
|
28
|
+
scenario.with(*args).once.returns(&returns)
|
29
|
+
scenario
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RR
|
2
|
+
# RR::ProbeCreator uses RR::ProbeCreator#method_missing to create
|
3
|
+
# a Scenario that acts like a probe.
|
4
|
+
#
|
5
|
+
# The following example probes method_name with arg1 and arg2
|
6
|
+
# returning return_value.
|
7
|
+
#
|
8
|
+
# probe(subject).method_name(arg1, arg2) { return_value }
|
9
|
+
#
|
10
|
+
# The ProbeCreator also supports a block sytnax.
|
11
|
+
#
|
12
|
+
# probe(subject) do |m|
|
13
|
+
# m.method_name(arg1, arg2) { return_value }
|
14
|
+
# end
|
15
|
+
class ProbeCreator
|
16
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
|
17
|
+
|
18
|
+
def initialize(space, subject)
|
19
|
+
@space = space
|
20
|
+
@subject = subject
|
21
|
+
yield(self) if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
def method_missing(method_name, *args, &returns)
|
26
|
+
double = @space.create_double(@subject, method_name)
|
27
|
+
scenario = @space.create_scenario(double)
|
28
|
+
scenario.with(*args).once.implemented_by(double.original_method)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/rr/scenario.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
module RR
|
2
|
+
# RR::Scenario is the use case for a method call.
|
3
|
+
# It has the ArgumentEqualityExpectation, TimesCalledExpectation,
|
4
|
+
# and the implementation.
|
5
|
+
class Scenario
|
6
|
+
attr_reader :times_called, :argument_expectation, :times_called_expectation
|
7
|
+
|
8
|
+
def initialize(space)
|
9
|
+
@space = space
|
10
|
+
@implementation = nil
|
11
|
+
@argument_expectation = nil
|
12
|
+
@times_called_expectation = nil
|
13
|
+
@times_called = 0
|
14
|
+
end
|
15
|
+
|
16
|
+
# Scenario#with creates an ArgumentEqualityExpectation for the
|
17
|
+
# Scenario. it takes a list of expected arguments.
|
18
|
+
#
|
19
|
+
# Passing in a block sets the return value.
|
20
|
+
#
|
21
|
+
# mock(subject).method_name.with(1, 2) {:return_value}
|
22
|
+
def with(*args, &returns)
|
23
|
+
@argument_expectation = Expectations::ArgumentEqualityExpectation.new(*args)
|
24
|
+
returns(&returns) if returns
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
# Scenario#with_any_args creates an AnyArgumentEqualityExpectation
|
29
|
+
# for the Scenario.
|
30
|
+
#
|
31
|
+
# Passing in a block sets the return value.
|
32
|
+
#
|
33
|
+
# mock(subject).method_name.with_any_args {:return_value}
|
34
|
+
def with_any_args(&returns)
|
35
|
+
@argument_expectation = Expectations::AnyArgumentExpectation.new
|
36
|
+
returns(&returns) if returns
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Scenario#with_no_args creates an ArgumentEqualityExpectation with
|
41
|
+
# no arguments for the Scenario.
|
42
|
+
#
|
43
|
+
# Passing in a block sets the return value.
|
44
|
+
#
|
45
|
+
# mock(subject).method_name.with_no_args {:return_value}
|
46
|
+
def with_no_args(&returns)
|
47
|
+
@argument_expectation = Expectations::ArgumentEqualityExpectation.new()
|
48
|
+
returns(&returns) if returns
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
# Scenario#never creates an TimesCalledExpectation of 0.
|
53
|
+
#
|
54
|
+
# This method does not accept a block because it will never be called.
|
55
|
+
#
|
56
|
+
# mock(subject).method_name.never
|
57
|
+
def never
|
58
|
+
@times_called_expectation = Expectations::TimesCalledExpectation.new(0)
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Scenario#once creates an TimesCalledExpectation of 1.
|
63
|
+
#
|
64
|
+
# Passing in a block sets the return value.
|
65
|
+
#
|
66
|
+
# mock(subject).method_name.once {:return_value}
|
67
|
+
def once(&returns)
|
68
|
+
@times_called_expectation = Expectations::TimesCalledExpectation.new(1)
|
69
|
+
returns(&returns) if returns
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
# Scenario#twice creates an TimesCalledExpectation of 2.
|
74
|
+
#
|
75
|
+
# Passing in a block sets the return value.
|
76
|
+
#
|
77
|
+
# mock(subject).method_name.twice {:return_value}
|
78
|
+
def twice(&returns)
|
79
|
+
@times_called_expectation = Expectations::TimesCalledExpectation.new(2)
|
80
|
+
returns(&returns) if returns
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
# Scenario#twice creates an TimesCalledExpectation of the passed
|
85
|
+
# in number.
|
86
|
+
#
|
87
|
+
# Passing in a block sets the return value.
|
88
|
+
#
|
89
|
+
# mock(subject).method_name.times(4) {:return_value}
|
90
|
+
def times(number, &returns)
|
91
|
+
@times_called_expectation = Expectations::TimesCalledExpectation.new(number)
|
92
|
+
returns(&returns) if returns
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
# Scenario#ordered sets the Scenario to have an ordered
|
97
|
+
# expectation.
|
98
|
+
#
|
99
|
+
# Passing in a block sets the return value.
|
100
|
+
#
|
101
|
+
# mock(subject).method_name.ordered {return_value}
|
102
|
+
def ordered(&returns)
|
103
|
+
@ordered = true
|
104
|
+
@space.ordered_scenarios << self unless @space.ordered_scenarios.include?(self)
|
105
|
+
returns(&returns) if returns
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
# Scenario#ordered? returns true when the Scenario is ordered.
|
110
|
+
#
|
111
|
+
# mock(subject).method_name.ordered?
|
112
|
+
def ordered?
|
113
|
+
@ordered
|
114
|
+
end
|
115
|
+
|
116
|
+
# Scenario#returns causes Scenario to return the return value of
|
117
|
+
# the passed in block.
|
118
|
+
def returns(&implementation)
|
119
|
+
implemented_by implementation
|
120
|
+
end
|
121
|
+
|
122
|
+
# Scenario#implemented_by sets the implementation of the Scenario.
|
123
|
+
# This method takes a Proc or a Method. Passing in a Method allows
|
124
|
+
# the Scenario to accept blocks.
|
125
|
+
#
|
126
|
+
# obj = Object.new
|
127
|
+
# def obj.foobar
|
128
|
+
# yield(1)
|
129
|
+
# end
|
130
|
+
# mock(obj).method_name.implemented_by(obj.method(:foobar))
|
131
|
+
def implemented_by(implementation)
|
132
|
+
@implementation = implementation
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
# Scenario#call calls the Scenario's implementation. The return
|
137
|
+
# value of the implementation is returned.
|
138
|
+
#
|
139
|
+
# A TimesCalledExpectationError is raised when the times called
|
140
|
+
# exceeds the expected TimesCalledExpectation.
|
141
|
+
def call(*args, &block)
|
142
|
+
@times_called_expectation.verify_input if @times_called_expectation
|
143
|
+
@space.verify_ordered_scenario(self) if ordered?
|
144
|
+
return nil unless @implementation
|
145
|
+
|
146
|
+
if @implementation.is_a?(Method)
|
147
|
+
return @implementation.call(*args, &block)
|
148
|
+
else
|
149
|
+
args << block if block
|
150
|
+
return @implementation.call(*args)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Scenario#exact_match? returns true when the passed in arguments
|
155
|
+
# exactly match the ArgumentEqualityExpectation arguments.
|
156
|
+
def exact_match?(*arguments)
|
157
|
+
return false unless @argument_expectation
|
158
|
+
@argument_expectation.exact_match?(*arguments)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Scenario#wildcard_match? returns true when the passed in arguments
|
162
|
+
# wildcard match the ArgumentEqualityExpectation arguments.
|
163
|
+
def wildcard_match?(*arguments)
|
164
|
+
return false unless @argument_expectation
|
165
|
+
@argument_expectation.wildcard_match?(*arguments)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Scenario#times_called_verified? returns true when the
|
169
|
+
# TimesCalledExpectation is satisfied.
|
170
|
+
def times_called_verified?
|
171
|
+
return false unless @times_called_expectation
|
172
|
+
@times_called_expectation.verify
|
173
|
+
end
|
174
|
+
|
175
|
+
# Scenario#verify verifies the the TimesCalledExpectation
|
176
|
+
# is satisfied for this scenario. A TimesCalledExpectationError
|
177
|
+
# is raised if the TimesCalledExpectation is not met.
|
178
|
+
def verify
|
179
|
+
return true unless @times_called_expectation
|
180
|
+
@times_called_expectation.verify!
|
181
|
+
true
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|