/lib/rr/double_definitions/double_definition.rb
http://github.com/btakita/rr · Ruby · 367 lines · 282 code · 16 blank · 69 comment · 0 complexity · 15d61f06bff463e2f6cfa5fbe8c464ba MD5 · raw file
- module RR
- module DoubleDefinitions
- class DoubleDefinition #:nodoc:
- ORIGINAL_METHOD = Object.new
- attr_accessor(
- :argument_expectation,
- :times_matcher,
- :implementation,
- :after_call_proc,
- :yields_value,
- :double,
- :double_definition_create
- )
- include Space::Reader
- def initialize(double_definition_create)
- @implementation = nil
- @argument_expectation = nil
- @times_matcher = nil
- @after_call_proc = nil
- @yields_value = nil
- @double_definition_create = double_definition_create
- @ordered = false
- @verbose = false
- @verify_method_signature = false
- end
- def subject
- double_definition_create.subject
- end
- def root_subject
- double_definition_create.root_subject
- end
- module ArgumentDefinitionConstructionMethods
- # Double#with sets the expectation that the Double will receive
- # the passed in arguments.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.with(1, 2) {:return_value}
- def with(*args, &return_value_block)
- @argument_expectation = Expectations::ArgumentEqualityExpectation.new(*args)
- install_method_callback return_value_block
- self
- end
- # Double#with_any_args sets the expectation that the Double can receive
- # any arguments.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.with_any_args {:return_value}
- def with_any_args(&return_value_block)
- @argument_expectation = Expectations::AnyArgumentExpectation.new
- install_method_callback return_value_block
- self
- end
- # Double#with_no_args sets the expectation that the Double will receive
- # no arguments.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.with_no_args {:return_value}
- def with_no_args(&return_value_block)
- @argument_expectation = Expectations::ArgumentEqualityExpectation.new()
- install_method_callback return_value_block
- self
- end
- end
- include ArgumentDefinitionConstructionMethods
- module TimesDefinitionConstructionMethods
- # Double#never sets the expectation that the Double will never be
- # called.
- #
- # This method does not accept a block because it will never be called.
- #
- # mock(subject).method_name.never
- def never
- @times_matcher = TimesCalledMatchers::NeverMatcher.new
- self
- end
- # Double#once sets the expectation that the Double will be called
- # 1 time.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.once {:return_value}
- def once(&return_value_block)
- @times_matcher = TimesCalledMatchers::IntegerMatcher.new(1)
- install_method_callback return_value_block
- self
- end
- # Double#twice sets the expectation that the Double will be called
- # 2 times.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.twice {:return_value}
- def twice(&return_value_block)
- @times_matcher = TimesCalledMatchers::IntegerMatcher.new(2)
- install_method_callback return_value_block
- self
- end
- # Double#at_least sets the expectation that the Double
- # will be called at least n times.
- # It works by creating a TimesCalledExpectation.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.at_least(4) {:return_value}
- def at_least(number, &return_value_block)
- @times_matcher = TimesCalledMatchers::AtLeastMatcher.new(number)
- install_method_callback return_value_block
- self
- end
- # Double#at_most allows sets the expectation that the Double
- # will be called at most n times.
- # It works by creating a TimesCalledExpectation.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.at_most(4) {:return_value}
- def at_most(number, &return_value_block)
- @times_matcher = TimesCalledMatchers::AtMostMatcher.new(number)
- install_method_callback return_value_block
- self
- end
- # Double#any_number_of_times sets an that the Double will be called
- # any number of times. This effectively removes the times called expectation
- # from the Doublen
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.any_number_of_times
- def any_number_of_times(&return_value_block)
- @times_matcher = TimesCalledMatchers::AnyTimesMatcher.new
- install_method_callback return_value_block
- self
- end
- alias_method :any_times, :any_number_of_times
- # Double#times creates an TimesCalledExpectation of the passed
- # in number.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.times(4) {:return_value}
- def times(matcher_value, &return_value_block)
- @times_matcher = TimesCalledMatchers::TimesCalledMatcher.create(matcher_value)
- install_method_callback return_value_block
- self
- end
- end
- include TimesDefinitionConstructionMethods
- module DefinitionConstructionMethods
- # Double#ordered sets the Double to have an ordered
- # expectation.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.ordered {return_value}
- def ordered(&return_value_block)
- raise(
- Errors::DoubleDefinitionError,
- "Double Definitions must have a dedicated Double to be ordered. " <<
- "For example, using instance_of does not allow ordered to be used. " <<
- "proxy the class's #new method instead."
- ) unless @double
- @ordered = true
- space.register_ordered_double(@double)
- install_method_callback return_value_block
- DoubleDefinitionCreateBlankSlate.new(double_definition_create)
- end
- alias_method :then, :ordered
- # Double#yields sets the Double to invoke a passed in block when
- # the Double is called.
- # An Expection will be raised if no block is passed in when the
- # Double is called.
- #
- # Passing in a block sets the return value.
- #
- # mock(subject).method_name.yields(yield_arg1, yield_arg2) {return_value}
- # subject.method_name {|yield_arg1, yield_arg2|}
- def yields(*args, &return_value_block)
- @yields_value = args
- install_method_callback return_value_block
- self
- end
- # Double#after_call creates a callback that occurs after call
- # is called. The passed in block receives the return value of
- # the Double being called.
- # An Expection will be raised if no block is passed in.
- #
- # mock(subject).method_name {return_value}.after_call {|return_value|}
- # subject.method_name # return_value
- #
- # This feature is built into proxies.
- # mock.proxy(User).find('1') {|user| mock(user).valid? {false}}
- def after_call(&after_call_proc)
- raise ArgumentError, "after_call expects a block" unless after_call_proc
- @after_call_proc = after_call_proc
- self
- end
- # Double#verbose sets the Double to print out each method call it receives.
- #
- # Passing in a block sets the return value
- def verbose(&after_call_proc)
- @verbose = true
- @after_call_proc = after_call_proc
- self
- end
- # Double#returns accepts an argument value or a block.
- # It will raise an ArgumentError if both are passed in.
- #
- # Passing in a block causes Double to return the return value of
- # the passed in block.
- #
- # Passing in an argument causes Double to return the argument.
- def returns(*args, &implementation)
- if !args.empty? && implementation
- raise ArgumentError, "returns cannot accept both an argument and a block"
- end
- if implementation
- install_method_callback implementation
- else
- install_method_callback(lambda do |*lambda_args|
- args.first
- end)
- end
- self
- end
- def implemented_by_original_method
- implemented_by ORIGINAL_METHOD
- self
- end
- # Double#implemented_by sets the implementation of the Double.
- # This method takes a Proc or a Method. Passing in a Method allows
- # the Double to accept blocks.
- #
- # obj = Object.new
- # def obj.foobar
- # yield(1)
- # end
- # mock(obj).method_name.implemented_by(obj.method(:foobar))
- def implemented_by(implementation)
- @implementation = implementation
- self
- end
- def verify_method_signature
- @verify_method_signature = true
- self
- end
- alias_method :strong, :verify_method_signature
- protected
- def install_method_callback(block)
- if block
- if implementation_is_original_method?
- after_call(&block)
- else
- implemented_by block
- end
- end
- end
- end
- include DefinitionConstructionMethods
- module StateQueryMethods
- # Double#ordered? returns true when the Double is ordered.
- #
- # mock(subject).method_name.ordered?
- def ordered?
- @ordered
- end
- # Double#verbose? returns true when verbose has been called on it. It returns
- # true when the double is set to print each method call it receives.
- def verbose?
- @verbose ? true : false
- end
- def exact_match?(*arguments)
- raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @argument_expectation
- @argument_expectation.exact_match?(*arguments)
- end
- def wildcard_match?(*arguments)
- raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @argument_expectation
- @argument_expectation.wildcard_match?(*arguments)
- end
- def terminal?
- raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @times_matcher
- @times_matcher.terminal?
- end
- def expected_arguments
- argument_expectation ? argument_expectation.expected_arguments : []
- end
- def implementation_is_original_method?
- implementation_strategy.is_a?(Strategies::Implementation::Proxy)
- end
- def verify_method_signature?
- !!@verify_method_signature
- end
- alias_method :strong?, :verify_method_signature?
- protected
- def implementation_strategy
- double_definition_create.implementation_strategy
- end
- end
- include StateQueryMethods
- include ::RR::DoubleDefinitions::Strategies::StrategyMethods
- def mock(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
- ChildDoubleDefinitionCreate.new(self).mock(subject, method_name, &definition_eval_block)
- end
- def stub(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
- ChildDoubleDefinitionCreate.new(self).stub(subject, method_name, &definition_eval_block)
- end
- def dont_allow(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
- ChildDoubleDefinitionCreate.new(self).dont_allow(subject, method_name, &definition_eval_block)
- end
- def proxy(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
- ChildDoubleDefinitionCreate.new(self).proxy(subject, method_name, &definition_eval_block)
- end
- def strong(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
- ChildDoubleDefinitionCreate.new(self).strong(subject, method_name, &definition_eval_block)
- end
- def any_instance_of(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
- ChildDoubleDefinitionCreate.new(self).any_instance_of(subject, method_name, &definition_eval_block)
- end
- def instance_of(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
- ChildDoubleDefinitionCreate.new(self).instance_of(subject, method_name, &definition_eval_block)
- end
- end
- end
- end