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 +3 -2
- data/lib/handoff/argument_normalizer.rb +3 -6
- data/lib/handoff/assertion.rb +47 -17
- data/lib/handoff/usage_error.rb +4 -0
- data/lib/handoff.rb +27 -5
- data/test/example_test.rb +22 -11
- data/test/handoff/assertion_test.rb +73 -17
- data/test/handoff_test.rb +15 -0
- data/test/test_helper_test.rb +10 -0
- metadata +5 -3
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
|
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
|
-
|
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
|
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
|
-
|
12
|
+
each {|method_symbol| hash[method_symbol] = method_symbol}
|
16
13
|
hash
|
17
14
|
end
|
18
15
|
|
data/lib/handoff/assertion.rb
CHANGED
@@ -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
|
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 :
|
11
|
-
private :method_map, :
|
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
|
28
|
-
@
|
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
|
-
@
|
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.
|
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
|
-
|
55
|
+
self
|
50
56
|
end
|
51
|
-
alias for_method
|
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
|
-
|
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
|
-
|
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[
|
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
|
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
|
4
|
+
# be called from any Test::Unit::TestCase test method to create
|
6
5
|
# a new Handoff::Assertion.
|
7
6
|
#
|
8
|
-
# After defining Handoff,
|
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
|
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
|
-
#
|
4
|
-
#
|
5
|
-
#
|
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.
|
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
|
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).
|
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
|
-
|
13
|
-
|
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
|
20
|
-
|
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
|
-
|
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
|
7
|
-
date: 2007-
|
8
|
-
summary: Handoff is a fluent interface for making test assertions about
|
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: []
|