handoff 0.0.1 → 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/README CHANGED
@@ -3,8 +3,9 @@ stdlib module +Forwardable+ makes implementing delegation simple and readable.
3
3
  Handoff is intended to make the code to test it just as simple and
4
4
  readable.
5
5
 
6
- Handoff depends on Mocha and Stubba (http://mocha.rubyforge.org/).
6
+ Handoff depends on Mocha and Stubba (http://mocha.rubyforge.org).
7
7
 
8
- Here's example_test.rb to give you an idea of what your tests will look like:
8
+ Here's example_test.rb to give you an idea of what you can do:
9
9
 
10
10
  :include: test/example_test.rb
11
+
@@ -1,18 +1,15 @@
1
1
  module Handoff
2
- =begin rdoc
3
- Module mixed into a *args array to munge it into a hash keying delegating
4
- method names to target method names.
5
- =end
2
+ # Handoff internal.
6
3
  module ArgumentNormalizer
7
4
 
8
5
  def to_method_map
9
- return self.first if self.size == 1 && self.first.is_a?(Hash)
6
+ return first if size == 1 && first.respond_to?(:each_pair)
10
7
  to_self_referencing_hash
11
8
  end
12
9
 
13
10
  def to_self_referencing_hash
14
11
  hash = Hash.new
15
- self.each {|sym| hash[sym]=sym}
12
+ each {|method_symbol| hash[method_symbol] = method_symbol}
16
13
  hash
17
14
  end
18
15
 
@@ -1,22 +1,28 @@
1
1
  require 'handoff/argument_normalizer'
2
+ require 'handoff/usage_error'
2
3
 
3
4
  module Handoff
4
5
  # This is where the fluent stuff is.
5
6
  # Get an assertion by calling Handoff#assert_handoff. Specify the delegating
6
- # behavior with from or from_any, to, and for_method or for_methods.
7
+ # behavior with
8
+ # * +from+ or +from_any+,
9
+ # * +to+,
10
+ # * +for+, +for_method+ or +for_methods+, and
11
+ # * (optionally) +with+ or +with_arguments+.
7
12
  class Assertion
8
13
 
14
+ # :stopdoc:
15
+
9
16
  attr_reader :method_map
10
- attr_reader :object_under_test
11
- private :method_map, :object_under_test
17
+ attr_reader :delegator
18
+ private :method_map, :delegator
12
19
 
13
- # Creates an expectation on the mock delegate that it will be touched later
14
- # to give a failure if the user forgets to specify everything required.
15
20
  def initialize mock_delegate
16
21
  @mock_delegate = mock_delegate
17
- @mock_delegate.expects(:called!)
18
22
  end
19
23
 
24
+ # :startdoc:
25
+
20
26
  # Creates an instance of +class_under_test+ with no arguments and uses it
21
27
  # as the object under test.
22
28
  def from_any class_under_test
@@ -24,20 +30,20 @@ module Handoff
24
30
  end
25
31
 
26
32
  # Specifies the object being tested, returning self.
27
- def from object_under_test
28
- @object_under_test = object_under_test
33
+ def from delegator
34
+ @delegator = delegator
29
35
  self
30
36
  end
31
37
 
32
38
  # Specifies the accessor the object under test will use to acquire its delegate,
33
39
  # returning self.
34
40
  def to(delegate_accessor)
35
- @object_under_test.expects(delegate_accessor).at_least_once.returns(@mock_delegate)
41
+ @delegate_accessor = delegate_accessor
36
42
  self
37
43
  end
38
44
 
39
45
  # Tells the assertion what delegating methods to test and what methods in the delegate
40
- # they should delegate to. Then actually does the testing.
46
+ # they should delegate to.
41
47
  #
42
48
  # :call-seq: for_methods(:delegating_method1, :delegating_method2, ...)
43
49
  # for_methods({:delegating_method1 => :target_method1, :delegating_method2 => :target_method2})
@@ -46,25 +52,49 @@ module Handoff
46
52
  # a Hash mapping methods in the delegating class to methods in the delegate.
47
53
  def for_methods(*args)
48
54
  @method_map = args.extend(ArgumentNormalizer).to_method_map
49
- conduct
55
+ self
50
56
  end
51
- alias for_method for_methods
57
+ alias for_method for_methods
58
+ alias for for_methods
59
+
60
+ def with_arguments *args
61
+ @args = args
62
+ end
63
+ alias with with_arguments
64
+
65
+ # Specifies that the assertion should pass no arguments to the delegating method and
66
+ # require that no arguments are sent to the target method.
67
+ # Equivalent to calling +with_arguments+ with no arguments.
68
+ def with_no_arguments
69
+ with_arguments
70
+ end
71
+
72
+ # :stopdoc:
52
73
 
53
- private
54
74
  def conduct
55
- @mock_delegate.called!
75
+ validate_sufficiently_specified
76
+ @delegator.expects(@delegate_accessor).at_least_once.returns(@mock_delegate)
56
77
  expected_returns = {}
57
78
  method_map.each_pair do |delegating_method, target|
58
79
  expected_returns[delegating_method] = "#{delegating_method.to_s}_return"
59
- @mock_delegate.expects(target).returns(expected_returns[delegating_method])
80
+ expectation = @mock_delegate.expects(target).returns(expected_returns[delegating_method])
81
+ expectation.with(*@args) unless @args.nil?
60
82
  end
61
83
  method_map.each_pair do |delegating_method, target|
62
- actual = object_under_test.send(delegating_method)
84
+ send_args = [delegating_method, @args].flatten
85
+ actual = delegator.send(*send_args)
63
86
  unless actual.equal? expected_returns[delegating_method]
64
- raise "expected #{delegating_method.to_s} to return #{expected_returns[method]}, but was #{actual}"
87
+ raise "expected #{delegating_method.to_s} to return #{expected_returns[delegating_method]}, but was #{actual}"
65
88
  end
66
89
  end
67
90
  end
68
91
 
92
+ private
93
+ def validate_sufficiently_specified
94
+ raise UsageError, "Object under test must be specified with 'from' or 'from_any'" unless @delegator
95
+ raise UsageError, "Delegate accessor must be specified with 'to'" unless @delegate_accessor
96
+ raise UsageError, "Delegating methods must be specified with 'for_method' or 'for_methods'" unless @method_map
97
+ end
98
+ # :startdoc:
69
99
  end
70
100
  end
@@ -0,0 +1,4 @@
1
+ module Handoff
2
+ class UsageError < RuntimeError
3
+ end
4
+ end
data/lib/handoff.rb CHANGED
@@ -1,18 +1,40 @@
1
1
  require 'handoff/assertion'
2
- require 'test/unit'
3
2
 
4
3
  # +require+ (or +require_gem+) 'handoff' and #assert_handoff can
5
- # be called from any +Test::Unit::TestCase+ test method to create
4
+ # be called from any Test::Unit::TestCase test method to create
6
5
  # a new Handoff::Assertion.
7
6
  #
8
- # After defining Handoff, This file includes it in Test::Unit::TestCase.
7
+ # After defining Handoff, this file includes it in Test::Unit::TestCase.
9
8
  module Handoff
10
9
 
11
10
  # Creates an Assertion, handing a it a mock named 'handoff delegate' on which
12
11
  # expectations will be set.
13
12
  def assert_handoff
14
- Handoff::Assertion.new(self.mock('handoff delegate'))
13
+ a = Handoff::Assertion.new(self.mock('handoff delegate'))
14
+ handoff_assertions << a
15
+ a
15
16
  end
17
+
18
+ def handoff_assertions
19
+ @handoff_assertions ||= []
20
+ end
21
+
22
+ def conduct_and_clear_handoffs
23
+ begin
24
+ handoff_assertions.each {|a| a.conduct }
25
+ ensure
26
+ handoff_assertions.clear
27
+ end
28
+ end
29
+
30
+ def self.included(base)
31
+ base.class_eval do
32
+ add_teardown_method :conduct_and_clear_handoffs
33
+ end
34
+ end
35
+
16
36
  end
17
37
 
18
- Test::Unit::TestCase.send :include, Handoff
38
+ class Test::Unit::TestCase
39
+ include Handoff unless ancestors.include?(Handoff)
40
+ end
data/test/example_test.rb CHANGED
@@ -1,30 +1,32 @@
1
1
  # This example is only helpful if you're familiar with Forwardable from the
2
2
  # stdlib, which you should be if you're coding delegating behavior in Ruby.
3
- # Here comes an example of something you might have in your code base, but
4
- # haven't managed to test readably. You might have some code like this that's
5
- # tested in very long methods which either repeat but varying set-ups
6
- # Foo is a simple delegating class, which normally wouldn't live in
7
- # your test.
3
+ #
4
+ # Foo is a simple delegating class. We want to specify that delegation in
5
+ # its unit test.
8
6
  class Foo
9
7
  require 'forwardable'
10
8
  extend Forwardable
11
9
  def_delegators :bar, :baz, :bat
12
10
  def_delegator :bar, :baq, :bar_baq
13
11
  def_delegator :bar, :ban, :bar_ban
12
+
13
+ # Foo#bar gets the delegate. This is not what Handoff tests.
14
+ # Rather, Handoff will mock this method and ensure that the
15
+ # delegating methods defined above use it correctly.
14
16
  def bar
15
- @bar ||= BarFactory.create_for(Foo) # gets the delegate ... this is not what we're testing
17
+ @bar ||= BarFactory.create
16
18
  end
17
19
  end
18
20
 
19
21
  require 'test/unit'
20
22
  require 'rubygems'
21
23
 
22
- require File.dirname(__FILE__) + '/../lib/handoff' # I require it this way because I don't want the installed gem.
23
- # require 'handoff' # this is how you'd require handoff (or do require_gem w/ a version).
24
-
25
- require 'mocha' # you need to require mocha and stubba in your test
24
+ require 'mocha' # You need to require mocha and stubba in your test.
26
25
  require 'stubba' # ditto
27
26
 
27
+ require File.dirname(__FILE__) + '/../lib/handoff' # I require it this way because I don't want the installed gem.
28
+ # require 'handoff' # This is how you'd require handoff (or do require_gem w/ a version).
29
+
28
30
  class ExampleTest < Test::Unit::TestCase
29
31
 
30
32
  def test_using_from_and_single_method
@@ -36,6 +38,15 @@ class ExampleTest < Test::Unit::TestCase
36
38
  end
37
39
 
38
40
  def test_showing_delegating_methods_with_names_different_than_their_delegate_methods
39
- assert_handoff.from(Foo.new).to(:bar).for_methods(:bar_baq => :baq, :bar_ban => :ban)
41
+ assert_handoff.from(Foo.new).to(:bar).for(:bar_baq => :baq, :bar_ban => :ban)
40
42
  end
43
+
44
+ def test_specifying_args
45
+ assert_handoff.from(Foo.new).to(:bar).for(:baz).with('arg1', 2, :three)
46
+ end
47
+
48
+ def test_specifying_some_arg
49
+ assert_handoff.from(Foo.new).to(:bar).for(:baz).with_no_arguments
50
+ end
51
+
41
52
  end
@@ -9,32 +9,88 @@ module Handoff
9
9
  class AssertionTest < Test::Unit::TestCase
10
10
 
11
11
  test 'should construct instance to test and return self when from_any is called' do
12
- mock_class = mock(:new => nil)
13
- mock_delegate = mock
14
- assertion = Assertion.new(mock_delegate)
15
- mock_delegate.expectations.clear
16
- assert_same assertion, assertion.from_any(mock_class)
12
+ assertion = Assertion.new(nil)
13
+ assert_same assertion, assertion.from_any(mock('class under test', :new => nil))
17
14
  end
18
15
 
19
- test 'should create expectation on creation to guard against incomplete usage' do
20
- mock_delegate = mock
21
- Assertion.new(mock_delegate)
22
- assert_equal :called!, mock_delegate.expectations.first.method_name
23
- mock_delegate.expectations.clear
24
- end
25
-
26
- test 'should create expectations on mock_delegate and object under test once fully specified' do
27
- cut = define_class(Object, <<-EOS)
16
+ test 'should create expectations on mock_delegate and object under test upon conduct' do
17
+ cut = Class.new do
28
18
  extend Forwardable
29
19
  def_delegator :asdf, :meth
30
20
  attr_reader :asdf
31
- EOS
21
+ end
32
22
  mock_delegate = mock
33
23
  object_under_test = cut.new
34
- Assertion.new(mock_delegate).from(object_under_test).to(:asdf).for_method(:meth)
24
+ assertion = Assertion.new(mock_delegate).from(object_under_test).to(:asdf).for_method(:meth)
25
+ assertion.conduct
35
26
  assert_equal :asdf, object_under_test.mocha.expectations.first.method_name
36
27
  assert_equal :meth, mock_delegate.expectations.last.method_name
37
28
  end
38
29
 
30
+ test 'should create mock_delegate expectation with arguments when supplied' do
31
+ delegator = Class.new do
32
+ extend Forwardable
33
+ def_delegator :delegate, :meth
34
+ attr_reader :delegate
35
+ end.new
36
+ assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
37
+ assertion.with('arg1', :arg2, 3)
38
+ assertion.conduct
39
+ assert_true mock_delegate.expectations.first.match?(:meth, 'arg1', :arg2, 3)
40
+ assert_false mock_delegate.expectations.first.match?(:meth)
41
+ assert_false mock_delegate.expectations.first.match?(:meth, 'arg1')
42
+ end
43
+
44
+ {
45
+ :with => lambda {|assertion| assertion.with},
46
+ :with_no_arguments => lambda {|assertion| assertion.with_no_arguments}
47
+ }.each_pair do |method, proc|
48
+ test "should force no args when #{method} is used to specify none" do
49
+ delegator = Class.new do
50
+ extend Forwardable
51
+ def_delegator :delegate, :meth
52
+ attr_reader :delegate
53
+ end.new
54
+ assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
55
+ proc.call(assertion)
56
+ assertion.conduct
57
+ assert_true mock_delegate.expectations.first.match?(:meth)
58
+ assert_false mock_delegate.expectations.first.match?(:meth, 'arg1')
59
+ end
60
+ end
61
+
62
+ test 'should allow for args or no args if nothing specified' do
63
+ delegator = Class.new do
64
+ extend Forwardable
65
+ def_delegator :delegate, :meth
66
+ attr_reader :delegate
67
+ end.new
68
+ assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
69
+ assertion.conduct
70
+ assert_true mock_delegate.expectations.first.match?(:meth)
71
+ assert_true mock_delegate.expectations.first.match?(:meth, 'arg1')
72
+ end
73
+
74
+ test 'validates presence of delegator, delegate accessor, and delegating methods' do
75
+ assertion = Assertion.new(mock).from(nil).to(:asdf).for_method(:meth)
76
+ assert_raises(UsageError) { assertion.conduct }
77
+
78
+ assertion = Assertion.new(mock).from("whatever").to(nil).for_method(:meth)
79
+ assert_raises(UsageError) { assertion.conduct }
80
+
81
+ assertion = Assertion.new(mock).from("whatever").to(:asdf)
82
+ assert_raises(UsageError) { assertion.conduct }
83
+ end
84
+
85
+ test 'for, for_method, and for_methods are aliases' do
86
+ a = Assertion.new nil
87
+ assert_equal a.method(:for_methods), a.method(:for_method)
88
+ assert_equal a.method(:for_methods), a.method(:for)
89
+ end
90
+
91
+ test 'with_arguments and with are aliases' do
92
+ a = Assertion.new nil
93
+ assert_equal a.method(:with_arguments), a.method(:with)
94
+ end
39
95
  end
40
- end
96
+ end
data/test/handoff_test.rb CHANGED
@@ -13,5 +13,20 @@ class HandoffTest < Test::Unit::TestCase
13
13
  end
14
14
  Assertion.expects(:new).with(:mock_return).returns(:new_assertion)
15
15
  assert_equal :new_assertion, assert_handoff
16
+ assert_equal :new_assertion, handoff_assertions.delete(:new_assertion)
17
+ assert_true handoff_assertions.empty?
18
+ end
19
+
20
+ test 'should add a teardown method for conducting handoff assertions' do
21
+ assert_true self.class.teardown_methods.include?(:conduct_and_clear_handoffs)
22
+ end
23
+
24
+ test 'should conduct each assertion in conduct_and_clear_handoffs' do
25
+ handoff_assertions << assertion1 = mock(:conduct => nil)
26
+ handoff_assertions << assertion2 = mock(:conduct => nil)
27
+ conduct_and_clear_handoffs
28
+ assert_true handoff_assertions.empty?
29
+ assertion1.verify
30
+ assertion2.verify
16
31
  end
17
32
  end
@@ -0,0 +1,10 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class TestHelperTest < Test::Unit::TestCase
4
+
5
+ test 'assert_true passes only for true, not for non-nulls' do
6
+ assert_true true
7
+ assert_raises(Test::Unit::AssertionFailedError) { assert_true 'foo' }
8
+ end
9
+
10
+ end
metadata CHANGED
@@ -3,9 +3,9 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: handoff
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2007-01-17 00:00:00 -05:00
8
- summary: Handoff is a fluent interface for making test assertions about simple delegation.
6
+ version: 0.1.0
7
+ date: 2007-02-17 00:00:00 -05:00
8
+ summary: Handoff is a fluent interface for making test assertions about delegation.
9
9
  require_paths:
10
10
  - lib
11
11
  email: ""
@@ -33,10 +33,12 @@ files:
33
33
  - lib/handoff.rb
34
34
  - lib/handoff/argument_normalizer.rb
35
35
  - lib/handoff/assertion.rb
36
+ - lib/handoff/usage_error.rb
36
37
  - README
37
38
  test_files:
38
39
  - test/example_test.rb
39
40
  - test/handoff_test.rb
41
+ - test/test_helper_test.rb
40
42
  - test/handoff/argument_normalizer_test.rb
41
43
  - test/handoff/assertion_test.rb
42
44
  rdoc_options: []