bourne 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/LICENSE +19 -0
- data/README +59 -0
- data/Rakefile +61 -0
- data/lib/bourne.rb +2 -0
- data/lib/bourne/api.rb +82 -0
- data/lib/bourne/expectation.rb +23 -0
- data/lib/bourne/invocation.rb +14 -0
- data/lib/bourne/mock.rb +23 -0
- data/lib/bourne/mockery.rb +19 -0
- data/test/acceptance/acceptance_test_helper.rb +38 -0
- data/test/acceptance/mocha_example_test.rb +98 -0
- data/test/acceptance/spy_test.rb +134 -0
- data/test/acceptance/stubba_example_test.rb +102 -0
- data/test/execution_point.rb +36 -0
- data/test/matcher_helpers.rb +5 -0
- data/test/method_definer.rb +24 -0
- data/test/simple_counter.rb +13 -0
- data/test/test_helper.rb +19 -0
- data/test/test_runner.rb +47 -0
- data/test/unit/assert_received_test.rb +145 -0
- data/test/unit/expectation_test.rb +526 -0
- data/test/unit/have_received_test.rb +192 -0
- data/test/unit/invocation_test.rb +17 -0
- data/test/unit/mock_test.rb +329 -0
- data/test/unit/mockery_test.rb +163 -0
- metadata +128 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "acceptance_test_helper")
|
2
|
+
require 'bourne'
|
3
|
+
require 'matcher_helpers'
|
4
|
+
|
5
|
+
module SpyTestMethods
|
6
|
+
|
7
|
+
def setup
|
8
|
+
setup_acceptance_test
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
teardown_acceptance_test
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_should_accept_wildcard_stub_call_without_arguments
|
16
|
+
instance = new_instance
|
17
|
+
instance.stubs(:to_s)
|
18
|
+
instance.to_s
|
19
|
+
assert_received(instance, :to_s)
|
20
|
+
assert_matcher_accepts have_received(:to_s), instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_accept_wildcard_stub_call_with_arguments
|
24
|
+
instance = new_instance
|
25
|
+
instance.stubs(:to_s)
|
26
|
+
instance.to_s(:argument)
|
27
|
+
assert_received(instance, :to_s)
|
28
|
+
assert_matcher_accepts have_received(:to_s), instance
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_should_not_accept_wildcard_stub_without_call
|
32
|
+
instance = new_instance
|
33
|
+
instance.stubs(:to_s)
|
34
|
+
assert_fails { assert_received(instance, :to_s) }
|
35
|
+
assert_fails { assert_matcher_accepts have_received(:to_s), instance }
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_not_accept_call_without_arguments
|
39
|
+
instance = new_instance
|
40
|
+
instance.stubs(:to_s)
|
41
|
+
instance.to_s
|
42
|
+
assert_fails { assert_received(instance, :to_s) {|expect| expect.with(1) } }
|
43
|
+
assert_fails { assert_matcher_accepts have_received(:to_s).with(1), instance }
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_should_not_accept_call_with_different_arguments
|
47
|
+
instance = new_instance
|
48
|
+
instance.stubs(:to_s)
|
49
|
+
instance.to_s(2)
|
50
|
+
assert_fails { assert_received(instance, :to_s) {|expect| expect.with(1) } }
|
51
|
+
assert_fails { assert_matcher_accepts have_received(:to_s).with(1), instance }
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_should_accept_call_with_correct_arguments
|
55
|
+
instance = new_instance
|
56
|
+
instance.stubs(:to_s)
|
57
|
+
instance.to_s(1)
|
58
|
+
assert_received(instance, :to_s) {|expect| expect.with(1) }
|
59
|
+
assert_matcher_accepts have_received(:to_s).with(1), instance
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_should_accept_call_with_wildcard_arguments
|
63
|
+
instance = new_instance
|
64
|
+
instance.stubs(:to_s)
|
65
|
+
instance.to_s('hello')
|
66
|
+
assert_received(instance, :to_s) {|expect| expect.with(is_a(String)) }
|
67
|
+
assert_matcher_accepts have_received(:to_s).with(is_a(String)), instance
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_should_reject_call_on_different_mock
|
71
|
+
instance = new_instance
|
72
|
+
other = new_instance
|
73
|
+
instance.stubs(:to_s)
|
74
|
+
other.stubs(:to_s)
|
75
|
+
other.to_s('hello')
|
76
|
+
assert_fails { assert_received(instance, :to_s) {|expect| expect.with(is_a(String)) } }
|
77
|
+
assert_fails { assert_matcher_accepts have_received(:to_s).with(is_a(String)), instance }
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_should_accept_correct_number_of_calls
|
81
|
+
instance = new_instance
|
82
|
+
instance.stubs(:to_s)
|
83
|
+
2.times { instance.to_s }
|
84
|
+
assert_received(instance, :to_s) {|expect| expect.twice }
|
85
|
+
assert_matcher_accepts have_received(:to_s).twice, instance
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_reject_not_enough_calls
|
89
|
+
instance = new_instance
|
90
|
+
instance.stubs(:to_s)
|
91
|
+
instance.to_s
|
92
|
+
message = /expected exactly twice/
|
93
|
+
assert_fails(message) { assert_received(instance, :to_s) {|expect| expect.twice } }
|
94
|
+
assert_fails(message) { assert_matcher_accepts have_received(:to_s).twice, instance }
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_should_reject_too_many_calls
|
98
|
+
instance = new_instance
|
99
|
+
instance.stubs(:to_s)
|
100
|
+
2.times { instance.to_s }
|
101
|
+
message = /expected exactly once/
|
102
|
+
assert_fails(message) { assert_received(instance, :to_s) {|expect| expect.once } }
|
103
|
+
assert_fails(message) { assert_matcher_accepts have_received(:to_s).once, instance }
|
104
|
+
end
|
105
|
+
|
106
|
+
def assert_fails(message=/not yet invoked/)
|
107
|
+
begin
|
108
|
+
yield
|
109
|
+
rescue Test::Unit::AssertionFailedError => exception
|
110
|
+
assert_match message, exception.message, "Test failed, but with the wrong message"
|
111
|
+
return
|
112
|
+
end
|
113
|
+
flunk("Expected to fail")
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
class PartialSpyTest < Test::Unit::TestCase
|
119
|
+
include AcceptanceTest
|
120
|
+
include SpyTestMethods
|
121
|
+
|
122
|
+
def new_instance
|
123
|
+
Object.new
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class PureSpyTest < Test::Unit::TestCase
|
128
|
+
include AcceptanceTest
|
129
|
+
include SpyTestMethods
|
130
|
+
|
131
|
+
def new_instance
|
132
|
+
stub
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
require 'mocha'
|
3
|
+
|
4
|
+
class Widget
|
5
|
+
|
6
|
+
def model
|
7
|
+
'original_model'
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def find(options)
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(attributes)
|
17
|
+
Widget.new
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
module Thingy
|
25
|
+
|
26
|
+
def self.wotsit
|
27
|
+
:hoojamaflip
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class StubbaExampleTest < Test::Unit::TestCase
|
33
|
+
|
34
|
+
def test_should_stub_instance_method
|
35
|
+
widget = Widget.new
|
36
|
+
widget.expects(:model).returns('different_model')
|
37
|
+
assert_equal 'different_model', widget.model
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_should_stub_module_method
|
41
|
+
should_stub_module_method
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_stub_module_method_again
|
45
|
+
should_stub_module_method
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_should_stub_class_method
|
49
|
+
should_stub_class_method
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_stub_class_method_again
|
53
|
+
should_stub_class_method
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_should_stub_instance_method_on_any_instance_of_a_class
|
57
|
+
should_stub_instance_method_on_any_instance_of_a_class
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_should_stub_instance_method_on_any_instance_of_a_class_again
|
61
|
+
should_stub_instance_method_on_any_instance_of_a_class
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_should_stub_two_different_class_methods
|
65
|
+
should_stub_two_different_class_methods
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_should_stub_two_different_class_methods_again
|
69
|
+
should_stub_two_different_class_methods
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def should_stub_module_method
|
75
|
+
Thingy.expects(:wotsit).returns(:dooda)
|
76
|
+
assert_equal :dooda, Thingy.wotsit
|
77
|
+
end
|
78
|
+
|
79
|
+
def should_stub_class_method
|
80
|
+
widgets = [Widget.new]
|
81
|
+
Widget.expects(:find).with(:all).returns(widgets)
|
82
|
+
assert_equal widgets, Widget.find(:all)
|
83
|
+
end
|
84
|
+
|
85
|
+
def should_stub_two_different_class_methods
|
86
|
+
found_widgets = [Widget.new]
|
87
|
+
created_widget = Widget.new
|
88
|
+
Widget.expects(:find).with(:all).returns(found_widgets)
|
89
|
+
Widget.expects(:create).with(:model => 'wombat').returns(created_widget)
|
90
|
+
assert_equal found_widgets, Widget.find(:all)
|
91
|
+
assert_equal created_widget, Widget.create(:model => 'wombat')
|
92
|
+
end
|
93
|
+
|
94
|
+
def should_stub_instance_method_on_any_instance_of_a_class
|
95
|
+
Widget.any_instance.expects(:model).at_least_once.returns('another_model')
|
96
|
+
widget_1 = Widget.new
|
97
|
+
widget_2 = Widget.new
|
98
|
+
assert_equal 'another_model', widget_1.model
|
99
|
+
assert_equal 'another_model', widget_2.model
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class ExecutionPoint
|
2
|
+
|
3
|
+
attr_reader :backtrace
|
4
|
+
|
5
|
+
def self.current
|
6
|
+
new(caller)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(backtrace)
|
10
|
+
@backtrace = backtrace
|
11
|
+
end
|
12
|
+
|
13
|
+
def file_name
|
14
|
+
return "unknown" unless @backtrace && @backtrace.first
|
15
|
+
/\A(.*?):\d+/.match(@backtrace.first)[1]
|
16
|
+
end
|
17
|
+
|
18
|
+
def line_number
|
19
|
+
return "unknown" unless @backtrace && @backtrace.first
|
20
|
+
Integer(/\A.*?:(\d+)/.match(@backtrace.first)[1])
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
return false unless other.is_a?(ExecutionPoint)
|
25
|
+
(file_name == other.file_name) and (line_number == other.line_number)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"file: #{file_name}; line: #{line_number}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'mocha/metaclass'
|
2
|
+
|
3
|
+
module Mocha
|
4
|
+
|
5
|
+
module ObjectMethods
|
6
|
+
def define_instance_method(method_symbol, &block)
|
7
|
+
__metaclass__.send(:define_method, method_symbol, block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def replace_instance_method(method_symbol, &block)
|
11
|
+
raise "Cannot replace #{method_symbol} as #{self} does not respond to it." unless self.respond_to?(method_symbol)
|
12
|
+
define_instance_method(method_symbol, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def define_instance_accessor(*symbols)
|
16
|
+
symbols.each { |symbol| __metaclass__.send(:attr_accessor, symbol) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class Object
|
23
|
+
include Mocha::ObjectMethods
|
24
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
unless defined?(STANDARD_OBJECT_PUBLIC_INSTANCE_METHODS)
|
2
|
+
STANDARD_OBJECT_PUBLIC_INSTANCE_METHODS = Object.public_instance_methods
|
3
|
+
end
|
4
|
+
|
5
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
6
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__)))
|
7
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'unit'))
|
8
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'unit', 'parameter_matchers'))
|
9
|
+
$:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'acceptance'))
|
10
|
+
|
11
|
+
if ENV['MOCHA_OPTIONS'] == 'use_test_unit_gem'
|
12
|
+
require 'rubygems'
|
13
|
+
gem 'test-unit'
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
require 'test/unit'
|
18
|
+
gem 'mocha', '0.9.8'
|
19
|
+
|
data/test/test_runner.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test/unit/testcase'
|
2
|
+
|
3
|
+
if defined?(MiniTest)
|
4
|
+
require 'mocha/integration/mini_test'
|
5
|
+
require File.expand_path('../mini_test_result', __FILE__)
|
6
|
+
else
|
7
|
+
require 'test/unit/testresult'
|
8
|
+
end
|
9
|
+
|
10
|
+
module TestRunner
|
11
|
+
def run_as_test(test_result = nil, &block)
|
12
|
+
test_class = Class.new(Test::Unit::TestCase) do
|
13
|
+
define_method(:test_me, &block)
|
14
|
+
end
|
15
|
+
test = test_class.new(:test_me)
|
16
|
+
|
17
|
+
if defined?(Test::Unit::TestResult)
|
18
|
+
test_result ||= Test::Unit::TestResult.new
|
19
|
+
test.run(test_result) {}
|
20
|
+
class << test_result
|
21
|
+
attr_reader :failures, :errors
|
22
|
+
def failure_messages
|
23
|
+
failures.map { |failure| failure.message }
|
24
|
+
end
|
25
|
+
def error_messages
|
26
|
+
errors.map { |error| error.message }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
else
|
30
|
+
runner = MiniTest::Unit.new
|
31
|
+
test.run(runner)
|
32
|
+
test_result = MiniTestResult.new(runner, test)
|
33
|
+
end
|
34
|
+
|
35
|
+
test_result
|
36
|
+
end
|
37
|
+
|
38
|
+
def assert_passed(test_result)
|
39
|
+
flunk "Test failed unexpectedly with message: #{test_result.failures}" if test_result.failure_count > 0
|
40
|
+
flunk "Test failed unexpectedly with message: #{test_result.errors}" if test_result.error_count > 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def assert_failed(test_result)
|
44
|
+
flunk "Test passed unexpectedly" if test_result.passed?
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "test_helper")
|
2
|
+
require 'test_runner'
|
3
|
+
require 'bourne/api'
|
4
|
+
require 'bourne/mockery'
|
5
|
+
require 'mocha/object'
|
6
|
+
|
7
|
+
class AssertReceivedTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
include Mocha
|
10
|
+
include TestRunner
|
11
|
+
include Mocha::API
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
Mockery.reset_instance
|
15
|
+
end
|
16
|
+
|
17
|
+
class FakeMock
|
18
|
+
def initialize(name)
|
19
|
+
@name = name
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
@name
|
24
|
+
end
|
25
|
+
|
26
|
+
def mocha
|
27
|
+
self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_passes_if_invocation_exists
|
32
|
+
method = :a_method
|
33
|
+
mock = FakeMock.new('a mock')
|
34
|
+
Mockery.instance.invocation(mock, method, [])
|
35
|
+
assert_passes do
|
36
|
+
assert_received(mock, method)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_fails_if_invocation_doesnt_exist
|
41
|
+
method = :a_method
|
42
|
+
mock = FakeMock.new('a mock')
|
43
|
+
assert_fails do
|
44
|
+
assert_received(mock, method)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_fails_if_invocation_exists_with_different_arguments
|
49
|
+
method = :a_method
|
50
|
+
mock = FakeMock.new('a mock')
|
51
|
+
Mockery.instance.invocation(mock, method, [2, 1])
|
52
|
+
assert_fails do
|
53
|
+
assert_received(mock, method) {|expect| expect.with(1, 2) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_passes_if_invocation_exists_with_wildcard_arguments
|
58
|
+
method = :a_method
|
59
|
+
mock = FakeMock.new('a mock')
|
60
|
+
Mockery.instance.invocation(mock, method, ['hello'])
|
61
|
+
assert_passes do
|
62
|
+
assert_received(mock, method) {|expect| expect.with(is_a(String)) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_passes_if_invocation_exists_with_exact_arguments
|
67
|
+
method = :a_method
|
68
|
+
mock = FakeMock.new('a mock')
|
69
|
+
Mockery.instance.invocation(mock, method, ['hello'])
|
70
|
+
assert_passes do
|
71
|
+
assert_received(mock, method) {|expect| expect.with('hello') }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_fails_if_invocation_exists_only_on_other_mock
|
76
|
+
method = :a_method
|
77
|
+
mock = FakeMock.new('a mock')
|
78
|
+
other = 'another mock'
|
79
|
+
Mockery.instance.invocation(other, method, ['hello'])
|
80
|
+
assert_fails do
|
81
|
+
assert_received(mock, method)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_passes_if_invocation_exists_for_impersonating_mock
|
86
|
+
method = :a_method
|
87
|
+
object = Object.new
|
88
|
+
mock = FakeMock.new('a mock')
|
89
|
+
|
90
|
+
class << object
|
91
|
+
attr_accessor :mocha
|
92
|
+
end
|
93
|
+
object.mocha = mock
|
94
|
+
|
95
|
+
Mockery.instance.invocation(mock, method, ['hello'])
|
96
|
+
assert_passes do
|
97
|
+
assert_received(object, method) {|expect| expect.with('hello') }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_passes_if_invocation_count_correct
|
102
|
+
method = :a_method
|
103
|
+
mock = FakeMock.new('a mock')
|
104
|
+
2.times { Mockery.instance.invocation(mock, method, []) }
|
105
|
+
assert_passes do
|
106
|
+
assert_received(mock, method) {|expect| expect.twice }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_fails_if_invocation_count_too_low
|
111
|
+
method = :a_method
|
112
|
+
mock = FakeMock.new('a mock')
|
113
|
+
Mockery.instance.invocation(mock, method, [])
|
114
|
+
assert_fails do
|
115
|
+
assert_received(mock, method) {|expect| expect.twice }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_fails_if_invocation_count_too_high
|
120
|
+
method = :a_method
|
121
|
+
mock = FakeMock.new('a mock')
|
122
|
+
2.times { Mockery.instance.invocation(mock, method, []) }
|
123
|
+
assert_fails do
|
124
|
+
assert_received(mock, method) {|expect| expect.once }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def assert_passes(&block)
|
129
|
+
assert ! fails?(&block)
|
130
|
+
end
|
131
|
+
|
132
|
+
def assert_fails(&block)
|
133
|
+
assert fails?(&block)
|
134
|
+
end
|
135
|
+
|
136
|
+
def fails?
|
137
|
+
begin
|
138
|
+
yield
|
139
|
+
false
|
140
|
+
rescue Test::Unit::AssertionFailedError
|
141
|
+
true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|