handoff 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -3,7 +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
+ You can install the latest gem using <code>gem install handoff</code> or download the .gem file from http://rubyforge.org/projects/handoff.
7
+
8
+ As of 0.2.0, Handoff no longer depends on Mocha and Stubba.
7
9
 
8
10
  Here's example_test.rb to give you an idea of what you can do:
9
11
 
@@ -1,4 +1,4 @@
1
- require 'handoff/assertion'
1
+ require File.expand_path(File.dirname(__FILE__) + '/handoff/assertion')
2
2
 
3
3
  # +require+ (or +require_gem+) 'handoff' and #assert_handoff can
4
4
  # be called from any Test::Unit::TestCase test method to create
@@ -7,14 +7,28 @@ require 'handoff/assertion'
7
7
  # After defining Handoff, this file includes it in Test::Unit::TestCase.
8
8
  module Handoff
9
9
 
10
+ TEARDOWN_ALIAS = :teardown_before_handoff_rewrite
11
+
10
12
  # Creates an Assertion, handing a it a mock named 'handoff delegate' on which
11
13
  # expectations will be set.
12
14
  def assert_handoff
13
- a = Handoff::Assertion.new(self.mock('handoff delegate'))
15
+ ensure_handoff_teardown_defined
16
+ a = Handoff::Assertion.new(self)
14
17
  handoff_assertions << a
15
18
  a
16
19
  end
17
20
 
21
+ def ensure_handoff_teardown_defined
22
+ return if respond_to?(TEARDOWN_ALIAS)
23
+ class << self
24
+ alias_method TEARDOWN_ALIAS, :teardown
25
+ def teardown
26
+ conduct_and_clear_handoffs
27
+ send TEARDOWN_ALIAS
28
+ end
29
+ end
30
+ end
31
+
18
32
  def handoff_assertions
19
33
  @handoff_assertions ||= []
20
34
  end
@@ -27,12 +41,6 @@ module Handoff
27
41
  end
28
42
  end
29
43
 
30
- def self.included(base)
31
- base.class_eval do
32
- add_teardown_method :conduct_and_clear_handoffs
33
- end
34
- end
35
-
36
44
  end
37
45
 
38
46
  class Test::Unit::TestCase
@@ -1,5 +1,6 @@
1
- require 'handoff/argument_normalizer'
2
- require 'handoff/usage_error'
1
+ require File.expand_path(File.dirname(__FILE__) + '/argument_normalizer')
2
+ require File.expand_path(File.dirname(__FILE__) + '/usage_error')
3
+ require File.expand_path(File.dirname(__FILE__) + '/verifying_delegate')
3
4
 
4
5
  module Handoff
5
6
  # This is where the fluent stuff is.
@@ -9,7 +10,7 @@ module Handoff
9
10
  # * +to+,
10
11
  # * +for+, +for_method+ or +for_methods+, and
11
12
  # * (optionally) +with+ or +with_arguments+.
12
- class Assertion
13
+ class Assertion < Struct.new(:test)
13
14
 
14
15
  # :stopdoc:
15
16
 
@@ -17,10 +18,6 @@ module Handoff
17
18
  attr_reader :delegator
18
19
  private :method_map, :delegator
19
20
 
20
- def initialize mock_delegate
21
- @mock_delegate = mock_delegate
22
- end
23
-
24
21
  # :startdoc:
25
22
 
26
23
  # Creates an instance of +class_under_test+ with no arguments and uses it
@@ -64,7 +61,6 @@ module Handoff
64
61
 
65
62
  # Specifies that the assertion should pass no arguments to the delegating method and
66
63
  # require that no arguments are sent to the target method.
67
- # Equivalent to calling +with_arguments+ with no arguments.
68
64
  def with_no_arguments
69
65
  with_arguments
70
66
  end
@@ -73,19 +69,19 @@ module Handoff
73
69
 
74
70
  def conduct
75
71
  validate_sufficiently_specified
76
- @delegator.expects(@delegate_accessor).at_least_once.returns(@mock_delegate)
77
- expected_returns = {}
78
- method_map.each_pair do |delegating_method, target|
79
- expected_returns[delegating_method] = "#{delegating_method.to_s}_return"
80
- expectation = @mock_delegate.expects(target).returns(expected_returns[delegating_method])
81
- expectation.with(*@args) unless @args.nil?
82
- end
83
- method_map.each_pair do |delegating_method, target|
84
- send_args = [delegating_method, @args].flatten
85
- actual = delegator.send(*send_args)
86
- unless actual.equal? expected_returns[delegating_method]
87
- raise "expected #{delegating_method.to_s} to return #{expected_returns[delegating_method]}, but was #{actual}"
72
+ method_map.each do |delegating_method, target_method|
73
+ mock_delegate = VerifyingDelegate.new(target_method, @args||[])
74
+ accessor = @delegate_accessor
75
+ (class << @delegator; self; end).class_eval do
76
+ define_method(accessor) { mock_delegate }
88
77
  end
