diff options
author | Francis Irving <francis@mysociety.org> | 2009-12-02 17:56:17 +0000 |
---|---|---|
committer | Francis Irving <francis@mysociety.org> | 2009-12-02 17:56:17 +0000 |
commit | 1bbb3f950f8bee92bf8a39e6f18c3278f1bab11d (patch) | |
tree | 5d0312d5d00446cd1454a5e1c799cd89d57b10d6 /vendor/plugins/rspec/lib/spec/mocks | |
parent | 5f3139b538d1ff58b719a72d7c7cf05a5b6136b5 (diff) |
Changing rspec / rspec_on_rails version
Diffstat (limited to 'vendor/plugins/rspec/lib/spec/mocks')
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/argument_constraint_matchers.rb | 31 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/argument_expectation.rb | 210 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/error_generator.rb | 28 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/errors.rb | 2 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/framework.rb | 4 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/message_expectation.rb | 106 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/methods.rb | 56 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/mock.rb | 61 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/proxy.rb | 139 | ||||
-rw-r--r-- | vendor/plugins/rspec/lib/spec/mocks/spec_methods.rb | 38 |
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 |