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.
Files changed (65) hide show
  1. data/CHANGES +1 -0
  2. data/README +45 -0
  3. data/Rakefile +60 -0
  4. data/examples/environment_fixture_setup.rb +7 -0
  5. data/examples/example_helper.rb +8 -0
  6. data/examples/example_suite.rb +45 -0
  7. data/examples/high_level_example.rb +132 -0
  8. data/examples/rr/do_not_allow_creator_example.rb +90 -0
  9. data/examples/rr/double_bind_example.rb +32 -0
  10. data/examples/rr/double_dispatching_example.rb +167 -0
  11. data/examples/rr/double_example.rb +67 -0
  12. data/examples/rr/double_register_scenario_example.rb +25 -0
  13. data/examples/rr/double_reset_example.rb +60 -0
  14. data/examples/rr/double_verify_example.rb +24 -0
  15. data/examples/rr/expectations/any_argument_expectation_example.rb +43 -0
  16. data/examples/rr/expectations/anything_argument_equality_expectation_example.rb +39 -0
  17. data/examples/rr/expectations/argument_equality_expectation_example.rb +53 -0
  18. data/examples/rr/expectations/boolean_argument_equality_expectation_example.rb +49 -0
  19. data/examples/rr/expectations/duck_type_argument_equality_expectation_example.rb +68 -0
  20. data/examples/rr/expectations/is_a_argument_equality_expectation_example.rb +51 -0
  21. data/examples/rr/expectations/numeric_argument_equality_expectation_example.rb +47 -0
  22. data/examples/rr/expectations/range_argument_equality_expectation_example.rb +60 -0
  23. data/examples/rr/expectations/regexp_argument_equality_expectation_example.rb +68 -0
  24. data/examples/rr/expectations/times_called_expectation_example.rb +176 -0
  25. data/examples/rr/extensions/double_methods_example.rb +130 -0
  26. data/examples/rr/mock_creator_example.rb +65 -0
  27. data/examples/rr/probe_creator_example.rb +83 -0
  28. data/examples/rr/rspec/rspec_adapter_example.rb +64 -0
  29. data/examples/rr/rspec/rspec_usage_example.rb +38 -0
  30. data/examples/rr/scenario_example.rb +399 -0
  31. data/examples/rr/space_create_example.rb +185 -0
  32. data/examples/rr/space_example.rb +30 -0
  33. data/examples/rr/space_helper.rb +7 -0
  34. data/examples/rr/space_register_example.rb +33 -0
  35. data/examples/rr/space_reset_example.rb +73 -0
  36. data/examples/rr/space_verify_example.rb +146 -0
  37. data/examples/rr/stub_creator_example.rb +74 -0
  38. data/examples/rr/test_unit/test_helper.rb +9 -0
  39. data/examples/rr/test_unit/test_unit_integration_test.rb +39 -0
  40. data/examples/rspec_example_suite.rb +25 -0
  41. data/examples/test_unit_example_suite.rb +21 -0
  42. data/lib/rr.rb +27 -0
  43. data/lib/rr/adapters/rspec.rb +19 -0
  44. data/lib/rr/adapters/test_unit.rb +27 -0
  45. data/lib/rr/do_not_allow_creator.rb +39 -0
  46. data/lib/rr/double.rb +91 -0
  47. data/lib/rr/expectations/any_argument_expectation.rb +17 -0
  48. data/lib/rr/expectations/argument_equality_expectation.rb +35 -0
  49. data/lib/rr/expectations/times_called_expectation.rb +39 -0
  50. data/lib/rr/expectations/wildcard_matchers/anything.rb +15 -0
  51. data/lib/rr/expectations/wildcard_matchers/boolean.rb +20 -0
  52. data/lib/rr/expectations/wildcard_matchers/duck_type.rb +26 -0
  53. data/lib/rr/expectations/wildcard_matchers/is_a.rb +22 -0
  54. data/lib/rr/expectations/wildcard_matchers/numeric.rb +11 -0
  55. data/lib/rr/expectations/wildcard_matchers/range.rb +7 -0
  56. data/lib/rr/expectations/wildcard_matchers/regexp.rb +7 -0
  57. data/lib/rr/extensions/double_methods.rb +81 -0
  58. data/lib/rr/mock_creator.rb +32 -0
  59. data/lib/rr/probe_creator.rb +31 -0
  60. data/lib/rr/scenario.rb +184 -0
  61. data/lib/rr/scenario_not_found_error.rb +4 -0
  62. data/lib/rr/scenario_order_error.rb +4 -0
  63. data/lib/rr/space.rb +111 -0
  64. data/lib/rr/stub_creator.rb +36 -0
  65. metadata +113 -0
@@ -0,0 +1,15 @@
1
+ module RR
2
+ module Expectations
3
+ module WildcardMatchers
4
+ class Anything
5
+ def wildcard_match?(other)
6
+ true
7
+ end
8
+
9
+ def ==(other)
10
+ other.is_a?(self.class)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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,11 @@
1
+ module RR
2
+ module Expectations
3
+ module WildcardMatchers
4
+ class Numeric < IsA
5
+ def initialize
6
+ @klass = ::Numeric
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ class Range
2
+ def wildcard_match?(other)
3
+ return true if self == other
4
+ return false unless other.is_a?(Numeric)
5
+ self.include?(other)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Regexp
2
+ def wildcard_match?(other)
3
+ return true if self == other
4
+ return false unless other.is_a?(String)
5
+ (other =~ self) ? true : false
6
+ end
7
+ 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
@@ -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