Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

rdoc - mostly hiding noise end-users don't care about

  • Loading branch information...
commit 8ddbed720afc07cb1b9fe0a9661427b2c9d59cdf 1 parent 45b6314
@dchelimsky dchelimsky authored
View
18 lib/rspec/mocks/any_instance.rb
@@ -5,13 +5,23 @@
module RSpec
module Mocks
module AnyInstance
- # Use this to set stubs and expectations on any instance
- # of a given class.
+ # Used to set stubs and message expectations on any instance of a given
+ # class. Returns a [Recorder](Recorder), which records messages like
+ # `stub` and `should_receive` for later playback on instances of the
+ # class.
#
# @example
#
- # Thing.any_instance.should_receive(:go)
- # Thing.new.go
+ # Car.any_instance.should_receive(:go)
+ # race = Race.new
+ # race.cars << Car.new
+ # race.go # assuming this delegates to all of its cars
+ # # this example would pass
+ #
+ # Account.any_instance.stub(:balance) { Money.new(:USD, 25) }
+ # Account.new.balance # => Money.new(:USD, 25))
+ #
+ # @return [Recorder]
def any_instance
RSpec::Mocks::space.add(self)
__recorder
View
43 lib/rspec/mocks/any_instance/chain.rb
@@ -73,21 +73,16 @@ def record(rspec_method_name, *args, &block)
end
end
+ # @private
class ExpectationChain < Chain
+
+ # @private
def initialize(*args, &block)
record(:should_receive, *args, &block)
@expectation_fulfilled = false
end
- def invocation_order
- @invocation_order ||= {
- :should_receive => [nil],
- :with => [:should_receive],
- :and_return => [:with, :should_receive],
- :and_raise => [:with, :should_receive]
- }
- end
-
+ # @private
def expectation_fulfilled?
@expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
end
@@ -96,13 +91,32 @@ def expectation_fulfilled?
def verify_invocation_order(rspec_method_name, *args, &block)
end
+
+ def invocation_order
+ @invocation_order ||= {
+ :should_receive => [nil],
+ :with => [:should_receive],
+ :and_return => [:with, :should_receive],
+ :and_raise => [:with, :should_receive]
+ }
+ end
end
+ # @private
class StubChain < Chain
+
+ # @private
def initialize(*args, &block)
record(:stub, *args, &block)
end
+ # @private
+ def expectation_fulfilled?
+ true
+ end
+
+ private
+
def invocation_order
@invocation_order ||= {
:stub => [nil],
@@ -113,12 +127,6 @@ def invocation_order
}
end
- def expectation_fulfilled?
- true
- end
-
- private
-
def verify_invocation_order(rspec_method_name, *args, &block)
unless invocation_order[rspec_method_name].include?(last_message)
raise(NoMethodError, "Undefined method #{rspec_method_name}")
@@ -126,11 +134,16 @@ def verify_invocation_order(rspec_method_name, *args, &block)
end
end
+ # @private
class StubChainChain < StubChain
+
+ # @private
def initialize(*args, &block)
record(:stub_chain, *args, &block)
end
+ private
+
def invocation_order
@invocation_order ||= {
:stub_chain => [nil],
View
16 lib/rspec/mocks/any_instance/message_chains.rb
@@ -1,46 +1,46 @@
module RSpec
module Mocks
module AnyInstance
- # @api private
+ # @private
class MessageChains < Hash
def initialize
super {|h,k| h[k] = []}
end
- # @api private
+ # @private
def add(method_name, chain)
self[method_name] << chain
chain
end
- # @api private
+ # @private
def remove_stub_chains_for!(method_name)
self[method_name].reject! {|chain| chain.is_a?(StubChain)}
end
- # @api private
+ # @private
def has_expectation?(method_name)
self[method_name].find {|chain| chain.is_a?(ExpectationChain)}
end
- # @api private
+ # @private
def all_expectations_fulfilled?
all? {|method_name, chains| chains.all? {|chain| chain.expectation_fulfilled?}}
end
- # @api private
+ # @private
def unfulfilled_expectations
map do |method_name, chains|
method_name.to_s if chains.last.is_a?(ExpectationChain) unless chains.last.expectation_fulfilled?
end.compact
end
- # @api private
+ # @private
def received_expected_message!(method_name)
self[method_name].each {|chain| chain.expectation_fulfilled!}
end
- # @api private
+ # @private
def playback!(instance, method_name)
raise_if_second_instance_to_receive_message(instance)
self[method_name].each {|chain| chain.playback!(instance)}
View
11 lib/rspec/mocks/any_instance/recorder.rb
@@ -1,6 +1,14 @@
module RSpec
module Mocks
module AnyInstance
+ # Given a class `TheClass`, `TheClass.any_instance` returns a `Recorder`,
+ # which records stubs and message expectations for later playback on
+ # instances of `TheClass`.
+ #
+ # Further constraints are stored in instances of [Chain](Chain).
+ #
+ # @see AnyInstance
+ # @see Chain
class Recorder
# @private
attr_reader :message_chains
@@ -29,7 +37,8 @@ def stub(method_name_or_method_map, &block)
end
# Initializes the recording a stub chain to be played back against any
- # instance of this object that invokes the submitted method.
+ # instance of this object that invokes the method matching the first
+ # argument.
#
# @see Methods#stub_chain
def stub_chain(*args, &block)
View
40 lib/rspec/mocks/error_generator.rb
@@ -1,55 +1,65 @@
module RSpec
module Mocks
+ # @private
class ErrorGenerator
attr_writer :opts
-
+
def initialize(target, name, options={})
@declared_as = options[:__declared_as] || 'Mock'
@target = target
@name = name
end
-
+
+ # @private
def opts
@opts ||= {}
end
+ # @private
def raise_unexpected_message_error(sym, *args)
__raise "#{intro} received unexpected message :#{sym}#{arg_message(*args)}"
end
-
+
+ # @private
def raise_unexpected_message_args_error(expectation, *args)
expected_args = format_args(*expectation.expected_args)
actual_args = format_args(*args)
__raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
end
-
+
+ # @private
def raise_similar_message_args_error(expectation, *args)
expected_args = format_args(*expectation.expected_args)
actual_args = args.collect {|a| format_args(*a)}.join(", ")
__raise "#{intro} received #{expectation.sym.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
end
-
+
+ # @private
def raise_expectation_error(sym, expected_received_count, actual_received_count, *args)
__raise "(#{intro}).#{sym}#{format_args(*args)}\n expected: #{count_message(expected_received_count)}\n received: #{count_message(actual_received_count)}"
end
-
+
+ # @private
def raise_out_of_order_error(sym)
__raise "#{intro} received :#{sym} out of order"
end
-
+
+ # @private
def raise_block_failed_error(sym, detail)
__raise "#{intro} received :#{sym} but passed block failed with: #{detail}"
end
-
+
+ # @private
def raise_missing_block_error(args_to_yield)
__raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed"
end
-
+
+ # @private
def raise_wrong_arity_error(args_to_yield, arity)
__raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with arity of #{arity}"
end
-
- private
+
+ private
def intro
if @name
@@ -64,16 +74,16 @@ def intro
"nil"
end
end
-
+
def __raise(message)
message = opts[:message] unless opts[:message].nil?
Kernel::raise(RSpec::Mocks::MockExpectationError, message)
end
-
+
def arg_message(*args)
" with " + format_args(*args)
end
-
+
def format_args(*args)
args.empty? ? "(no args)" : "(" + arg_list(*args) + ")"
end
@@ -81,7 +91,7 @@ def format_args(*args)
def arg_list(*args)
args.collect {|arg| arg.respond_to?(:description) ? arg.description : arg.inspect}.join(", ")
end
-
+
def count_message(count)
return "at least #{pretty_print(count.abs)}" if count < 0
return pretty_print(count)
View
3  lib/rspec/mocks/message_expectation.rb
@@ -479,11 +479,14 @@ def clear_actual_received_count!
end
end
+ # @private
class NegativeMessageExpectation < MessageExpectation
+ # @private
def initialize(message, expectation_ordering, expected_from, sym, method_block)
super(message, expectation_ordering, expected_from, sym, method_block, 0)
end
+ # @private
def negative_expectation_for?(sym)
return @sym == sym
end
View
39 lib/rspec/mocks/method_double.rb
@@ -1,8 +1,11 @@
module RSpec
module Mocks
+ # @private
class MethodDouble < Hash
+ # @private
attr_reader :method_name
+ # @private
def initialize(object, method_name, proxy)
@method_name = method_name
@object = object
@@ -12,14 +15,17 @@ def initialize(object, method_name, proxy)
store(:stubs, [])
end
+ # @private
def expectations
self[:expectations]
end
+ # @private
def stubs
self[:stubs]
end
+ # @private
def visibility
if Mock === @object
'public'
@@ -32,18 +38,22 @@ def visibility
end
end
+ # @private
def object_singleton_class
class << @object; self; end
end
+ # @private
def obfuscate(method_name)
"obfuscated_by_rspec_mocks__#{method_name}"
end
+ # @private
def stashed_method_name
obfuscate(method_name)
end
+ # @private
def object_responds_to?(method_name)
if @proxy.already_proxied_respond_to?
@object.__send__(obfuscate(:respond_to?), method_name)
@@ -54,6 +64,7 @@ def object_responds_to?(method_name)
end
end
+ # @private
def configure_method
RSpec::Mocks::space.add(@object) if RSpec::Mocks::space
warn_if_nil_class
@@ -63,6 +74,7 @@ def configure_method
end
end
+ # @private
def stash_original_method
stashed = stashed_method_name
orig = @method_name
@@ -72,6 +84,7 @@ def stash_original_method
@stashed = true
end
+ # @private
def define_proxy_method
method_name = @method_name
visibility_for_method = "#{visibility} :#{method_name}"
@@ -79,10 +92,11 @@ def define_proxy_method
def #{method_name}(*args, &block)
__mock_proxy.message_received :#{method_name}, *args, &block
end
- #{visibility_for_method}
+ #{visibility_for_method}
EOF
end
+ # @private
def restore_original_method
if @stashed
method_name = @method_name
@@ -98,32 +112,37 @@ def restore_original_method
end
end
+ # @private
def verify
expectations.each {|e| e.verify_messages_received}
end
+ # @private
def reset
reset_nil_expectations_warning
restore_original_method
clear
end
+ # @private
def clear
expectations.clear
stubs.clear
end
+ # @private
def add_expectation(error_generator, expectation_ordering, expected_from, opts, &block)
configure_method
expectation = if existing_stub = stubs.first
- existing_stub.build_child(expected_from, block, 1, opts)
- else
- MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, block, 1, opts)
- end
+ existing_stub.build_child(expected_from, block, 1, opts)
+ else
+ MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, block, 1, opts)
+ end
expectations << expectation
expectation
end
+ # @private
def add_negative_expectation(error_generator, expectation_ordering, expected_from, &implementation)
configure_method
expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, implementation)
@@ -131,32 +150,38 @@ def add_negative_expectation(error_generator, expectation_ordering, expected_fro
expectation
end
+ # @private
def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
configure_method
stub = MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, nil, :any, opts, &implementation)
stubs.unshift stub
stub
end
-
+
+ # @private
def remove_stub
raise_method_not_stubbed_error if stubs.empty?
expectations.empty? ? reset : stubs.clear
end
+ # @private
def proxy_for_nil_class?
@object.nil?
end
+ # @private
def warn_if_nil_class
if proxy_for_nil_class? & RSpec::Mocks::Proxy.warn_about_expectations_on_nil
Kernel.warn("An expectation of :#{@method_name} was set on nil. Called from #{caller[4]}. Use allow_message_expectations_on_nil to disable warnings.")
end
end
-
+
+ # @private
def raise_method_not_stubbed_error
raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
end
+ # @private
def reset_nil_expectations_warning
RSpec::Mocks::Proxy.warn_about_expectations_on_nil = true if proxy_for_nil_class?
end
View
32 lib/rspec/mocks/methods.rb
@@ -4,6 +4,13 @@ module Mocks
module Methods
# Sets and expectation that this object should receive a message before
# the end of the example.
+ #
+ # @example
+ #
+ # logger = double('logger')
+ # thing_that_logs = ThingThatLogs.new(logger)
+ # logger.should_receive(:log)
+ # thing_that_logs.do_something_that_logs_a_message
def should_receive(message, opts={}, &block)
__mock_proxy.add_message_expectation(opts[:expected_from] || caller(1)[0], message.to_sym, opts, &block)
end
@@ -14,7 +21,7 @@ def should_not_receive(message, &block)
__mock_proxy.add_negative_message_expectation(caller(1)[0], message.to_sym, &block)
end
- # Tells the object to respond to the message with a canned value
+ # Tells the object to respond to the message with the specified value.
#
# @example
#
@@ -43,13 +50,32 @@ def unstub(message)
alias_method :stub!, :stub
alias_method :unstub!, :unstub
- # Stubs a chain of methods. Especially useful with fluent and/or
- # composable interfaces.
+ # @overload stub_chain(method1, method2)
+ # @overload stub_chain("method1.method2")
+ # @overload stub_chain(method1, method_to_value_hash)
+ #
+ # Stubs a chain of methods.
+ #
+ # ## Warning:
+ #
+ # Chains can be arbitrarily long, which makes it quite painless to
+ # violate the Law of Demeter in violent ways, so you should consider any
+ # use of `stub_chain` a code smell. Even though not all code smells
+ # indicate real problems (think fluent interfaces), `stub_chain` still
+ # results in brittle examples. For example, if you write
+ # `foo.stub_chain(:bar, :baz => 37)` in a spec and then the
+ # implementation calls `foo.baz.bar`, the stub will not work.
#
# @example
#
# double.stub_chain("foo.bar") { :baz }
+ # double.stub_chain(:foo, :bar => :baz)
# double.stub_chain(:foo, :bar) { :baz }
+ #
+ # # Given any of ^^ these three forms ^^:
+ # double.foo.bar # => :baz
+ #
+ # # Common use in Rails/ActiveRecord:
# Article.stub_chain("recent.published") { [Article.new] }
def stub_chain(*chain, &blk)
chain, blk = format_chain(*chain, &blk)
View
14 lib/rspec/mocks/order_group.rb
@@ -1,24 +1,28 @@
module RSpec
module Mocks
- # @api private
+ # @private
class OrderGroup
def initialize error_generator
@error_generator = error_generator
@ordering = Array.new
end
-
+
+ # @private
def register(expectation)
@ordering << expectation
end
-
+
+ # @private
def ready_for?(expectation)
return @ordering.first == expectation
end
-
+
+ # @private
def consume
@ordering.shift
end
-
+
+ # @private
def handle_order_constraint expectation
return unless @ordering.include? expectation
return consume if ready_for?(expectation)
View
45 lib/rspec/mocks/proxy.rb
@@ -1,44 +1,51 @@
module RSpec
module Mocks
- # @api private
+ # @private
class Proxy
class << self
+ # @private
def warn_about_expectations_on_nil
defined?(@warn_about_expectations_on_nil) ? @warn_about_expectations_on_nil : true
end
-
+
+ # @private
def warn_about_expectations_on_nil=(new_value)
@warn_about_expectations_on_nil = new_value
end
-
+
+ # @private
def 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::space.add(nil) unless RSpec::Mocks::space.nil?
end
+ # @private
def allow_message_expectations_on_nil?
!warn_about_expectations_on_nil
end
end
+ # @private
def initialize(object, name=nil, options={})
@object = object
@name = name
@error_generator = ErrorGenerator.new object, name, options
@expectation_ordering = OrderGroup.new @error_generator
- @messages_received = []
+ @messages_received = []
@options = options
@already_proxied_respond_to = false
@null_object = false
end
+ # @private
def null_object?
@null_object
end
+ # @private
# Tells the object to ignore any messages that aren't explicitly set as
# stubs or message expectations.
def as_null_object
@@ -46,52 +53,64 @@ def as_null_object
@object
end
+ # @private
def already_proxied_respond_to
@already_proxied_respond_to = true
end
+ # @private
def already_proxied_respond_to?
@already_proxied_respond_to
end
+ # @private
def add_message_expectation(location, method_name, opts={}, &block)
method_double[method_name].add_expectation @error_generator, @expectation_ordering, location, opts, &block
end
+ # @private
def add_negative_message_expectation(location, method_name, &implementation)
method_double[method_name].add_negative_expectation @error_generator, @expectation_ordering, location, &implementation
end
+ # @private
def add_stub(location, method_name, opts={}, &implementation)
method_double[method_name].add_stub @error_generator, @expectation_ordering, location, opts, &implementation
end
-
+
+ # @private
def remove_stub(method_name)
method_double[method_name].remove_stub
end
-
+
+ # @private
def verify
method_doubles.each {|d| d.verify}
ensure
reset
end
+ # @private
def reset
method_doubles.each {|d| d.reset}
end
+ # @private
def received_message?(method_name, *args, &block)
@messages_received.any? {|array| array == [method_name, args, block]}
end
+ # @private
def has_negative_expectation?(method_name)
method_double[method_name].expectations.detect {|expectation| expectation.negative_expectation_for?(method_name)}
end
-
+
+ # @private
def record_message_received(method_name, *args, &block)
@messages_received << [method_name, args, block]
end
+ # @private
def message_received(method_name, *args, &block)
expectation = find_matching_expectation(method_name, *args)
stub = find_matching_method_stub(method_name, *args)
@@ -117,15 +136,17 @@ def message_received(method_name, *args, &block)
end
end
+ # @private
def raise_unexpected_message_args_error(expectation, *args)
@error_generator.raise_unexpected_message_args_error(expectation, *args)
end
+ # @private
def raise_unexpected_message_error(method_name, *args)
@error_generator.raise_unexpected_message_error method_name, *args
end
-
- private
+
+ private
def method_double
@method_double ||= Hash.new {|h,k|
@@ -136,10 +157,10 @@ def method_double
def method_doubles
method_double.values
end
-
+
def find_matching_expectation(method_name, *args)
method_double[method_name].expectations.find {|expectation| expectation.matches?(method_name, *args) && !expectation.called_max_times?} ||
- method_double[method_name].expectations.find {|expectation| expectation.matches?(method_name, *args)}
+ method_double[method_name].expectations.find {|expectation| expectation.matches?(method_name, *args)}
end
def find_almost_matching_expectation(method_name, *args)
View
4 lib/rspec/mocks/serialization.rb
@@ -3,12 +3,16 @@
module RSpec
module Mocks
+ # @private
module Serialization
+ # @private
def self.fix_for(object)
object.extend(YAML) if defined?(::YAML)
end
+ # @private
module YAML
+ # @private
def to_yaml(options = {})
return nil if defined?(::Psych) && options.respond_to?(:[]) && options[:nodump]
return super(options) unless instance_variable_defined?(:@mock_proxy)
View
1  lib/rspec/mocks/space.rb
@@ -1,5 +1,6 @@
module RSpec
module Mocks
+ # @api private
class Space
def add(obj)
mocks << obj unless mocks.detect {|m| m.equal? obj}
Please sign in to comment.
Something went wrong with that request. Please try again.