78
+ send_args = [delegating_method]
79
+ if @args
80
+ send_args << @args
81
+ send_args.flatten!
82
+ end
83
+ actual_return = @delegator.send *send_args
84
+ test.assert_equal mock_delegate.happy_return, actual_return
89
85
  end
90
86
  end
91
87
 
@@ -95,6 +91,7 @@ module Handoff
95
91
  raise UsageError, "Delegate accessor must be specified with 'to'" unless @delegate_accessor
96
92
  raise UsageError, "Delegating methods must be specified with 'for_method' or 'for_methods'" unless @method_map
97
93
  end
94
+
98
95
  # :startdoc:
99
96
  end
100
97
  end
@@ -0,0 +1,21 @@
1
+ module Handoff
2
+ class VerifyingDelegate
3
+ def initialize target_method, expected_args
4
+ @target_method = target_method
5
+ (class << self; self; end).class_eval do
6
+ define_method target_method do |*actual_args|
7
+ raise( ArgumentError, "expected arguments: '#{expected_args}' but got '#{actual_args}'") unless expected_args == actual_args
8
+ happy_return
9
+ end
10
+ end
11
+ end
12
+
13
+ def happy_return
14
+ "return value from #{to_s}"
15
+ end
16
+
17
+ def method_missing method, *args
18
+ raise NoMethodError, "Expected handoff to method `#{@target_method}', but `#{method}' was called"
19
+ end
20
+ end
21
+ end
@@ -19,12 +19,8 @@ class Foo
19
19
  end
20
20
 
21
21
  require 'test/unit'
22
- require 'rubygems'
23
22
 
24
- require 'mocha' # You need to require mocha and stubba in your test.
25
- require 'stubba' # ditto
26
-
27
- require File.dirname(__FILE__) + '/../lib/handoff' # I require it this way because I don't want the installed gem.
23
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/handoff') # I require it this way because I don't want the installed gem.
28
24
  # require 'handoff' # This is how you'd require handoff (or do require_gem w/ a version).
29
25
 
30
26
  class ExampleTest < Test::Unit::TestCase
@@ -1,20 +1,20 @@
1
- require 'test/unit'
2
- require File.dirname(__FILE__) + '/../../lib/handoff/argument_normalizer'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../lib/handoff/argument_normalizer')
3
3
 
4
4
  module Handoff
5
5
  class ArgumentNormalizerTest < Test::Unit::TestCase
6
6
 
7
- def test_should_return_hash_at_front_of_one_element_array
7
+ test "should return hash at front of one element array" do
8
8
  hash = {}
9
9
  assert_same hash, [hash].extend(ArgumentNormalizer).to_method_map
10
10
  end
11
11
 
12
- def test_should_return_self_referencing_hash_for_array_of_symbols
12
+ test "should return self referencing hash for array of symbols" do
13
13
  args = [:bar, :foo].extend(ArgumentNormalizer)
14
14
  assert_equal({:foo => :foo, :bar => :bar}, args.to_method_map)
15
15
  end
16
16
 
17
- def test_should_return_self_referencing_hash_for_one_element_array_with_symbol
17
+ test "should return self referencing hash for one element array with symbol" do
18
18
  args = [:foo].extend(ArgumentNormalizer)
19
19
  assert_equal({:foo => :foo}, args.to_method_map)
20
20
  end
@@ -1,6 +1,5 @@
1
- require 'test/unit'
2
- require File.dirname(__FILE__) + '/../test_helper'
3
- require File.dirname(__FILE__) + '/../../lib/handoff/assertion'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../lib/handoff/assertion')
4
3
  require 'rubygems'
5
4
  require 'mocha'
6
5
  require 'stubba'
@@ -13,63 +12,73 @@ module Handoff
13
12
  assert_same assertion, assertion.from_any(mock('class under test', :new => nil))
14
13
  end
15
14
 
