facon 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ === 0.1 / 2008-02-09
2
+
3
+ * 1 XXX
4
+ * XXX
5
+
data/Manifest.txt ADDED
@@ -0,0 +1,17 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/facon.rb
6
+ lib/facon/baconize.rb
7
+ lib/facon/core_ext/object.rb
8
+ lib/facon/error_generator.rb
9
+ lib/facon/expectation.rb
10
+ lib/facon/mock.rb
11
+ lib/facon/mockable.rb
12
+ lib/facon/proxy.rb
13
+ spec/baconize_spec.rb
14
+ spec/expectation_spec.rb
15
+ spec/mock_spec.rb
16
+ spec/spec_helper.rb
17
+ spec/stub_spec.rb
data/README.txt ADDED
@@ -0,0 +1,87 @@
1
+ = Facon
2
+
3
+ Facon is a mocking library in the spirit of the Bacon spec library. Small, compact, and works with Bacon.
4
+
5
+ == Synopsis
6
+
7
+ To use Facon with Bacon[http://rubyforge.org/projects/test-spec/], simply <code>require 'facon'</code> and you're done.
8
+
9
+ You can now write Bacon specs like this (in RSpec-like style):
10
+
11
+ require 'bacon'
12
+ require 'facon'
13
+
14
+ describe 'PersonController' do
15
+ before do
16
+ @konata = mock('konata', :id => 1, :name => 'Konata Izumi')
17
+ @kagami = mock('kagami', :id => 2, :name => 'Kagami Hiiragi')
18
+ end
19
+
20
+ it "should find all people on GET to 'index'" do
21
+ Person.should.receive(:find).with(:all).and_return([@konata, @kagami])
22
+
23
+ get('/people/index')
24
+ end
25
+
26
+ it "should find the person with id of 1 on Get to 'show/1'" do
27
+ Person.should.receive(:find).with(1).and_return(@konata)
28
+
29
+ get('/people/show/1')
30
+ end
31
+ end
32
+
33
+ For now, more examples can be found in the specs included with the Facon gem. I promise to get better examples into the documentation!
34
+
35
+ See Facon::Baconize for more documentation on using Facon with Bacon[http://rubyforge.org/projects/test-spec/].
36
+
37
+ == Requirements
38
+
39
+ * Ruby 1.8
40
+ * Bacon (optional, required for running specs)
41
+
42
+ == Installation
43
+
44
+ Simply install the gem:
45
+ gem install facon
46
+
47
+ == Links
48
+
49
+ * Facon website - http://facon.rubyforge.org/
50
+ * Facon Rubyforge project - http://rubyforge.org/projects/facon/
51
+ * Bacon - http://rubyforge.org/projects/test-spec/
52
+ * RSpec - http://rspec.info/
53
+
54
+ == Todos
55
+
56
+ * test/unit and RSpec integration.
57
+ * Remove the $facon_mocks global.
58
+
59
+ == Thanks to
60
+
61
+ * RSpec (http://rspec.info/) for creating spec/mocks, from which a lot of the code for Facon is stolen.
62
+ * Christian Neukirchen (http://rubyforge.org/projects/test-spec/) for creating Bacon.
63
+
64
+ == License:
65
+
66
+ (The MIT License)
67
+
68
+ Copyright (c) 2008 Cheah Chu Yeow
69
+
70
+ Permission is hereby granted, free of charge, to any person obtaining
71
+ a copy of this software and associated documentation files (the
72
+ 'Software'), to deal in the Software without restriction, including
73
+ without limitation the rights to use, copy, modify, merge, publish,
74
+ distribute, sublicense, and/or sell copies of the Software, and to
75
+ permit persons to whom the Software is furnished to do so, subject to
76
+ the following conditions:
77
+
78
+ The above copyright notice and this permission notice shall be
79
+ included in all copies or substantial portions of the Software.
80
+
81
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
82
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
83
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
84
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
85
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
86
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
87
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ $:.unshift(File.dirname(__FILE__) + '/lib')
4
+ require 'facon'
5
+
6
+ Hoe.new('facon', Facon::VERSION) do |p|
7
+ p.rubyforge_name = 'facon'
8
+ p.author = 'Cheah Chu Yeow'
9
+ p.email = 'chuyeow@gmail.com'
10
+ p.summary = 'Tiny mocking library.'
11
+ p.url = 'http://facon.rubyforge.org/'
12
+ p.description = 'A mocking library in the spirit of the Bacon spec library. Small, compact, and works with Bacon.'
13
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
14
+ p.remote_rdoc_dir = 'rdocs'
15
+ # p.clean_globs = ['test/actual'] # Remove this directory on "rake clean"
16
+ end
data/lib/facon.rb ADDED
@@ -0,0 +1,16 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'facon/mockable'
4
+ require 'facon/mock'
5
+ require 'facon/error_generator'
6
+ require 'facon/expectation'
7
+ require 'facon/proxy'
8
+ require 'facon/baconize'
9
+
10
+ require 'facon/core_ext/object'
11
+
12
+ # Facon is a mocking library in the spirit of the Bacon spec library. Small,
13
+ # compact, and works with Bacon.
14
+ module Facon
15
+ VERSION = '0.1'
16
+ end
@@ -0,0 +1,95 @@
1
+ module Facon
2
+ # == Bacon integration
3
+ #
4
+ # To use Facon with Bacon, simply <code>require 'facon'</code>. Facon injects
5
+ # itself into Bacon if it can find it, so all you have to do is to make sure
6
+ # you have Bacon and Facon available on the load path.
7
+ #
8
+ # == Example
9
+ #
10
+ # In <code>spec_helper.rb</code>:
11
+ # require 'rubygems'
12
+ # require 'bacon'
13
+ # require 'facon'
14
+ #
15
+ # Simply <code>require</code> your <code>spec_helper.rb</code> in your specs and you are now
16
+ # able to create mocks and expectations:
17
+ #
18
+ # require '/path/to/spec_helper'
19
+ #
20
+ # describe 'My examples' do
21
+ # it "should allow mocks and expectations" do
22
+ # @mock = mock('test mock')
23
+ # @mock.should.receive(:message).and_return(:foo)
24
+ #
25
+ # do_something_with(@mock)
26
+ # end
27
+ # end
28
+ module Baconize
29
+
30
+ # Mixin intended for Bacon::Context so that it runs spec_verify on all mocks
31
+ # after each example.
32
+ module ContextExtensions
33
+ def self.included(base)
34
+ base.class_eval do
35
+ alias_method :it_without_mock_verification, :it
36
+ alias_method :it, :it_with_mock_verification
37
+ end
38
+ end
39
+
40
+ def setup_facon_mocks
41
+ $facon_mocks ||= []
42
+ end
43
+
44
+ def verify_facon_mocks
45
+ $facon_mocks.each { |mock| mock.spec_verify }
46
+ end
47
+
48
+ def it_with_mock_verification(description, &block)
49
+ setup_facon_mocks
50
+ it_without_mock_verification(description, &block)
51
+ verify_facon_mocks
52
+ end
53
+ end
54
+
55
+ # Mixin intended for Bacon's Should class so that we can do
56
+ # mock.should.receive(:message) and mock.should.not.receive(:message).
57
+ module ShouldExtensions
58
+ def self.included(base)
59
+ # Remove Facon::Mockable methods we mixed in to Object, since we don't
60
+ # need those in the Should class.
61
+ base.class_eval do
62
+ instance_methods.each do |method|
63
+ undef_method(method) if Facon::Mockable.public_instance_methods.include?(method)
64
+ end
65
+ end
66
+ end
67
+
68
+ def receive(method, &block)
69
+ if @negated
70
+ @object.mock_proxy.add_negative_expectation(caller(1)[0], method, &block)
71
+ else
72
+ @object.mock_proxy.add_expectation(caller(1)[0], method, &block)
73
+ end
74
+ end
75
+
76
+ private
77
+ def mock_proxy
78
+ @mock_proxy ||= Proxy.new(@object, Mock === @object ? @object.name : @object.class.name)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ begin
86
+ Bacon::Context.class_eval { include Facon::Baconize::ContextExtensions }
87
+ Should.class_eval { include Facon::Baconize::ShouldExtensions }
88
+ rescue NameError
89
+ require 'rubygems'
90
+ require 'bacon'
91
+ Bacon::Context.class_eval { include Facon::Baconize::ContextExtensions }
92
+ Should.class_eval { include Facon::Baconize::ShouldExtensions }
93
+ rescue LoadError
94
+ puts 'Bacon is not available.'
95
+ end
@@ -0,0 +1,3 @@
1
+ class Object #:nodoc:
2
+ include Facon::Mockable
3
+ end
@@ -0,0 +1,42 @@
1
+ module Facon
2
+ # A helper class for generating errors for expectation errors on mocks.
3
+ class ErrorGenerator
4
+ def initialize(target, name)
5
+ @target, @name = target, name
6
+ end
7
+
8
+ def raise_expectation_error(expectation)
9
+ message = "#{target_name} expected :#{expectation.method} with #{format_args(*expectation.argument_expectation)} #{format_count(expectation.expected_received_count)}, but received it #{format_count(expectation.actual_received_count)}"
10
+ raise(Facon::MockExpectationError, message)
11
+ end
12
+
13
+ def raise_unexpected_message_error(method, *args)
14
+ raise(Facon::MockExpectationError, "#{target_name} received unexpected message :#{method} with #{format_args(*args)}")
15
+ end
16
+
17
+ def raise_unexpected_message_args_error(expectation, *args)
18
+ message = "#{target_name} expected :#{expectation.method} with #{format_args(*expectation.argument_expectation)}, but received it with #{format_args(*args)}"
19
+ raise(Facon::MockExpectationError, message)
20
+ end
21
+
22
+ def raise_block_failed_error(method, exception_message)
23
+ message = "#{target_name} received :#{method} but passed block failed with: #{exception_message}"
24
+ raise(Facon::MockExpectationError, message)
25
+ end
26
+
27
+ private
28
+ def target_name
29
+ @name ? "Mock '#{@name}'" : @target.inspect
30
+ end
31
+
32
+ def format_args(*args)
33
+ return '(no args)' if args.empty?
34
+ return '(any args)' if args == [:any]
35
+ "(#{args.map { |a| a.inspect }.join(', ')})"
36
+ end
37
+
38
+ def format_count(count)
39
+ count == 1 ? '1 time' : "#{count} times"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,130 @@
1
+ require 'forwardable'
2
+
3
+ module Facon
4
+ # An Expectation, also know as a mock method, is an expectation that an object
5
+ # should receive a specific message during the execution of an example.
6
+ class Expectation
7
+ extend ::Forwardable
8
+ def_delegators :@error_generator, :raise_expectation_error, :raise_block_failed_error
9
+
10
+ attr_reader :error_generator, :expectation_ordering, :expected_from, :method, :method_block, :expected_received_count, :actual_received_count, :argument_expectation
11
+
12
+ def initialize(error_generator, expectation_ordering, expected_from, method, method_block, expected_received_count = 1)
13
+ @error_generator = error_generator
14
+ @expectation_ordering = expectation_ordering
15
+ @expected_from = expected_from
16
+ @method = method
17
+ @method_block = method_block
18
+ @expected_received_count = expected_received_count
19
+
20
+ @argument_expectation = :any
21
+ @exception_to_raise = nil
22
+ @symbol_to_throw = nil
23
+ @actual_received_count = 0
24
+ @args_to_yield = []
25
+ end
26
+
27
+ # Sets up the expected method to return the given value.
28
+ def and_return(value)
29
+ raise MockExpectationError, 'Ambiguous return expectation' unless @method_block.nil?
30
+
31
+ @return_block = lambda { value }
32
+ end
33
+
34
+ # Sets up the expected method to yield with the given arguments.
35
+ def and_yield(*args)
36
+ @args_to_yield << args
37
+ self
38
+ end
39
+
40
+ # Sets up the expected method to raise the given <code>exception</code>
41
+ # (default: Exception).
42
+ def and_raise(exception = Exception)
43
+ @exception_to_raise = exception
44
+ end
45
+
46
+ # Sets up the expected method to throw the given <code>symbol</code>.
47
+ def and_throw(sym)
48
+ @symbol_to_throw = sym
49
+ end
50
+
51
+ def with(*args, &block)
52
+ @method_block = block if block
53
+ @argument_expectation = args
54
+ self
55
+ end
56
+
57
+ def invoke(args, block)
58
+ begin
59
+ raise @exception_to_raise unless @exception_to_raise.nil?
60
+ throw @symbol_to_throw unless @symbol_to_throw.nil?
61
+
62
+ return_value = if !@method_block.nil?
63
+ invoke_method_block(args)
64
+ elsif @args_to_yield.size > 0
65
+ @args_to_yield.each { |curr_args| block.call(*curr_args) }
66
+ else
67
+ nil
68
+ end
69
+
70
+ if @return_block
71
+ args << block unless block.nil?
72
+ @return_block.call(*args)
73
+ else
74
+ return_value
75
+ end
76
+ ensure
77
+ @actual_received_count += 1
78
+ end
79
+ end
80
+
81
+ # Returns true if this expectation has been met.
82
+ # TODO at_least and at_most conditions
83
+ def met?
84
+ return true if @expected_received_count == :any ||
85
+ @expected_received_count == @actual_received_count
86
+
87
+ raise_expectation_error(self)
88
+ end
89
+
90
+ # Returns true if the given <code>method</code> and arguments match this
91
+ # Expectation.
92
+ def matches(method, args)
93
+ @method == method && check_arguments(args)
94
+ end
95
+
96
+ # Returns true if the given <code>method</code> matches this Expectation,
97
+ # but the given arguments don't.
98
+ def matches_name_but_not_args(method, args)
99
+ @method == method && !check_arguments(args)
100
+ end
101
+
102
+ def negative_expectation_for?(method)
103
+ false
104
+ end
105
+
106
+ private
107
+ def check_arguments(args)
108
+ case @argument_expectation
109
+ when :any then true
110
+ when args then true
111
+ end
112
+ end
113
+
114
+ def invoke_method_block(args)
115
+ @method_block.call(*args)
116
+ rescue => e
117
+ raise_block_failed_error(@method, e.message)
118
+ end
119
+ end
120
+
121
+ class NegativeExpectation < Expectation
122
+ def initialize(error_generator, expectation_ordering, expected_from, method, method_block, expected_received_count = 0)
123
+ super(error_generator, expectation_ordering, expected_from, method, method_block, expected_received_count)
124
+ end
125
+
126
+ def negative_expectation_for?(method)
127
+ @method == method
128
+ end
129
+ end
130
+ end
data/lib/facon/mock.rb ADDED
@@ -0,0 +1,41 @@
1
+ module Facon
2
+ # Error returned when an expectation on a mock is not satisfied.
3
+ class MockExpectationError < StandardError
4
+ end
5
+
6
+ # A mock object is a 'fake' object on which you can impose simulated behavior.
7
+ # Defining expectations and stubs on mock objects allows you to specify
8
+ # object collaboration without needing to actually instantiate those
9
+ # collaborative objects.
10
+ #
11
+ # == Examples
12
+ #
13
+ # mock = mock('A name')
14
+ # mock = mock('Mock person', :name => 'Konata', :sex => 'female')
15
+ class Mock
16
+ include Facon::Mockable
17
+
18
+ attr_accessor :name
19
+
20
+ def initialize(name, stubs = {})
21
+ @name = name
22
+ stub_out(stubs)
23
+ end
24
+
25
+ def method_missing(method, *args, &block)
26
+ super(method, *args, &block)
27
+ rescue NameError
28
+ # An unexpected method was called on this mock.
29
+ mock_proxy.raise_unexpected_message_error(method, *args)
30
+ end
31
+
32
+ private
33
+
34
+ # Stubs out all the given stubs.
35
+ def stub_out(stubs)
36
+ stubs.each_pair do |method, response|
37
+ stub!(method).and_return(response)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,43 @@
1
+ module Facon
2
+ # A module containing convenient methods for creating mocks, stubs and
3
+ # expectations.
4
+ module Mockable
5
+
6
+ # Shortcut for creating a Facon::Mock instance.
7
+ #
8
+ # == Example
9
+ #
10
+ # mock = mock('test mock', :foo => 'bar')
11
+ # mock.foo # => 'bar'
12
+ def mock(name, stubs = {})
13
+ Mock.new(name, stubs)
14
+ end
15
+
16
+ def stub!(method)
17
+ mock_proxy.add_stub(caller(1)[0], method)
18
+ end
19
+
20
+ def should_receive(method, &block)
21
+ mock_proxy.add_expectation(caller(1)[0], method, &block)
22
+ end
23
+
24
+ def should_not_receive(method, &block)
25
+ mock_proxy.add_negative_expectation(caller(1)[0], method, &block)
26
+ end
27
+
28
+ # Verifies that the expectations set on this mock are all met, otherwise
29
+ # raises a MockExpectationError.
30
+ def spec_verify
31
+ mock_proxy.verify
32
+ end
33
+
34
+ def spec_reset
35
+ mock_proxy.reset
36
+ end
37
+
38
+ # Returns the mock proxy object.
39
+ def mock_proxy
40
+ @mock_proxy ||= Proxy.new(self, Mock === self ? @name : self.class.name)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,99 @@
1
+ require 'forwardable'
2
+
3
+ module Facon
4
+ # A proxy for mock objects. Stubs and expectations are set on this proxy.
5
+ class Proxy
6
+ extend ::Forwardable
7
+ def_delegators :@error_generator, :raise_unexpected_message_error, :raise_unexpected_message_args_error
8
+
9
+ def initialize(target, name)
10
+ @target, @name = target, name
11
+ @expectations = []
12
+ @stubs = []
13
+ @error_generator = ErrorGenerator.new(target, name)
14
+ end
15
+
16
+ def add_stub(expected_from, method)
17
+ $facon_mocks << (@target) unless $facon_mocks.nil?
18
+ define_expected_method(method)
19
+
20
+ # A stub is really an expectation that can be called any number of times.
21
+ @stubs.unshift(Expectation.new(@error_generator, @expectation_ordering, expected_from, method, nil, :any))
22
+ @stubs.first
23
+ end
24
+
25
+ def add_expectation(expected_from, method, &block)
26
+ $facon_mocks << (@target) unless $facon_mocks.nil?
27
+ define_expected_method(method)
28
+
29
+ @expectations << Expectation.new(@error_generator, @expectation_ordering, expected_from, method, (block_given? ? block : nil), 1)
30
+ @expectations.last
31
+ end
32
+
33
+ def add_negative_expectation(expected_from, method, &block)
34
+ $facon_mocks << (@target) unless $facon_mocks.nil?
35
+ define_expected_method(method)
36
+
37
+ @expectations << NegativeExpectation.new(@error_generator, @expectation_ordering, expected_from, method, (block_given? ? block : nil))
38
+ @expectations.last
39
+ end
40
+
41
+ def message_received(method, *args, &block)
42
+ if expectation = find_matching_expectation(method, *args)
43
+ expectation.invoke(args, block)
44
+ elsif stub = find_matching_method_stub(method, *args)
45
+ stub.invoke([], block)
46
+ elsif expectation = find_almost_matching_expectation(method, *args)
47
+ raise_unexpected_message_args_error(expectation, *args) unless has_negative_expectation?(method)
48
+ else
49
+ @target.send(:method_missing, method, *args, &block)
50
+ end
51
+ end
52
+
53
+ def verify
54
+ @expectations.each { |expectation| expectation.met? }
55
+ ensure
56
+ reset
57
+ end
58
+
59
+ def reset
60
+ @expectations.clear
61
+ @stubs.clear
62
+ end
63
+
64
+ private
65
+ # Defines an expected method that simply calls the
66
+ # <code>message_received</code> method.
67
+ def define_expected_method(method)
68
+ metaclass_eval(<<-EOF, __FILE__, __LINE__)
69
+ def #{method}(*args, &block)
70
+ mock_proxy.message_received(:#{method}, *args, &block)
71
+ end
72
+ EOF
73
+ end
74
+
75
+ def metaclass
76
+ (class << @target; self; end)
77
+ end
78
+
79
+ def metaclass_eval(str, filename, lineno)
80
+ metaclass.class_eval(str, filename, lineno)
81
+ end
82
+
83
+ def find_matching_expectation(method, *args)
84
+ @expectations.find { |expectation| expectation.matches(method, args) }
85
+ end
86
+
87
+ def find_almost_matching_expectation(method, *args)
88
+ @expectations.find { |expectation| expectation.matches_name_but_not_args(method, args) }
89
+ end
90
+
91
+ def find_matching_method_stub(method, *args)
92
+ @stubs.find { |stub| stub.matches(method, args) }
93
+ end
94
+
95
+ def has_negative_expectation?(method)
96
+ @expectations.find { |expectation| expectation.negative_expectation_for?(method) }
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Facon::Baconize" do
4
+
5
+ before do
6
+ @mock = Facon::Mock.new('test mock')
7
+ end
8
+
9
+ it "should make the #receive instance method available to Should" do
10
+ Should.public_instance_methods.should.include?('receive')
11
+ end
12
+
13
+ it "should allow expectations on mocks with should.receive(:message)" do
14
+ @mock.should.receive(:message).and_return(:foo)
15
+
16
+ @mock.message.should == :foo
17
+ end
18
+
19
+ it "should allow expectations on mocks with should.not.receive(:message)" do
20
+ @mock.should.not.receive(:message)
21
+
22
+ @mock.to_s
23
+ # FIXME: needs a better test on the actual error raised, but the
24
+ # instance_eval in it() makes it hard.
25
+ end
26
+
27
+ it "should allow expectations on arbitrary objects with should.receive(:message)" do
28
+ Object.should.receive(:message).and_return(:foo)
29
+ @object = Object.new
30
+ @object.should.receive(:message).and_return(:bar)
31
+
32
+ Object.message.should == :foo
33
+ @object.message.should == :bar
34
+ end
35
+ end
@@ -0,0 +1,124 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "A mock object" do
4
+ before do
5
+ @mock = Facon::Mock.new('test mock')
6
+ end
7
+
8
+ after do
9
+ @mock.spec_reset
10
+ end
11
+
12
+ def verify_mock
13
+ lambda { @mock.spec_verify }.should.not.raise(Facon::MockExpectationError)
14
+ end
15
+
16
+ it "should pass when not receiving message specified as not to be received" do
17
+ @mock.should_not_receive(:not_expected)
18
+
19
+ verify_mock
20
+ end
21
+
22
+ it "should pass when receiving message specified as not to be received with different args" do
23
+ @mock.should_not_receive(:message).with(:not_this)
24
+ @mock.should_receive(:message).with(:but_this)
25
+ @mock.message(:but_this)
26
+
27
+ verify_mock
28
+ end
29
+
30
+ it "should raise a MockExpectationError when receiving message specified as not to be received" do
31
+ @mock.should_not_receive(:not_expected)
32
+ @mock.not_expected
33
+
34
+ lambda { @mock.spec_verify }.should.raise(Facon::MockExpectationError).message.should == "Mock 'test mock' expected :not_expected with (any args) 0 times, but received it 1 time"
35
+ end
36
+
37
+ it "should raise a MockExpectationError when receiving message specified as not to be received with arguments" do
38
+ @mock.should_not_receive(:not_expected).with(:unexpected_arg)
39
+ @mock.not_expected(:unexpected_arg)
40
+
41
+ lambda { @mock.spec_verify }.should.raise(Facon::MockExpectationError).message.should == "Mock 'test mock' expected :not_expected with (:unexpected_arg) 0 times, but received it 1 time"
42
+ end
43
+
44
+ it "should pass when receiving message specified as not to be received, but with wrong arguments" do
45
+ @mock.should_not_receive(:not_expected).with(:unexpected_arg)
46
+ @mock.not_expected(:wrong_arg)
47
+
48
+ verify_mock
49
+ end
50
+
51
+ it "should raise a MockExpectationError when receiving message specified as to be received is not called" do
52
+ @mock.should_receive(:expected_message)
53
+
54
+ lambda { @mock.spec_verify }.should.raise(Facon::MockExpectationError).message.should == "Mock 'test mock' expected :expected_message with (any args) 1 time, but received it 0 times"
55
+ end
56
+
57
+ it "should raise a MockExpectationError when receiving message specified as to be received, but with wrong arguments" do
58
+ @mock.should_receive(:expected_message).with(:expected_arg)
59
+
60
+ lambda {
61
+ @mock.expected_message(:unexpected_arg)
62
+ }.should.raise(Facon::MockExpectationError).message.should == "Mock 'test mock' expected :expected_message with (:expected_arg), but received it with (:unexpected_arg)"
63
+ end
64
+
65
+ it "should raise a MockExpectationError when receiving an unexpected message" do
66
+ lambda {
67
+ @mock.not_expected
68
+ }.should.raise(Facon::MockExpectationError).message.should == "Mock 'test mock' received unexpected message :not_expected with (no args)"
69
+ end
70
+
71
+ it "should raise a MockExpectationError when receiving an unexpected message with arguments" do
72
+ lambda {
73
+ @mock.not_expected(:foo, :bar)
74
+ }.should.raise(Facon::MockExpectationError).message.should == "Mock 'test mock' received unexpected message :not_expected with (:foo, :bar)"
75
+ end
76
+
77
+ it "should use block for expectation if provided" do
78
+ @mock.should_receive(:expected_message) do |a, b|
79
+ a.should == :first
80
+ b.should == :second
81
+ 'foo'
82
+ end
83
+
84
+ @mock.expected_message(:first, :second).should == 'foo'
85
+
86
+ verify_mock
87
+ end
88
+
89
+ it "should use block with given arguments for expectation if provided" do
90
+ @mock.should_receive(:expected_message).with(:expected_arg) do |arg|
91
+ arg.should == :expected_arg
92
+ 'foo'
93
+ end
94
+
95
+ @mock.expected_message(:expected_arg).should == 'foo'
96
+
97
+ verify_mock
98
+ end
99
+
100
+ it "should raise a MockExpectationError if block for expectation fails" do
101
+ @mock.should_receive(:expected_message) do |arg|
102
+ arg.should == :expected_arg
103
+ end
104
+
105
+ lambda {
106
+ @mock.expected_message(:unexpected_arg)
107
+ }.should.raise(Facon::MockExpectationError).message.should == "Mock 'test mock' received :expected_message but passed block failed with: :unexpected_arg.==(:expected_arg) failed"
108
+ end
109
+
110
+ it "should raise a MockExpectationError if there's a block for expectation and an #and_return expectation is also set" do
111
+ lambda {
112
+ @mock.should_receive(:foo) do
113
+ :this
114
+ end.and_return(:that)
115
+ }.should.raise(Facon::MockExpectationError).message.should == 'Ambiguous return expectation'
116
+ end
117
+ end
118
+
119
+
120
+
121
+
122
+
123
+
124
+
data/spec/mock_spec.rb ADDED
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Facon::Mock" do
4
+ before do
5
+ @mock = Facon::Mock.new('test mock')
6
+ end
7
+
8
+ it "should stub out a method on a mock" do
9
+ @mock.stub!(:stubbed_method).and_return(:stub_value)
10
+
11
+ @mock.stubbed_method.should == :stub_value
12
+ @mock.stubbed_method.should == :stub_value # called twice to ensure it stays
13
+ end
14
+
15
+ it "should stub out a method on a non-mock" do
16
+ non_mock = Object.new
17
+ non_mock.stub!(:stubbed_method).and_return(:stub_value)
18
+
19
+ non_mock.stubbed_method.should == :stub_value
20
+ non_mock.stubbed_method.should == :stub_value # called twice to ensure it stays
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'bacon'
3
+
4
+ lib_path = File.expand_path("#{File.dirname(__FILE__)}/../lib")
5
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
6
+ require 'facon'
data/spec/stub_spec.rb ADDED
@@ -0,0 +1,127 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "A method stub" do
4
+ before do
5
+ @class = Class.new do
6
+ def self.existing_class_method
7
+ :original_value
8
+ end
9
+
10
+ def existing_instance_method
11
+ :original_value
12
+ end
13
+ end
14
+ @object = @class.new
15
+ end
16
+
17
+ it "should override existing methods" do
18
+ @object.existing_instance_method.should == :original_value
19
+ @object.stub!(:existing_instance_method).and_return(:stubbed_value)
20
+
21
+ @object.existing_instance_method.should == :stubbed_value
22
+ end
23
+
24
+ it "should return the expected value given to #and_return when the stubbed method is received" do
25
+ @object.stub!(:stubbed_method).and_return(:stubbed_value)
26
+
27
+ @object.stubbed_method.should == :stubbed_value
28
+ end
29
+
30
+ it "should ignore when the stubbed method is received" do
31
+ @object.stub!(:stubbed_method)
32
+
33
+ lambda {
34
+ @object.stubbed_method
35
+ }.should.not.raise
36
+ end
37
+
38
+ it "should ignore when the stubbed method is received with arguments" do
39
+ @object.stub!(:stubbed_method)
40
+
41
+ lambda {
42
+ @object.stubbed_method(:some_arg)
43
+ }.should.not.raise
44
+ end
45
+
46
+ it "should yield the argument given to #and_yield" do
47
+ @object.stub!(:yielding_method).and_yield(:yielded)
48
+
49
+ yielded = nil
50
+ @object.yielding_method { |arg| yielded = arg }
51
+
52
+ yielded.should == :yielded
53
+ end
54
+
55
+ it "should yield the multiple arguments given to #and_yield" do
56
+ @object.stub!(:yielding_method).and_yield(:first, :second)
57
+
58
+ first_arg, second_arg = nil, nil
59
+ @object.yielding_method { |arg1, arg2| first_arg, second_arg = arg1, arg2 }
60
+
61
+ first_arg.should == :first
62
+ second_arg.should == :second
63
+ end
64
+
65
+ it "should yield multiple times with multiple calls to #and_yield (i.e. allow chaining of #and_yield calls)" do
66
+ @object.stub!(:yielding_method).and_yield(:one).and_yield(:two)
67
+
68
+ yielded = []
69
+ @object.yielding_method { |arg| yielded << arg }
70
+
71
+ yielded.should == [:one, :two]
72
+ end
73
+
74
+ it "should be able to yield and return different objects (i.e. allow #and_yield and #and_return chaining)" do
75
+ @object.stub!(:yield_and_return).and_yield(:yielded).and_return(:returned)
76
+
77
+ yielded = nil
78
+ @object.yield_and_return { |arg| yielded = arg }.should == :returned
79
+
80
+ yielded.should == :yielded
81
+ end
82
+
83
+ it "should raise the expected exception given to #and_raise" do
84
+ @object.stub!(:failing_method).and_raise(RuntimeError)
85
+
86
+ lambda { @object.failing_method }.should.raise(RuntimeError)
87
+ end
88
+
89
+ it "should throw the expected thrown value given to #and_throw" do
90
+ @object.stub!(:pitch).and_throw(:ball)
91
+
92
+ lambda { @object.pitch }.should.throw(:ball)
93
+ end
94
+
95
+ it "should ignore when the stubbed method is never called" do
96
+ end
97
+ end
98
+
99
+ describe "A method stub with arguments" do
100
+
101
+ before do
102
+ @object = Object.new
103
+ @object.stub!(:stubbed_method).with(:expected_arg).and_return(:stubbed_value)
104
+ end
105
+
106
+ it "should ignore when never called" do
107
+ end
108
+
109
+ it "should ignore when called with expected argument" do
110
+ @object.stubbed_method(:expected_arg)
111
+ end
112
+
113
+ it "should raise a NoMethodError when called with no arguments" do
114
+ lambda { @object.stubbed_method }.should.raise(NoMethodError)
115
+ end
116
+
117
+ it "should raise a NoMethodError when called with some other argument" do
118
+ lambda { @object.stubbed_method(:something_else) }.should.raise(NoMethodError)
119
+ end
120
+
121
+ it "should allow another stub with different arguments" do
122
+ @object.stub!(:stubbed_method).with(:something_else).and_return(:bar)
123
+
124
+ @object.stubbed_method(:expected_arg).should == :stubbed_value
125
+ @object.stubbed_method(:something_else).should == :bar
126
+ end
127
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: facon
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Cheah Chu Yeow
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-02-10 00:00:00 +08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.5.0
23
+ version:
24
+ description: A mocking library in the spirit of the Bacon spec library. Small, compact, and works with Bacon.
25
+ email: chuyeow@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - History.txt
32
+ - Manifest.txt
33
+ - README.txt
34
+ files:
35
+ - History.txt
36
+ - Manifest.txt
37
+ - README.txt
38
+ - Rakefile
39
+ - lib/facon.rb
40
+ - lib/facon/baconize.rb
41
+ - lib/facon/core_ext/object.rb
42
+ - lib/facon/error_generator.rb
43
+ - lib/facon/expectation.rb
44
+ - lib/facon/mock.rb
45
+ - lib/facon/mockable.rb
46
+ - lib/facon/proxy.rb
47
+ - spec/baconize_spec.rb
48
+ - spec/expectation_spec.rb
49
+ - spec/mock_spec.rb
50
+ - spec/spec_helper.rb
51
+ - spec/stub_spec.rb
52
+ has_rdoc: true
53
+ homepage: http://facon.rubyforge.org/
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --main
57
+ - README.txt
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project: facon
75
+ rubygems_version: 1.0.1
76
+ signing_key:
77
+ specification_version: 2
78
+ summary: Tiny mocking library.
79
+ test_files: []
80
+