aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/plugins/rspec/lib/spec/mocks
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/plugins/rspec/lib/spec/mocks')
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/argument_constraint_matchers.rb31
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/argument_expectation.rb210
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/error_generator.rb28
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/errors.rb2
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/framework.rb4
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/message_expectation.rb106
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/methods.rb56
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/mock.rb61
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/proxy.rb139
-rw-r--r--vendor/plugins/rspec/lib/spec/mocks/spec_methods.rb38
10 files changed, 312 insertions, 363 deletions
diff --git a/vendor/plugins/rspec/lib/spec/mocks/argument_constraint_matchers.rb b/vendor/plugins/rspec/lib/spec/mocks/argument_constraint_matchers.rb
deleted file mode 100644
index 96ccf0f4a..000000000
--- a/vendor/plugins/rspec/lib/spec/mocks/argument_constraint_matchers.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-module Spec
- module Mocks
- module ArgumentConstraintMatchers
-
- # Shortcut for creating an instance of Spec::Mocks::DuckTypeArgConstraint
- def duck_type(*args)
- DuckTypeArgConstraint.new(*args)
- end
-
- def any_args
- AnyArgsConstraint.new
- end
-
- def anything
- AnyArgConstraint.new(nil)
- end
-
- def boolean
- BooleanArgConstraint.new(nil)
- end
-
- def hash_including(expected={})
- HashIncludingConstraint.new(expected)
- end
-
- def no_args
- NoArgsConstraint.new
- end
- end
- end
-end
diff --git a/vendor/plugins/rspec/lib/spec/mocks/argument_expectation.rb b/vendor/plugins/rspec/lib/spec/mocks/argument_expectation.rb
index b3fdcc80d..b51b7bae5 100644
--- a/vendor/plugins/rspec/lib/spec/mocks/argument_expectation.rb
+++ b/vendor/plugins/rspec/lib/spec/mocks/argument_expectation.rb
@@ -1,208 +1,50 @@
module Spec
module Mocks
-
- class MatcherConstraint
- def initialize(matcher)
- @matcher = matcher
- end
-
- def matches?(value)
- @matcher.matches?(value)
- end
- end
-
- class LiteralArgConstraint
- def initialize(literal)
- @literal_value = literal
- end
-
- def matches?(value)
- @literal_value == value
- end
- end
-
- class RegexpArgConstraint
- def initialize(regexp)
- @regexp = regexp
- end
-
- def matches?(value)
- return value =~ @regexp unless value.is_a?(Regexp)
- value == @regexp
- end
- end
-
- class AnyArgConstraint
- def initialize(ignore)
- end
-
- def ==(other)
- true
- end
-
- # TODO - need this?
- def matches?(value)
- true
- end
- end
-
- class AnyArgsConstraint
- def description
- "any args"
- end
- end
-
- class NoArgsConstraint
- def description
- "no args"
- end
-
- def ==(args)
- args == []
- end
- end
-
- class NumericArgConstraint
- def initialize(ignore)
- end
-
- def matches?(value)
- value.is_a?(Numeric)
- end
- end
- class BooleanArgConstraint
- def initialize(ignore)
- end
-
- def ==(value)
- matches?(value)
- end
-
- def matches?(value)
- return true if value.is_a?(TrueClass)
- return true if value.is_a?(FalseClass)
- false
- end
- end
-
- class StringArgConstraint
- def initialize(ignore)
- end
-
- def matches?(value)
- value.is_a?(String)
- end
- end
-
- class DuckTypeArgConstraint
- def initialize(*methods_to_respond_to)
- @methods_to_respond_to = methods_to_respond_to
- end
-
- def matches?(value)
- @methods_to_respond_to.all? { |sym| value.respond_to?(sym) }
- end
-
- def description
- "duck_type"
- end
- end
-
- class HashIncludingConstraint
- def initialize(expected)
- @expected = expected
- end
-
- def ==(actual)
- @expected.each do | key, value |
- # check key for case that value evaluates to nil
- return false unless actual.has_key?(key) && actual[key] == value
- end
- true
- rescue NoMethodError => ex
- return false
- end
-
- def matches?(value)
- self == value
- end
-
- def description
- "hash_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})"
- end
-
- end
-
-
class ArgumentExpectation
attr_reader :args
- @@constraint_classes = Hash.new { |hash, key| LiteralArgConstraint}
- @@constraint_classes[:anything] = AnyArgConstraint
- @@constraint_classes[:numeric] = NumericArgConstraint
- @@constraint_classes[:boolean] = BooleanArgConstraint
- @@constraint_classes[:string] = StringArgConstraint
- def initialize(args)
+ def initialize(args, &block)
@args = args
- if [:any_args] == args
- @expected_params = nil
- warn_deprecated(:any_args.inspect, "any_args()")
- elsif args.length == 1 && args[0].is_a?(AnyArgsConstraint) then @expected_params = nil
- elsif [:no_args] == args
- @expected_params = []
- warn_deprecated(:no_args.inspect, "no_args()")
- elsif args.length == 1 && args[0].is_a?(NoArgsConstraint) then @expected_params = []
- else @expected_params = process_arg_constraints(args)
+ @matchers_block = block
+ @match_any_args = false
+ @matchers = nil
+
+ if ArgumentMatchers::AnyArgsMatcher === args.first
+ @match_any_args = true
+ elsif ArgumentMatchers::NoArgsMatcher === args.first
+ @matchers = []
+ else
+ @matchers = args.collect {|arg| matcher_for(arg)}
end
end
- def process_arg_constraints(constraints)
- constraints.collect do |constraint|
- convert_constraint(constraint)
- end
+ def matcher_for(arg)
+ return ArgumentMatchers::MatcherMatcher.new(arg) if is_matcher?(arg)
+ return ArgumentMatchers::RegexpMatcher.new(arg) if arg.is_a?(Regexp)
+ return ArgumentMatchers::EqualityProxy.new(arg)
end
- def warn_deprecated(deprecated_method, instead)
- Kernel.warn "The #{deprecated_method} constraint is deprecated. Use #{instead} instead."
+ def is_matcher?(obj)
+ return obj.respond_to?(:matches?) & obj.respond_to?(:description)
end
- def convert_constraint(constraint)
- if [:anything, :numeric, :boolean, :string].include?(constraint)
- case constraint
- when :anything
- instead = "anything()"
- when :boolean
- instead = "boolean()"
- when :numeric
- instead = "an_instance_of(Numeric)"
- when :string
- instead = "an_instance_of(String)"
- end
- warn_deprecated(constraint.inspect, instead)
- return @@constraint_classes[constraint].new(constraint)
- end
- return MatcherConstraint.new(constraint) if is_matcher?(constraint)
- return RegexpArgConstraint.new(constraint) if constraint.is_a?(Regexp)
- return LiteralArgConstraint.new(constraint)
+ def args_match?(given_args)
+ match_any_args? || matchers_block_matches?(given_args) || matchers_match?(given_args)
end
- def is_matcher?(obj)
- return obj.respond_to?(:matches?) && obj.respond_to?(:description)
+ def matchers_block_matches?(given_args)
+ @matchers_block ? @matchers_block.call(*given_args) : nil
end
- def check_args(args)
- return true if @expected_params.nil?
- return true if @expected_params == args
- return constraints_match?(args)
+ def matchers_match?(given_args)
+ @matchers == given_args
end
- def constraints_match?(args)
- return false if args.length != @expected_params.length
- @expected_params.each_index { |i| return false unless @expected_params[i].matches?(args[i]) }
- return true
+ def match_any_args?
+ @match_any_args
end
-
+
end
end
diff --git a/vendor/plugins/rspec/lib/spec/mocks/error_generator.rb b/vendor/plugins/rspec/lib/spec/mocks/error_generator.rb
index 01d8f720d..f63811fed 100644
--- a/vendor/plugins/rspec/lib/spec/mocks/error_generator.rb
+++ b/vendor/plugins/rspec/lib/spec/mocks/error_generator.rb
@@ -3,7 +3,8 @@ module Spec
class ErrorGenerator
attr_writer :opts
- def initialize(target, name)
+ def initialize(target, name, options={})
+ @declared_as = options[:__declared_as] || 'Mock'
@target = target
@name = name
end
@@ -19,7 +20,7 @@ module Spec
def raise_unexpected_message_args_error(expectation, *args)
expected_args = format_args(*expectation.expected_args)
actual_args = args.empty? ? "(no args)" : format_args(*args)
- __raise "#{intro} expected #{expectation.sym.inspect} with #{expected_args} but received it with #{actual_args}"
+ __raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
end
def raise_expectation_error(sym, expected_received_count, actual_received_count, *args)
@@ -42,9 +43,20 @@ module Spec
__raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with arity of #{arity}"
end
- private
+ private
+
def intro
- @name ? "Mock '#{@name}'" : @target.inspect
+ if @name
+ "#{@declared_as} #{@name.inspect}"
+ elsif Mock === @target
+ @declared_as
+ elsif Class === @target
+ "<#{@target.inspect} (class)>"
+ elsif @target
+ @target
+ else
+ "nil"
+ end
end
def __raise(message)
@@ -57,15 +69,11 @@ module Spec
end
def format_args(*args)
- return "(no args)" if args.empty? || args == [:no_args]
- return "(any args)" if args == [:any_args]
- "(" + arg_list(*args) + ")"
+ args.empty? ? "(no args)" : "(" + arg_list(*args) + ")"
end
def arg_list(*args)
- args.collect do |arg|
- arg.respond_to?(:description) ? arg.description : arg.inspect
- end.join(", ")
+ args.collect {|arg| arg.respond_to?(:description) ? arg.description : arg.inspect}.join(", ")
end
def count_message(count)
diff --git a/vendor/plugins/rspec/lib/spec/mocks/errors.rb b/vendor/plugins/rspec/lib/spec/mocks/errors.rb
index 68fdfe006..560b66a93 100644
--- a/vendor/plugins/rspec/lib/spec/mocks/errors.rb
+++ b/vendor/plugins/rspec/lib/spec/mocks/errors.rb
@@ -1,6 +1,6 @@
module Spec
module Mocks
- class MockExpectationError < StandardError
+ class MockExpectationError < Exception
end
class AmbiguousReturnError < StandardError
diff --git a/vendor/plugins/rspec/lib/spec/mocks/framework.rb b/vendor/plugins/rspec/lib/spec/mocks/framework.rb
index 92089673a..e25778655 100644
--- a/vendor/plugins/rspec/lib/spec/mocks/framework.rb
+++ b/vendor/plugins/rspec/lib/spec/mocks/framework.rb
@@ -3,8 +3,8 @@
# object in the system.
require 'spec/mocks/methods'
-require 'spec/mocks/argument_constraint_matchers'
-require 'spec/mocks/spec_methods'
+require 'spec/mocks/argument_matchers'
+require 'spec/mocks/example_methods'
require 'spec/mocks/proxy'
require 'spec/mocks/mock'
require 'spec/mocks/argument_expectation'
diff --git a/vendor/plugins/rspec/lib/spec/mocks/message_expectation.rb b/vendor/plugins/rspec/lib/spec/mocks/message_expectation.rb
index 0b10290b3..8a8133f69 100644
--- a/vendor/plugins/rspec/lib/spec/mocks/message_expectation.rb
+++ b/vendor/plugins/rspec/lib/spec/mocks/message_expectation.rb
@@ -3,17 +3,20 @@ module Spec
class BaseExpectation
attr_reader :sym
+ attr_writer :expected_received_count, :method_block, :expected_from
+ protected :expected_received_count=, :method_block=, :expected_from=
+ attr_accessor :error_generator
+ protected :error_generator, :error_generator=
- def initialize(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={})
+ def initialize(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={}, &implementation)
@error_generator = error_generator
@error_generator.opts = opts
@expected_from = expected_from
@sym = sym
@method_block = method_block
- @return_block = nil
@actual_received_count = 0
@expected_received_count = expected_received_count
- @args_expectation = ArgumentExpectation.new([AnyArgsConstraint.new])
+ @args_expectation = ArgumentExpectation.new([ArgumentMatchers::AnyArgsMatcher.new])
@consecutive = false
@exception_to_raise = nil
@symbol_to_throw = nil
@@ -21,6 +24,22 @@ module Spec
@at_least = nil
@at_most = nil
@args_to_yield = []
+ @failed_fast = nil
+ @args_to_yield_were_cloned = false
+ @return_block = implementation
+ end
+
+ def build_child(expected_from, method_block, expected_received_count, opts={})
+ child = clone
+ child.expected_from = expected_from
+ child.method_block = method_block
+ child.expected_received_count = expected_received_count
+ child.clear_actual_received_count!
+ new_gen = error_generator.clone
+ new_gen.opts = opts
+ child.error_generator = new_gen
+ child.clone_args_to_yield @args_to_yield
+ child
end
def expected_args
@@ -39,8 +58,6 @@ module Spec
@expected_received_count < values.size
end
@return_block = block_given? ? return_block : lambda { value }
- # Ruby 1.9 - see where this is used below
- @ignore_args = !block_given?
end
# :call-seq:
@@ -63,16 +80,22 @@ module Spec
end
def and_yield(*args)
+ if @args_to_yield_were_cloned
+ @args_to_yield.clear
+ @args_to_yield_were_cloned = false
+ end
+
@args_to_yield << args
self
end
-
+
def matches(sym, args)
- @sym == sym and @args_expectation.check_args(args)
+ @sym == sym and @args_expectation.args_match?(args)
end
- def invoke(args, block)
+ def invoke(*args, &block)
if @expected_received_count == 0
+ @failed_fast = true
@actual_received_count += 1
@error_generator.raise_expectation_error @sym, @expected_received_count, @actual_received_count, *args
end
@@ -85,17 +108,17 @@ module Spec
if !@method_block.nil?
- default_return_val = invoke_method_block(args)
+ default_return_val = invoke_method_block(*args)
elsif @args_to_yield.size > 0
- default_return_val = invoke_with_yield(block)
+ default_return_val = invoke_with_yield(&block)
else
default_return_val = nil
end
if @consecutive
- return invoke_consecutive_return_block(args, block)
+ return invoke_consecutive_return_block(*args, &block)
elsif @return_block
- return invoke_return_block(args, block)
+ return invoke_return_block(*args, &block)
else
return default_return_val
end
@@ -103,10 +126,23 @@ module Spec
@actual_received_count += 1
end
end
+
+ def called_max_times?
+ @expected_received_count != :any && @expected_received_count > 0 &&
+ @actual_received_count >= @expected_received_count
+ end
+
+ def invoke_return_block(*args, &block)
+ args << block unless block.nil?
+ # Ruby 1.9 - when we set @return_block to return values
+ # regardless of arguments, any arguments will result in
+ # a "wrong number of arguments" error
+ @return_block.arity == 0 ? @return_block.call : @return_block.call(*args)
+ end
protected
- def invoke_method_block(args)
+ def invoke_method_block(*args)
begin
@method_block.call(*args)
rescue => detail
@@ -114,7 +150,7 @@ module Spec
end
end
- def invoke_with_yield(block)
+ def invoke_with_yield(&block)
if block.nil?
@error_generator.raise_missing_block_error @args_to_yield
end
@@ -128,35 +164,34 @@ module Spec
value
end
- def invoke_consecutive_return_block(args, block)
- args << block unless block.nil?
- value = @return_block.call(*args)
-
+ def invoke_consecutive_return_block(*args, &block)
+ value = invoke_return_block(*args, &block)
index = [@actual_received_count, value.size-1].min
value[index]
end
- def invoke_return_block(args, block)
- args << block unless block.nil?
- # Ruby 1.9 - when we set @return_block to return values
- # regardless of arguments, any arguments will result in
- # a "wrong number of arguments" error
- if @ignore_args
- @return_block.call()
- else
- @return_block.call(*args)
- end
+ def clone_args_to_yield(args)
+ @args_to_yield = args.clone
+ @args_to_yield_were_cloned = true
+ end
+
+ def failed_fast?
+ @failed_fast
end
end
class MessageExpectation < BaseExpectation
+
+ def matches_name?(sym)
+ @sym == sym
+ end
def matches_name_but_not_args(sym, args)
- @sym == sym and not @args_expectation.check_args(args)
+ matches_name?(sym) and not @args_expectation.args_match?(args)
end
- def verify_messages_received
- return if expected_messages_received?
+ def verify_messages_received
+ return if expected_messages_received? || failed_fast?
generate_error
rescue Spec::Mocks::MockExpectationError => error
@@ -197,13 +232,12 @@ module Spec
if similar_messages.empty?
@error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *@args_expectation.args)
else
- @error_generator.raise_unexpected_message_args_error(self, *@similar_messages.first)
+ @error_generator.raise_unexpected_message_args_error(self, *@similar_messages)
end
end
def with(*args, &block)
- @method_block = block if block
- @args_expectation = ArgumentExpectation.new(args)
+ @args_expectation = ArgumentExpectation.new(args, &block)
self
end
@@ -274,6 +308,10 @@ module Spec
2
end
end
+
+ def clear_actual_received_count!
+ @actual_received_count = 0
+ end
end
diff --git a/vendor/plugins/rspec/lib/spec/mocks/methods.rb b/vendor/plugins/rspec/lib/spec/mocks/methods.rb
index d9fa324d3..16dd6e842 100644
--- a/vendor/plugins/rspec/lib/spec/mocks/methods.rb
+++ b/vendor/plugins/rspec/lib/spec/mocks/methods.rb
@@ -9,8 +9,50 @@ module Spec
__mock_proxy.add_negative_message_expectation(caller(1)[0], sym.to_sym, &block)
end
- def stub!(sym, opts={})
- __mock_proxy.add_stub(caller(1)[0], sym.to_sym, opts)
+ def stub!(sym_or_hash, opts={}, &block)
+ if Hash === sym_or_hash
+ sym_or_hash.each {|method, value| stub!(method).and_return value }
+ else
+ __mock_proxy.add_stub(caller(1)[0], sym_or_hash.to_sym, opts, &block)
+ end
+ end
+
+ alias_method :stub, :stub!
+
+ def unstub!(message)
+ __mock_proxy.remove_stub(message)
+ end
+
+ alias_method :unstub, :unstub!
+
+ # :call-seq:
+ # object.stub_chain(:first, :second, :third).and_return(:this)
+ #
+ # Supports stubbing a chain of methods. Each argument represents
+ # a method name to stub, and each one returns a proxy object that
+ # can accept more stubs, until the last, which returns whatever
+ # is passed to +and_return_.
+ #
+ # == Examples
+ #
+ # # with this in an example ...
+ # article = double('article')
+ # Article.stub_chain(:authored_by, :published, :recent).and_return([article])
+ # # then this will return an Array with the article double in it:
+ # Article.authored_by(params[:author_id]).published.recent
+ def stub_chain(*methods)
+ if methods.length > 1
+ if matching_stub = __mock_proxy.find_matching_method_stub(methods[0])
+ methods.shift
+ matching_stub.invoke_return_block.stub_chain(*methods)
+ else
+ next_in_chain = Object.new
+ stub!(methods.shift) {next_in_chain}
+ next_in_chain.stub_chain(*methods)
+ end
+ else
+ stub!(methods.shift)
+ end
end
def received_message?(sym, *args, &block) #:nodoc:
@@ -24,6 +66,14 @@ module Spec
def rspec_reset #:nodoc:
__mock_proxy.reset
end
+
+ def as_null_object
+ __mock_proxy.as_null_object
+ end
+
+ def null_object?
+ __mock_proxy.null_object?
+ end
private
@@ -31,7 +81,7 @@ module Spec
if Mock === self
@mock_proxy ||= Proxy.new(self, @name, @options)
else
- @mock_proxy ||= Proxy.new(self, self.class.name)
+ @mock_proxy ||= Proxy.new(self)
end
end
end
diff --git a/vendor/plugins/rspec/lib/spec/mocks/mock.rb b/vendor/plugins/rspec/lib/spec/mocks/mock.rb
index d0b5f204d..35a6c798e 100644
--- a/vendor/plugins/rspec/lib/spec/mocks/mock.rb
+++ b/vendor/plugins/rspec/lib/spec/mocks/mock.rb
@@ -7,23 +7,37 @@ module Spec
# only) == Options:
# * <tt>:null_object</tt> - if true, the mock object acts as a forgiving
# null object allowing any message to be sent to it.
- def initialize(name, stubs_and_options={})
- @name = name
- @options = parse_options(stubs_and_options)
+ def initialize(name=nil, stubs_and_options={})
+ if name.is_a?(Hash) && stubs_and_options.empty?
+ stubs_and_options = name
+ @name = nil
+ else
+ @name = name
+ end
+ @options = extract_options(stubs_and_options)
assign_stubs(stubs_and_options)
end
-
+
# This allows for comparing the mock to other objects that proxy such as
- # ActiveRecords belongs_to proxy objects By making the other object run
- # the comparison, we're sure the call gets delegated to the proxy target
- # This is an unfortunate side effect from ActiveRecord, but this should
- # be safe unless the RHS redefines == in a nonsensical manner
+ # ActiveRecords belongs_to proxy objects. By making the other object run
+ # the comparison, we're sure the call gets delegated to the proxy
+ # target.
def ==(other)
other == __mock_proxy
end
+ def inspect
+ "#<#{self.class}:#{sprintf '0x%x', self.object_id} @name=#{@name.inspect}>"
+ end
+
+ def to_s
+ inspect.gsub('<','[').gsub('>',']')
+ end
+
+ private
+
def method_missing(sym, *args, &block)
- __mock_proxy.instance_eval {@messages_received << [sym, args, block]}
+ __mock_proxy.record_message_received(sym, args, block)
begin
return self if __mock_proxy.null_object?
super(sym, *args, &block)
@@ -31,22 +45,27 @@ module Spec
__mock_proxy.raise_unexpected_message_error sym, *args
end
end
-
- def inspect
- "#<#{self.class}:#{sprintf '0x%x', self.object_id} @name=#{@name.inspect}>"
+
+ def extract_options(stubs_and_options)
+ options = {}
+ extract_option(stubs_and_options, options, :null_object)
+ extract_option(stubs_and_options, options, :__declared_as, 'Mock')
+ options
end
- private
-
- def parse_options(options)
- options.has_key?(:null_object) ? {:null_object => options.delete(:null_object)} : {}
+ def extract_option(source, target, key, default=nil)
+ if source[key]
+ target[key] = source.delete(key)
+ elsif default
+ target[key] = default
end
-
- def assign_stubs(stubs)
- stubs.each_pair do |message, response|
- stub!(message).and_return(response)
- end
+ end
+
+ def assign_stubs(stubs)
+ stubs.each_pair do |message, response|
+ stub!(message).and_return(response)
end
+ end
end
end
end
diff --git a/vendor/plugins/rspec/lib/spec/mocks/proxy.rb b/vendor/plugins/rspec/lib/spec/mocks/proxy.rb
index 45b96a30b..acf72e0f3 100644
--- a/vendor/plugins/rspec/lib/spec/mocks/proxy.rb
+++ b/vendor/plugins/rspec/lib/spec/mocks/proxy.rb
@@ -4,41 +4,75 @@ module Spec
DEFAULT_OPTIONS = {
:null_object => false,
}
+
+ @@warn_about_expectations_on_nil = true
+
+ def self.allow_message_expectations_on_nil
+ @@warn_about_expectations_on_nil = false
+
+ # ensure nil.rspec_verify is called even if an expectation is not set in the example
+ # otherwise the allowance would effect subsequent examples
+ $rspec_mocks.add(nil) unless $rspec_mocks.nil?
+ end
- def initialize(target, name, options={})
+ def initialize(target, name=nil, options={})
@target = target
@name = name
- @error_generator = ErrorGenerator.new target, name
+ @error_generator = ErrorGenerator.new target, name, options
@expectation_ordering = OrderGroup.new @error_generator
@expectations = []
@messages_received = []
@stubs = []
@proxied_methods = []
@options = options ? DEFAULT_OPTIONS.dup.merge(options) : DEFAULT_OPTIONS
+ @already_proxied_respond_to = false
end
def null_object?
@options[:null_object]
end
+
+ def as_null_object
+ @options[:null_object] = true
+ @target
+ end
- def add_message_expectation(expected_from, sym, opts={}, &block)
+ def add_message_expectation(expected_from, sym, opts={}, &block)
__add sym
- @expectations << MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil, 1, opts)
+ warn_if_nil_class sym
+ if existing_stub = @stubs.detect {|s| s.sym == sym }
+ expectation = existing_stub.build_child(expected_from, block_given?? block : nil, 1, opts)
+ else
+ expectation = MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil, 1, opts)
+ end
+ @expectations << expectation
@expectations.last
end
def add_negative_message_expectation(expected_from, sym, &block)
__add sym
+ warn_if_nil_class sym
@expectations << NegativeMessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil)
@expectations.last
end
- def add_stub(expected_from, sym, opts={})
+ def add_stub(expected_from, sym, opts={}, &implementation)
__add sym
- @stubs.unshift MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, nil, :any, opts)
+ @stubs.unshift MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, nil, :any, opts, &implementation)
@stubs.first
end
+ def remove_stub(message)
+ message = message.to_sym
+
+ if stub_to_remove = @stubs.detect { |s| s.matches_name?(message) }
+ reset_proxied_method(message)
+ @stubs.delete(stub_to_remove)
+ else
+ raise MockExpectationError, "The method `#{message}` was not stubbed or was already unstubbed"
+ end
+ end
+
def verify #:nodoc:
verify_expectations
ensure
@@ -50,6 +84,7 @@ module Spec
clear_stubs
reset_proxied_methods
clear_proxied_methods
+ reset_nil_expectations_warning
end
def received_message?(sym, *args, &block)
@@ -59,20 +94,27 @@ module Spec
def has_negative_expectation?(sym)
@expectations.detect {|expectation| expectation.negative_expectation_for?(sym)}
end
+
+ def record_message_received(sym, args, block)
+ @messages_received << [sym, args, block]
+ end
def message_received(sym, *args, &block)
- if expectation = find_matching_expectation(sym, *args)
- expectation.invoke(args, block)
- elsif (stub = find_matching_method_stub(sym, *args))
+ expectation = find_matching_expectation(sym, *args)
+ stub = find_matching_method_stub(sym, *args)
+
+ if (stub && expectation && expectation.called_max_times?) || (stub && !expectation)
if expectation = find_almost_matching_expectation(sym, *args)
expectation.advise(args, block) unless expectation.expected_messages_received?
end
- stub.invoke([], block)
+ stub.invoke(*args, &block)
+ elsif expectation
+ expectation.invoke(*args, &block)
elsif expectation = find_almost_matching_expectation(sym, *args)
expectation.advise(args, block) if null_object? unless expectation.expected_messages_received?
raise_unexpected_message_args_error(expectation, *args) unless (has_negative_expectation?(sym) or null_object?)
else
- @target.send :method_missing, sym, *args, &block
+ @target.__send__ :method_missing, sym, *args, &block
end
end
@@ -84,6 +126,10 @@ module Spec
@error_generator.raise_unexpected_message_error sym, *args
end
+ def find_matching_method_stub(sym, *args)
+ @stubs.find {|stub| stub.matches(sym, args)}
+ end
+
private
def __add(sym)
@@ -91,28 +137,35 @@ module Spec
define_expected_method(sym)
end
+ def warn_if_nil_class(sym)
+ if proxy_for_nil_class? & @@warn_about_expectations_on_nil
+ Kernel.warn("An expectation of :#{sym} was set on nil. Called from #{caller[2]}. Use allow_message_expectations_on_nil to disable warnings.")
+ end
+ end
+
def define_expected_method(sym)
- visibility_string = "#{visibility(sym)} :#{sym}"
- if target_responds_to?(sym) && !target_metaclass.method_defined?(munge(sym))
- munged_sym = munge(sym)
- target_metaclass.instance_eval do
- alias_method munged_sym, sym if method_defined?(sym.to_s)
+ unless @proxied_methods.include?(sym)
+ visibility_string = "#{visibility(sym)} :#{sym}"
+ if target_responds_to?(sym)
+ munged_sym = munge(sym)
+ target_metaclass.instance_eval do
+ alias_method munged_sym, sym if method_defined?(sym)
+ end
+ @proxied_methods << sym
end
- @proxied_methods << sym
+ target_metaclass.class_eval(<<-EOF, __FILE__, __LINE__)
+ def #{sym}(*args, &block)
+ __mock_proxy.message_received :#{sym}, *args, &block
+ end
+ #{visibility_string}
+ EOF
end
-
- target_metaclass.class_eval(<<-EOF, __FILE__, __LINE__)
- def #{sym}(*args, &block)
- __mock_proxy.message_received :#{sym}, *args, &block
- end
- #{visibility_string}
- EOF
end
def target_responds_to?(sym)
- return @target.send(munge(:respond_to?),sym) if @already_proxied_respond_to
+ return @target.__send__(munge(:respond_to?),sym) if @already_proxied_respond_to
return @already_proxied_respond_to = true if sym == :respond_to?
- return @target.respond_to?(sym)
+ return @target.respond_to?(sym, true)
end
def visibility(sym)
@@ -128,7 +181,7 @@ module Spec
end
def munge(sym)
- "proxied_by_rspec__#{sym.to_s}".to_sym
+ "proxied_by_rspec__#{sym}"
end
def clear_expectations
@@ -155,19 +208,31 @@ module Spec
def reset_proxied_methods
@proxied_methods.each do |sym|
- munged_sym = munge(sym)
- target_metaclass.instance_eval do
- if method_defined?(munged_sym.to_s)
- alias_method sym, munged_sym
- undef_method munged_sym
- else
- undef_method sym
- end
+ reset_proxied_method(sym)
+ end
+ end
+
+ def reset_proxied_method(sym)
+ munged_sym = munge(sym)
+ target_metaclass.instance_eval do
+ remove_method sym
+ if method_defined?(munged_sym)
+ alias_method sym, munged_sym
+ remove_method munged_sym
end
end
end
+
+ def proxy_for_nil_class?
+ @target.nil?
+ end
+
+ def reset_nil_expectations_warning
+ @@warn_about_expectations_on_nil = true if proxy_for_nil_class?
+ end
def find_matching_expectation(sym, *args)
+ @expectations.find {|expectation| expectation.matches(sym, args) && !expectation.called_max_times?} ||
@expectations.find {|expectation| expectation.matches(sym, args)}
end
@@ -175,10 +240,6 @@ module Spec
@expectations.find {|expectation| expectation.matches_name_but_not_args(sym, args)}
end
- def find_matching_method_stub(sym, *args)
- @stubs.find {|stub| stub.matches(sym, args)}
- end
-
end
end
end
diff --git a/vendor/plugins/rspec/lib/spec/mocks/spec_methods.rb b/vendor/plugins/rspec/lib/spec/mocks/spec_methods.rb
deleted file mode 100644
index d92a4cedd..000000000
--- a/vendor/plugins/rspec/lib/spec/mocks/spec_methods.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-module Spec
- module Mocks
- module ExampleMethods
- include Spec::Mocks::ArgumentConstraintMatchers
-
- # Shortcut for creating an instance of Spec::Mocks::Mock.
- #
- # +name+ is used for failure reporting, so you should use the
- # role that the mock is playing in the example.
- #
- # +stubs_and_options+ lets you assign options and stub values
- # at the same time. The only option available is :null_object.
- # Anything else is treated as a stub value.
- #
- # == Examples
- #
- # stub_thing = mock("thing", :a => "A")
- # stub_thing.a == "A" => true
- #
- # stub_person = stub("thing", :name => "Joe", :email => "joe@domain.com")
- # stub_person.name => "Joe"
- # stub_person.email => "joe@domain.com"
- def mock(name, stubs_and_options={})
- Spec::Mocks::Mock.new(name, stubs_and_options)
- end
-
- alias :stub :mock
-
- # Shortcut for creating a mock object that will return itself in response
- # to any message it receives that it hasn't been explicitly instructed
- # to respond to.
- def stub_everything(name = 'stub')
- mock(name, :null_object => true)
- end
-
- end
- end
-end