16
- test 'should create expectations on mock_delegate and object under test upon conduct' do
17
- cut = Class.new do
18
- extend Forwardable
19
- def_delegator :asdf, :meth
20
- attr_reader :asdf
21
- end
22
- mock_delegate = mock
23
- object_under_test = cut.new
24
- assertion = Assertion.new(mock_delegate).from(object_under_test).to(:asdf).for_method(:meth)
25
- assertion.conduct
26
- assert_equal :asdf, object_under_test.mocha.expectations.first.method_name
27
- assert_equal :meth, mock_delegate.expectations.last.method_name
28
- end
29
-
30
- test 'should create mock_delegate expectation with arguments when supplied' do
15
+ test 'conduct should pass with one Test::Unit assertion if delegator does its job' do
31
16
  delegator = Class.new do
32
- extend Forwardable
33
- def_delegator :delegate, :meth
34
- attr_reader :delegate
17
+ def foo; delegate.foo; end
18
+ def delegate; nil; end
35
19
  end.new
36
- assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
37
- assertion.with('arg1', :arg2, 3)
20
+ assertion = Assertion.new(mock(:assert_equal => nil))
21
+ assertion.from(delegator).to(:delegate).for(:foo)
38
22
  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
23
  end
61
24
 
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
25
+ # test 'should create expectations on mock_delegate and object under test upon conduct' do
26
+ # cut = Class.new do
27
+ # extend Forwardable
28
+ # def_delegator :asdf, :meth
29
+ # attr_reader :asdf
30
+ # end
31
+ # mock_delegate = mock
32
+ # object_under_test = cut.new
33
+ # assertion = Assertion.new(mock_delegate).from(object_under_test).to(:asdf).for_method(:meth)
34
+ # assertion.conduct
35
+ # assert_equal :asdf, object_under_test.mocha.expectations.first.method_name
36
+ # assert_equal :meth, mock_delegate.expectations.last.method_name
37
+ # end
38
+ #
39
+ # test 'should create mock_delegate expectation with arguments when supplied' do
40
+ # delegator = Class.new do
41
+ # extend Forwardable
42
+ # def_delegator :delegate, :meth
43
+ # attr_reader :delegate
44
+ # end.new
45
+ # assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
46
+ # assertion.with('arg1', :arg2, 3)
47
+ # assertion.conduct
48
+ # assert_true mock_delegate.expectations.first.match?(:meth, 'arg1', :arg2, 3)
49
+ # assert_false mock_delegate.expectations.first.match?(:meth)
50
+ # assert_false mock_delegate.expectations.first.match?(:meth, 'arg1')
51
+ # end
52
+ #
53
+ # {
54
+ # :with => lambda {|assertion| assertion.with},
55
+ # :with_no_arguments => lambda {|assertion| assertion.with_no_arguments}
56
+ # }.each_pair do |method, proc|
57
+ # test "should force no args when #{method} is used to specify none" do
58
+ # delegator = Class.new do
59
+ # extend Forwardable
60
+ # def_delegator :delegate, :meth
61
+ # attr_reader :delegate
62
+ # end.new
63
+ # assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
64
+ # proc.call(assertion)
65
+ # assertion.conduct
66
+ # assert_true mock_delegate.expectations.first.match?(:meth)
67
+ # assert_false mock_delegate.expectations.first.match?(:meth, 'arg1')
68
+ # end
69
+ # end
70
+ #
71
+ # test 'should allow for args or no args if nothing specified' do
72
+ # delegator = Class.new do
73
+ # extend Forwardable
74
+ # def_delegator :delegate, :meth
75
+ # attr_reader :delegate
76
+ # end.new
77
+ # assertion = Assertion.new(mock_delegate = mock).from(delegator).to(:delegate).for(:meth)
78
+ # assertion.conduct
79
+ # assert_true mock_delegate.expectations.first.match?(:meth)
80
+ # assert_true mock_delegate.expectations.first.match?(:meth, 'arg1')
81
+ # end
73
82
 
74
83
  test 'validates presence of delegator, delegate accessor, and delegating methods' do
75
84
  assertion = Assertion.new(mock).from(nil).to(:asdf).for_method(:meth)
