bourne 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Joe Ferris and thoughtbot, inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,59 @@
1
+ = Bourne
2
+
3
+ Bourne extends mocha to allow detailed tracking and querying of stub and mock
4
+ invocations. It allows test spies using the have_received rspec matcher and
5
+ assert_received for Test::Unit. Bourne was extracted from jferris-mocha, a fork
6
+ of mocha that adds test spies.
7
+
8
+ == Test Spies
9
+
10
+ Test spies are a form of test double that preserves the normal four-phase unit
11
+ test order and allows separation of stubbing and verification.
12
+
13
+ Using a test spy is like using a mocked expectation except that there are two steps:
14
+
15
+ 1. Stub out a method for which you want to verify invocations
16
+ 2. Use an assertion or matcher to verify that the stub was invoked correctly
17
+
18
+ == Examples
19
+
20
+ # rspec
21
+
22
+ mock.should have_received(:to_s)
23
+ Radio.should have_received(:new).with(1041)
24
+ radio.should have_received(:volume).with(11).twice
25
+ radio.should have_received(:off).never
26
+
27
+ # Test::Unit
28
+
29
+ assert_received(mock, :to_s)
30
+ assert_received(Radio, :new) {|expect| expect.with(1041) }
31
+ assert_received(radio, :volume) {|expect| expect.with(11).twice }
32
+ assert_received(radio, :off) {|expect| expect.never }
33
+
34
+ See Mocha::API for more information.
35
+
36
+ == Download
37
+
38
+ Rubygems:
39
+ gem install bourne
40
+
41
+ Github: http://github.com/thoughtbot/bourne/tree/master
42
+
43
+ == More Information
44
+
45
+ * RDoc[http://rdoc.info/projects/thoughtbot/bourne]
46
+ * Mocha mailing list[http://groups.google.com/group/mocha-developer?hl=en]
47
+ * Issues[http://github.com/thoughtbot/bourne/issues]
48
+ * GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS[http://giantrobots.thoughtbot.com]
49
+
50
+ == Author
51
+
52
+ Bourne was written by Joe Ferris. Mocha was written by James Mead. Several of
53
+ the test examples and helpers used in the Bourne test suite were copied
54
+ directly from Mocha.
55
+
56
+ Thanks to thoughtbot for inspiration, ideas, and funding. Thanks to James for
57
+ writing mocha.
58
+
59
+ Copyright 2010 Joe Ferris and thoughtbot[http://www.thoughtbot.com], inc.
@@ -0,0 +1,61 @@
1
+ require 'rake/rdoctask'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+
5
+ desc "Run all tests"
6
+ task 'default' => ['test:units', 'test:acceptance', 'test:performance']
7
+
8
+ namespace 'test' do
9
+ unit_tests = FileList['test/unit/**/*_test.rb']
10
+ acceptance_tests = FileList['test/acceptance/*_test.rb']
11
+
12
+ desc "Run unit tests"
13
+ Rake::TestTask.new('units') do |t|
14
+ t.libs << 'test'
15
+ t.test_files = unit_tests
16
+ end
17
+
18
+ desc "Run acceptance tests"
19
+ Rake::TestTask.new('acceptance') do |t|
20
+ t.libs << 'test'
21
+ t.test_files = acceptance_tests
22
+ end
23
+
24
+ desc "Run performance tests"
25
+ task 'performance' do
26
+ require File.join(File.dirname(__FILE__), 'test', 'acceptance', 'stubba_example_test')
27
+ require File.join(File.dirname(__FILE__), 'test', 'acceptance', 'mocha_example_test')
28
+ iterations = 1000
29
+ puts "\nBenchmarking with #{iterations} iterations..."
30
+ [MochaExampleTest, StubbaExampleTest].each do |test_case|
31
+ puts "#{test_case}: #{benchmark_test_case(test_case, iterations)} seconds."
32
+ end
33
+ end
34
+ end
35
+
36
+ def benchmark_test_case(klass, iterations)
37
+ require 'benchmark'
38
+ require 'test/unit/ui/console/testrunner'
39
+ begin
40
+ require 'test/unit/ui/console/outputlevel'
41
+ silent_option = { :output_level => Test::Unit::UI::Console::OutputLevel::SILENT }
42
+ rescue LoadError
43
+ silent_option = Test::Unit::UI::SILENT
44
+ end
45
+ time = Benchmark.realtime { iterations.times { Test::Unit::UI::Console::TestRunner.run(klass, silent_option) } }
46
+ end
47
+
48
+ eval("$specification = #{IO.read('bourne.gemspec')}")
49
+ Rake::GemPackageTask.new($specification) do |package|
50
+ package.need_zip = true
51
+ package.need_tar = true
52
+ end
53
+
54
+ desc 'Generate documentation.'
55
+ Rake::RDocTask.new(:rdoc) do |rdoc|
56
+ rdoc.rdoc_dir = 'rdoc'
57
+ rdoc.title = 'Bourne'
58
+ rdoc.options << '--line-numbers' << "--main" << "README"
59
+ rdoc.rdoc_files.include('README')
60
+ rdoc.rdoc_files.include('lib/**/*.rb')
61
+ end
@@ -0,0 +1,2 @@
1
+ require 'mocha'
2
+ require 'bourne/api'
@@ -0,0 +1,82 @@
1
+ require 'mocha/api'
2
+ require 'bourne/mockery'
3
+
4
+ module Mocha # :nodoc:
5
+ module API
6
+ # Asserts that the given mock received the given method.
7
+ #
8
+ # Examples:
9
+ #
10
+ # assert_received(mock, :to_s)
11
+ # assert_received(Radio, :new) {|expect| expect.with(1041) }
12
+ # assert_received(radio, :volume) {|expect| expect.with(11).twice }
13
+ def assert_received(mock, expected_method_name)
14
+ matcher = have_received(expected_method_name)
15
+ yield(matcher) if block_given?
16
+ assert matcher.matches?(mock), matcher.failure_message
17
+ end
18
+
19
+ class HaveReceived #:nodoc:
20
+ def initialize(expected_method_name)
21
+ @expected_method_name = expected_method_name
22
+ @expectations = []
23
+ end
24
+
25
+ def method_missing(method, *args, &block)
26
+ @expectations << [method, args, block]
27
+ self
28
+ end
29
+
30
+ def matches?(mock)
31
+ if mock.respond_to?(:mocha)
32
+ @mock = mock.mocha
33
+ else
34
+ @mock = mock
35
+ end
36
+
37
+ @expectation = Expectation.new(@mock, @expected_method_name)
38
+ @expectations.each do |method, args, block|
39
+ @expectation.send(method, *args, &block)
40
+ end
41
+ @expectation.invocation_count = invocation_count
42
+ @expectation.verified?
43
+ end
44
+
45
+ def failure_message
46
+ @expectation.mocha_inspect
47
+ end
48
+
49
+ private
50
+
51
+ def invocation_count
52
+ matching_invocations.size
53
+ end
54
+
55
+ def matching_invocations
56
+ invocations.select do |invocation|
57
+ @expectation.match?(invocation.method_name, *invocation.arguments)
58
+ end
59
+ end
60
+
61
+ def invocations
62
+ Mockery.instance.invocations.select do |invocation|
63
+ invocation.mock.equal?(@mock)
64
+ end
65
+ end
66
+ end
67
+
68
+ # :call-seq:
69
+ # should have_received(method).with(arguments).times(times)
70
+ #
71
+ # Ensures that the given mock received the given method.
72
+ #
73
+ # Examples:
74
+ #
75
+ # mock.should have_received(:to_s)
76
+ # Radio.should have_received(:new).with(1041)
77
+ # radio.should have_received(:volume).with(11).twice
78
+ def have_received(expected_method_name)
79
+ HaveReceived.new(expected_method_name)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,23 @@
1
+ require 'mocha/expectation'
2
+
3
+ module Mocha # :nodoc:
4
+ # Extends Mocha::Expectation to record the full arguments and count whenver a
5
+ # stubbed or mocked method is invoked.
6
+ class Expectation # :nodoc:
7
+ attr_accessor :invocation_count
8
+
9
+ def invoke_with_args(args, &block)
10
+ Mockery.instance.invocation(@mock, method_name, args)
11
+ invoke_without_args(&block)
12
+ end
13
+
14
+ alias_method :invoke_without_args, :invoke
15
+ alias_method :invoke, :invoke_with_args
16
+
17
+ private
18
+
19
+ def method_name
20
+ @method_matcher.expected_method_name
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ module Mocha # :nodoc:
2
+ # Used internally by Bourne extensions to Mocha. Represents a single
3
+ # invocation of a stubbed or mocked method. The mock, method name, and
4
+ # arguments are recorded and can be used to determine how a method was
5
+ # invoked.
6
+ class Invocation
7
+ attr_reader :mock, :method_name, :arguments
8
+ def initialize(mock, method_name, arguments)
9
+ @mock = mock
10
+ @method_name = method_name
11
+ @arguments = arguments
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ require 'mocha/mock'
2
+ require 'bourne/expectation'
3
+
4
+ module Mocha # :nodoc:
5
+ # Overwrites #method_missing on Mocha::Mock so pass arguments to
6
+ # Mocha::Expectation#invoke so that an Invocation can be created.
7
+ class Mock # :nodoc:
8
+ def method_missing(symbol, *arguments, &block)
9
+ if @responder and not @responder.respond_to?(symbol)
10
+ raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
11
+ end
12
+ if matching_expectation_allowing_invocation = @expectations.match_allowing_invocation(symbol, *arguments)
13
+ matching_expectation_allowing_invocation.invoke(arguments, &block)
14
+ else
15
+ if (matching_expectation = @expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
16
+ message = UnexpectedInvocation.new(self, symbol, *arguments).to_s
17
+ message << Mockery.instance.mocha_inspect
18
+ raise ExpectationError.new(message, caller)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require 'mocha/mockery'
2
+ require 'bourne/mock'
3
+ require 'bourne/invocation'
4
+
5
+ module Mocha
6
+ # Used internally by Bourne extensions to Mocha when recording invocations of
7
+ # stubbed and mocked methods. You shouldn't need to interact with this module
8
+ # through normal testing, but you can access the full list of invocations
9
+ # directly if necessary.
10
+ class Mockery
11
+ def invocation(mock, method_name, args)
12
+ invocations << Invocation.new(mock, method_name, args)
13
+ end
14
+
15
+ def invocations
16
+ @invocations ||= []
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+ require 'test_runner'
3
+ require 'mocha/configuration'
4
+
5
+ module AcceptanceTest
6
+
7
+ class FakeLogger
8
+
9
+ attr_reader :warnings
10
+
11
+ def initialize
12
+ @warnings = []
13
+ end
14
+
15
+ def warn(message)
16
+ @warnings << message
17
+ end
18
+
19
+ end
20
+
21
+ attr_reader :logger
22
+
23
+ include TestRunner
24
+
25
+ def setup_acceptance_test
26
+ Mocha::Configuration.reset_configuration
27
+ @logger = FakeLogger.new
28
+ mockery = Mocha::Mockery.instance
29
+ @original_logger = mockery.logger
30
+ mockery.logger = @logger
31
+ end
32
+
33
+ def teardown_acceptance_test
34
+ Mocha::Configuration.reset_configuration
35
+ Mocha::Mockery.instance.logger = @original_logger
36
+ end
37
+
38
+ end
@@ -0,0 +1,98 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+ require 'mocha'
3
+
4
+ class MochaExampleTest < Test::Unit::TestCase
5
+
6
+ class Rover
7
+
8
+ def initialize(left_track, right_track, steps_per_metre, steps_per_degree)
9
+ @left_track, @right_track, @steps_per_metre, @steps_per_degree = left_track, right_track, steps_per_metre, steps_per_degree
10
+ end
11
+
12
+ def forward(metres)
13
+ @left_track.step(metres * @steps_per_metre)
14
+ @right_track.step(metres * @steps_per_metre)
15
+ wait
16
+ end
17
+
18
+ def backward(metres)
19
+ forward(-metres)
20
+ end
21
+
22
+ def left(degrees)
23
+ @left_track.step(-degrees * @steps_per_degree)
24
+ @right_track.step(+degrees * @steps_per_degree)
25
+ wait
26
+ end
27
+
28
+ def right(degrees)
29
+ left(-degrees)
30
+ end
31
+
32
+ def wait
33
+ while (@left_track.moving? or @right_track.moving?); end
34
+ end
35
+
36
+ end
37
+
38
+ def test_should_step_both_tracks_forward_ten_steps
39
+ left_track = mock('left_track')
40
+ right_track = mock('right_track')
41
+ steps_per_metre = 5
42
+ rover = Rover.new(left_track, right_track, steps_per_metre, nil)
43
+
44
+ left_track.expects(:step).with(10)
45
+ right_track.expects(:step).with(10)
46
+
47
+ left_track.stubs(:moving?).returns(false)
48
+ right_track.stubs(:moving?).returns(false)
49
+
50
+ rover.forward(2)
51
+ end
52
+
53
+ def test_should_step_both_tracks_backward_ten_steps
54
+ left_track = mock('left_track')
55
+ right_track = mock('right_track')
56
+ steps_per_metre = 5
57
+ rover = Rover.new(left_track, right_track, steps_per_metre, nil)
58
+
59
+ left_track.expects(:step).with(-10)
60
+ right_track.expects(:step).with(-10)
61
+
62
+ left_track.stubs(:moving?).returns(false)
63
+ right_track.stubs(:moving?).returns(false)
64
+
65
+ rover.backward(2)
66
+ end
67
+
68
+ def test_should_step_left_track_forwards_five_steps_and_right_track_backwards_five_steps
69
+ left_track = mock('left_track')
70
+ right_track = mock('right_track')
71
+ steps_per_degree = 5.0 / 90.0
72
+ rover = Rover.new(left_track, right_track, nil, steps_per_degree)
73
+
74
+ left_track.expects(:step).with(+5)
75
+ right_track.expects(:step).with(-5)
76
+
77
+ left_track.stubs(:moving?).returns(false)
78
+ right_track.stubs(:moving?).returns(false)
79
+
80
+ rover.right(90)
81
+ end
82
+
83
+ def test_should_step_left_track_backwards_five_steps_and_right_track_forwards_five_steps
84
+ left_track = mock('left_track')
85
+ right_track = mock('right_track')
86
+ steps_per_degree = 5.0 / 90.0
87
+ rover = Rover.new(left_track, right_track, nil, steps_per_degree)
88
+
89
+ left_track.expects(:step).with(-5)
90
+ right_track.expects(:step).with(+5)
91
+
92
+ left_track.stubs(:moving?).returns(false)
93
+ right_track.stubs(:moving?).returns(false)
94
+
95
+ rover.left(90)
96
+ end
97
+
98
+ end