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,74 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../example_helper"
3
+
4
+ module RR
5
+ describe StubCreator, :shared => true do
6
+ before(:each) do
7
+ @space = Space.new
8
+ @subject = Object.new
9
+ end
10
+
11
+ it "initializes creator with passed in object" do
12
+ class << @creator
13
+ attr_reader :subject
14
+ end
15
+ @creator.subject.should === @subject
16
+ end
17
+ end
18
+
19
+ describe StubCreator, ".new without block" do
20
+ it_should_behave_like "RR::StubCreator"
21
+
22
+ before do
23
+ @creator = StubCreator.new(@space, @subject)
24
+ end
25
+ end
26
+
27
+ describe StubCreator, ".new with block" do
28
+ it_should_behave_like "RR::StubCreator"
29
+
30
+ before do
31
+ @creator = StubCreator.new(@space, @subject) do |c|
32
+ c.foobar(1, 2) {:one_two}
33
+ c.foobar(1) {:one}
34
+ c.foobar.with_any_args {:default}
35
+ c.baz() {:baz_result}
36
+ end
37
+ end
38
+
39
+ it "creates doubles" do
40
+ @subject.foobar(1, 2).should == :one_two
41
+ @subject.foobar(1, 2).should == :one_two
42
+ @subject.foobar(1).should == :one
43
+ @subject.foobar(1).should == :one
44
+ @subject.foobar(:something).should == :default
45
+ @subject.foobar(:something).should == :default
46
+ @subject.baz.should == :baz_result
47
+ @subject.baz.should == :baz_result
48
+ end
49
+ end
50
+
51
+ describe StubCreator, "#method_missing" do
52
+ it_should_behave_like "RR::StubCreator"
53
+
54
+ before do
55
+ @subject = Object.new
56
+ @creator = StubCreator.new(@space, @subject)
57
+ end
58
+
59
+ it "stubs the subject without any args" do
60
+ @creator.foobar {:baz}
61
+ @subject.foobar.should == :baz
62
+ end
63
+
64
+ it "stubs the subject mapping passed in args with the output" do
65
+ @creator.foobar(1, 2) {:one_two}
66
+ @creator.foobar(1) {:one}
67
+ @creator.foobar() {:nothing}
68
+ @subject.foobar.should == :nothing
69
+ @subject.foobar(1).should == :one
70
+ @subject.foobar(1, 2).should == :one_two
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,9 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../../environment_fixture_setup"
3
+ require "rr/adapters/test_unit"
4
+
5
+ require "test/unit"
6
+
7
+ class Test::Unit::TestCase
8
+ include RR::Adapters::TestUnit
9
+ end
@@ -0,0 +1,39 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/test_helper"
3
+
4
+ class TestUnitIntegrationTest < Test::Unit::TestCase
5
+ def setup
6
+ super
7
+ @subject = Object.new
8
+ end
9
+
10
+ def teardown
11
+ super
12
+ end
13
+
14
+ def test_using_a_mock
15
+ mock(@subject).foobar(1, 2) {:baz}
16
+ assert_equal :baz, @subject.foobar(1, 2)
17
+ end
18
+
19
+ def test_using_a_stub
20
+ stub(@subject).foobar {:baz}
21
+ assert_equal :baz, @subject.foobar("any", "thing")
22
+ end
23
+
24
+ def test_using_a_probe
25
+ def @subject.foobar
26
+ :baz
27
+ end
28
+
29
+ probe(@subject).foobar
30
+ assert_equal :baz, @subject.foobar
31
+ end
32
+
33
+ def test_times_called_verification
34
+ mock(@subject).foobar(1, 2) {:baz}
35
+ assert_raise RR::Expectations::TimesCalledExpectationError do
36
+ teardown
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ require "rubygems"
2
+ require "spec"
3
+
4
+ class RspecExampleSuite
5
+ def run
6
+ options = ::Spec::Runner::OptionParser.new.parse(ARGV.dup, STDERR, STDOUT, false)
7
+ $behaviour_runner = options.create_behaviour_runner
8
+
9
+ require_specs
10
+
11
+ puts "Running Rspec Example Suite"
12
+ $behaviour_runner.run(ARGV, false)
13
+ end
14
+
15
+ def require_specs
16
+ dir = File.dirname(__FILE__)
17
+ Dir["#{dir}/rr/rspec/**/*_example.rb"].each do |file|
18
+ require file
19
+ end
20
+ end
21
+ end
22
+
23
+ if $0 == __FILE__
24
+ RspecExampleSuite.new.run
25
+ end
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "spec"
3
+
4
+ class TestUnitTestSuite
5
+ def run
6
+ require_tests
7
+
8
+ puts "Running Test::Unit Test Suite"
9
+ end
10
+
11
+ def require_tests
12
+ dir = File.dirname(__FILE__)
13
+ Dir["#{dir}/rr/test_unit/**/*_test.rb"].each do |file|
14
+ require file
15
+ end
16
+ end
17
+ end
18
+
19
+ if $0 == __FILE__
20
+ TestUnitTestSuite.new.run
21
+ end
@@ -0,0 +1,27 @@
1
+ dir = File.dirname(__FILE__)
2
+
3
+ require "rr/double"
4
+
5
+ require "rr/mock_creator"
6
+ require "rr/stub_creator"
7
+ require "rr/probe_creator"
8
+ require "rr/do_not_allow_creator"
9
+
10
+ require "rr/scenario"
11
+ require "rr/space"
12
+
13
+ require "rr/scenario_not_found_error"
14
+ require "rr/scenario_order_error"
15
+
16
+ require "rr/expectations/argument_equality_expectation"
17
+ require "rr/expectations/any_argument_expectation"
18
+ require "rr/expectations/times_called_expectation"
19
+ require "rr/expectations/wildcard_matchers/anything"
20
+ require "rr/expectations/wildcard_matchers/is_a"
21
+ require "rr/expectations/wildcard_matchers/numeric"
22
+ require "rr/expectations/wildcard_matchers/boolean"
23
+ require "rr/expectations/wildcard_matchers/duck_type"
24
+ require "rr/expectations/wildcard_matchers/regexp"
25
+ require "rr/expectations/wildcard_matchers/range"
26
+
27
+ require "rr/extensions/double_methods"
@@ -0,0 +1,19 @@
1
+ require "spec/mocks"
2
+ require "rr"
3
+
4
+ module RR
5
+ module Adapters
6
+ module Rspec
7
+ include RR::Extensions::DoubleMethods
8
+ def setup_mocks_for_rspec
9
+ RR::Space.instance.reset_doubles
10
+ end
11
+ def verify_mocks_for_rspec
12
+ RR::Space.instance.verify_doubles
13
+ end
14
+ def teardown_mocks_for_rspec
15
+ RR::Space.instance.reset_doubles
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ require "spec/mocks"
2
+ require "rr"
3
+
4
+ module RR
5
+ module Adapters
6
+ module TestUnit
7
+ include RR::Extensions::DoubleMethods
8
+ def self.included(mod)
9
+ mod.class_eval do
10
+ alias_method :setup_without_rr, :setup
11
+ def setup_with_rr
12
+ setup_without_rr
13
+ RR::Space.instance.reset_doubles
14
+ end
15
+ alias_method :setup, :setup_with_rr
16
+
17
+ alias_method :teardown_without_rr, :teardown
18
+ def teardown_with_rr
19
+ RR::Space.instance.verify_doubles
20
+ teardown_without_rr
21
+ end
22
+ alias_method :teardown, :teardown_with_rr
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ module RR
2
+ # RR::DoNotAllowCreator uses RR::DoNotAllowCreator#method_missing to create
3
+ # a Scenario that expects never to be called.
4
+ #
5
+ # The following example mocks method_name with arg1 and arg2
6
+ # returning return_value.
7
+ #
8
+ # do_not_allow(subject).method_name(arg1, arg2) { return_value }
9
+ #
10
+ # The DoNotAllowCreator also supports a block sytnax.
11
+ #
12
+ # do_not_allow(subject) do |m|
13
+ # m.method1 # Do not allow method1 with any arguments
14
+ # m.method2(arg1, arg2) # Do not allow method2 with arguments arg1 and arg2
15
+ # m.method3.with_no_args # Do not allow method3 with no arguments
16
+ # end
17
+ class DoNotAllowCreator
18
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
19
+
20
+ def initialize(space, subject)
21
+ @space = space
22
+ @subject = subject
23
+ yield(self) if block_given?
24
+ end
25
+
26
+ protected
27
+ def method_missing(method_name, *args, &returns)
28
+ double = @space.create_double(@subject, method_name)
29
+ scenario = @space.create_scenario(double)
30
+ if args.empty?
31
+ scenario.with_any_args
32
+ else
33
+ scenario.with(*args)
34
+ end
35
+ scenario.never.returns(&returns)
36
+ scenario
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,91 @@
1
+ module RR
2
+ # RR::Double is the binding of an object and a method.
3
+ # A double has 0 to many Scenario objects. Each Scenario
4
+ # has Argument Expectations and Times called Expectations.
5
+ class Double
6
+ MethodArguments = Struct.new(:arguments, :block)
7
+ attr_reader :space, :object, :method_name, :original_method, :scenarios
8
+
9
+ def initialize(space, object, method_name)
10
+ @space = space
11
+ @object = object
12
+ @method_name = method_name.to_sym
13
+ @original_method = object.method(method_name) if @object.methods.include?(method_name.to_s)
14
+ @scenarios = []
15
+ end
16
+
17
+ # RR::Double#register_scenario adds the passed in Scenario
18
+ # into this Double's list of Scenario objects.
19
+ def register_scenario(scenario)
20
+ @scenarios << scenario
21
+ end
22
+
23
+ # RR::Double#bind injects a method that acts as a dispatcher
24
+ # that dispatches to the matching Scenario when the method
25
+ # is called.
26
+ def bind
27
+ define_implementation_placeholder
28
+ returns_method = <<-METHOD
29
+ def #{@method_name}(*args, &block)
30
+ arguments = MethodArguments.new(args, block)
31
+ #{placeholder_name}(arguments)
32
+ end
33
+ METHOD
34
+ meta.class_eval(returns_method, __FILE__, __LINE__ - 5)
35
+ end
36
+
37
+ # RR::Double#verify verifies each Scenario
38
+ # TimesCalledExpectation are met.
39
+ def verify
40
+ @scenarios.each do |scenario|
41
+ scenario.verify
42
+ end
43
+ end
44
+
45
+ # RR::Double#reset removes the injected dispatcher method.
46
+ # It binds the original method implementation on the object
47
+ # if one exists.
48
+ def reset
49
+ meta.send(:remove_method, placeholder_name)
50
+ if @original_method
51
+ meta.send(:define_method, @method_name, &@original_method)
52
+ else
53
+ meta.send(:remove_method, @method_name)
54
+ end
55
+ end
56
+
57
+ protected
58
+ def define_implementation_placeholder
59
+ me = self
60
+ meta.send(:define_method, placeholder_name) do |arguments|
61
+ me.send(:call_method, arguments.arguments, arguments.block)
62
+ end
63
+ end
64
+
65
+ def call_method(args, block)
66
+ matching_scenarios = []
67
+ @scenarios.each do |scenario|
68
+ if scenario.exact_match?(*args)
69
+ matching_scenarios << scenario
70
+ return scenario.call(*args, &block) unless scenario.times_called_verified?
71
+ end
72
+ end
73
+ @scenarios.each do |scenario|
74
+ if scenario.wildcard_match?(*args)
75
+ matching_scenarios << scenario
76
+ return scenario.call(*args, &block) unless scenario.times_called_verified?
77
+ end
78
+ end
79
+ matching_scenarios.first.call(*args) unless matching_scenarios.empty?
80
+ raise ScenarioNotFoundError, "No scenario for arguments #{args.inspect}"
81
+ end
82
+
83
+ def placeholder_name
84
+ "__rr__#{@method_name}__rr__"
85
+ end
86
+
87
+ def meta
88
+ (class << @object; self; end)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,17 @@
1
+ module RR
2
+ module Expectations
3
+ class AnyArgumentExpectation
4
+ def exact_match?(*arguments)
5
+ false
6
+ end
7
+
8
+ def wildcard_match?(*arguments)
9
+ true
10
+ end
11
+
12
+ def ==(other)
13
+ self.class == other.class
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ module RR
2
+ module Expectations
3
+ class ArgumentEqualityExpectationError < RuntimeError
4
+ end
5
+
6
+ class ArgumentEqualityExpectation
7
+ attr_reader :expected_arguments
8
+
9
+ def initialize(*expected_arguments)
10
+ @expected_arguments = expected_arguments
11
+ end
12
+
13
+ def exact_match?(*arguments)
14
+ @expected_arguments == arguments
15
+ end
16
+
17
+ def wildcard_match?(*arguments)
18
+ return false unless arguments.length == @expected_arguments.length
19
+ arguments.each_with_index do |arg, index|
20
+ expected_argument = @expected_arguments[index]
21
+ if expected_argument.respond_to?(:wildcard_match?)
22
+ return false unless expected_argument.wildcard_match?(arg)
23
+ else
24
+ return false unless expected_argument == arg
25
+ end
26
+ end
27
+ return true
28
+ end
29
+
30
+ def ==(other)
31
+ @expected_arguments == other.expected_arguments
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ module RR
2
+ module Expectations
3
+ class TimesCalledExpectationError < RuntimeError
4
+ end
5
+
6
+ class TimesCalledExpectation
7
+ attr_reader :times, :times_called
8
+
9
+ def initialize(times=nil, &time_condition_block)
10
+ raise ArgumentError, "Cannot pass in both an argument and a block" if times && time_condition_block
11
+ @times = times || time_condition_block
12
+ @times_called = 0
13
+ end
14
+
15
+ def verify_input
16
+ @times_called += 1
17
+ verify_input_error if @times.is_a?(Integer) && @times_called > @times
18
+ verify_input_error if @times.is_a?(Range) && @times_called > @times.end
19
+ return
20
+ end
21
+
22
+ def verify
23
+ return true if @times.is_a?(Integer) && @times == @times_called
24
+ return true if @times.is_a?(Proc) && @times.call(@times_called)
25
+ return true if @times.is_a?(Range) && @times.include?(@times_called)
26
+ return false
27
+ end
28
+
29
+ def verify!
30
+ raise TimesCalledExpectationError unless verify
31
+ end
32
+
33
+ protected
34
+ def verify_input_error
35
+ raise TimesCalledExpectationError, "Called #{@times_called.inspect} times. Expected #{@times.inspect}"
36
+ end
37
+ end
38
+ end
39
+ end