@@ -0,0 +1,36 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../lib/handoff/verifying_delegate')
3
+
4
+ module Handoff
5
+ class VerifyingDelegateTest < Test::Unit::TestCase
6
+ test "happy_return value" do
7
+ d = VerifyingDelegate.new(:foo, [])
8
+ assert_equal "return value from #{d.to_s}", d.happy_return
9
+ end
10
+
11
+ test 'responds to target method with happy_return value' do
12
+ d = VerifyingDelegate.new(:foo, [])
13
+ assert_equal d.happy_return, d.foo
14
+ end
15
+
16
+ test 'happy if args match' do
17
+ d = VerifyingDelegate.new(:foo, [1])
18
+ assert_equal d.happy_return, d.foo(1)
19
+ end
20
+
21
+ test 'raises if args dont match' do
22
+ d = VerifyingDelegate.new(:foo, [1])
23
+ assert_raises(ArgumentError) { d.foo }
24
+ end
25
+
26
+ test 'raises NoMethodError with helpful message if wrong method is invoked' do
27
+ d = VerifyingDelegate.new(:foo, [])
28
+ begin
29
+ d.foob
30
+ fail
31
+ rescue NoMethodError => e
32
+ assert_equal "Expected handoff to method `foo', but `foob' was called", e.message
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,26 +1,9 @@
1
- require File.dirname(__FILE__) + '/test_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
2
  require 'rubygems'
3
3
  require 'mocha'
4
- require 'stubba'
5
- require File.dirname(__FILE__) + '/../lib/handoff'
4
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/handoff')
6
5
 
7
6
  class HandoffTest < Test::Unit::TestCase
8
- test 'should create a mock named "handoff delegate" and hand it to a new Assertion' do
9
- given_name = nil
10
- def self.mock name
11
- assert_equal 'handoff delegate', name
12
- :mock_return
13
- end
14
- Assertion.expects(:new).with(:mock_return).returns(:new_assertion)
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
7
  test 'should conduct each assertion in conduct_and_clear_handoffs' do
25
8
  handoff_assertions << assertion1 = mock(:conduct => nil)
26
9
  handoff_assertions << assertion2 = mock(:conduct => nil)
@@ -29,4 +12,27 @@ class HandoffTest < Test::Unit::TestCase
29
12
  assertion1.verify
30
13
  assertion2.verify
31
14
  end
15
+
16
+ def blank_test_case
17
+ Class.new(Test::Unit::TestCase) do
18
+ define_method(:initialize) { super(:test_foo) }
19
+ define_method(:test_foo) {}
20
+ end
21
+ end
22
+
23
+ test 'first call to assert_handoff in a test aliases and defines new teardown' do
24
+ test = blank_test_case.new
25
+ assert_equal [], test.singleton_methods
26
+ test.assert_handoff
27
+ assert_equal %w(teardown teardown_before_handoff_rewrite ), test.singleton_methods.sort
28
+ end
29
+
30
+ test 'handoff-defined teardown does conduct_and_clear_handoffs then calls original teardown' do
31
+ test = blank_test_case.new
32
+ test.assert_handoff
33
+ test.expects(:conduct_and_clear_handoffs)
34
+ test.expects(:teardown_before_handoff_rewrite)
35
+ test.teardown
36
+ end
37
+
32
38
  end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/test_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
2
 
3
3
  class TestHelperTest < Test::Unit::TestCase
4
4
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: handoff
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2007-02-17 00:00:00 -05:00
6
+ version: 0.2.0
7
+ date: 2007-05-18 00:00:00 -07:00
8
8
  summary: Handoff is a fluent interface for making test assertions about delegation.
9
9
  require_paths:
10
10
  - lib
@@ -12,7 +12,7 @@ email: ""
12
12
  homepage: http://handoff.rubyforge.org/
13
13
  rubyforge_project:
14
14
  description:
15
- autorequire: hand_off
15
+ autorequire: handoff
16
16
  default_executable:
17
17
  bindir: bin
18
18
  has_rdoc: true
@@ -34,6 +34,7 @@ files:
34
34
  - lib/handoff/argument_normalizer.rb
35
35
  - lib/handoff/assertion.rb
36
36
  - lib/handoff/usage_error.rb
37
+ - lib/handoff/verifying_delegate.rb
37
38
  - README
38
39
  test_files:
39
40
  - test/example_test.rb
@@ -41,6 +42,7 @@ test_files:
41
42
  - test/test_helper_test.rb
42
43
  - test/handoff/argument_normalizer_test.rb
43
44
  - test/handoff/assertion_test.rb
45
+ - test/handoff/verifying_delegate_test.rb
44
46
  rdoc_options: []
45
47
 
46
48
  extra_rdoc_files:
@@ -51,13 +53,5 @@ extensions: []
51
53
 
52
54
  requirements: []
53
55
 
54
- dependencies:
55
- - !ruby/object:Gem::Dependency
56
- name: mocha
57
- version_requirement:
58
- version_requirements: !ruby/object:Gem::Version::Requirement
59
- requirements:
60
- - - ">"
61
- - !ruby/object:Gem::Version
62
- version: 0.0.0
63
- version:
56
+ dependencies: []
57
+