handoff 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|