RSpec::Support.require_rspec_mocks 'object_reference' module RSpec module Mocks # Contains methods intended to be used from within code examples. # Mix this in to your test context (such as a test framework base class) # to use rspec-mocks with your test framework. If you're using rspec-core, # it'll take care of doing this for you. module ExampleMethods include RSpec::Mocks::ArgumentMatchers # @overload double() # @overload double(name) # @param name [String/Symbol] name or description to be used in failure messages # @overload double(stubs) # @param stubs (Hash) hash of message/return-value pairs # @overload double(name, stubs) # @param name [String/Symbol] name or description to be used in failure messages # @param stubs (Hash) hash of message/return-value pairs # @return (Double) # # Constructs an instance of [RSpec::Mocks::Double](RSpec::Mocks::Double) configured # with an optional name, used for reporting in failure messages, and an optional # hash of message/return-value pairs. # # @example # book = double("book", :title => "The RSpec Book") # book.title #=> "The RSpec Book" # # card = double("card", :suit => "Spades", :rank => "A") # card.suit #=> "Spades" # card.rank #=> "A" # def double(*args) ExampleMethods.declare_double(Double, *args) end # @overload instance_double(doubled_class) # @param doubled_class [String, Class] # @overload instance_double(doubled_class, name) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @overload instance_double(doubled_class, stubs) # @param doubled_class [String, Class] # @param stubs [Hash] hash of message/return-value pairs # @overload instance_double(doubled_class, name, stubs) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return InstanceVerifyingDouble # # Constructs a test double against a specific class. If the given class # name has been loaded, only instance methods defined on the class are # allowed to be stubbed. In all other ways it behaves like a # [double](double). def instance_double(doubled_class, *args) ref = ObjectReference.for(doubled_class) ExampleMethods.declare_verifying_double(InstanceVerifyingDouble, ref, *args) end # @overload class_double(doubled_class) # @param doubled_class [String, Module] # @overload class_double(doubled_class, name) # @param doubled_class [String, Module] # @param name [String/Symbol] name or description to be used in failure messages # @overload class_double(doubled_class, stubs) # @param doubled_class [String, Module] # @param stubs [Hash] hash of message/return-value pairs # @overload class_double(doubled_class, name, stubs) # @param doubled_class [String, Module] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return ClassVerifyingDouble # # Constructs a test double against a specific class. If the given class # name has been loaded, only class methods defined on the class are # allowed to be stubbed. In all other ways it behaves like a # [double](double). def class_double(doubled_class, *args) ref = ObjectReference.for(doubled_class) ExampleMethods.declare_verifying_double(ClassVerifyingDouble, ref, *args) end # @overload object_double(object_or_name) # @param object_or_name [String, Object] # @overload object_double(object_or_name, name) # @param object_or_name [String, Object] # @param name [String/Symbol] name or description to be used in failure messages # @overload object_double(object_or_name, stubs) # @param object_or_name [String, Object] # @param stubs [Hash] hash of message/return-value pairs # @overload object_double(object_or_name, name, stubs) # @param object_or_name [String, Object] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return ObjectVerifyingDouble # # Constructs a test double against a specific object. Only the methods # the object responds to are allowed to be stubbed. If a String argument # is provided, it is assumed to reference a constant object which is used # for verification. In all other ways it behaves like a [double](double). def object_double(object_or_name, *args) ref = ObjectReference.for(object_or_name, :allow_direct_object_refs) ExampleMethods.declare_verifying_double(ObjectVerifyingDouble, ref, *args) end # @overload spy() # @overload spy(name) # @param name [String/Symbol] name or description to be used in failure messages # @overload spy(stubs) # @param stubs (Hash) hash of message/return-value pairs # @overload spy(name, stubs) # @param name [String/Symbol] name or description to be used in failure messages # @param stubs (Hash) hash of message/return-value pairs # @return (Double) # # Constructs a test double that is optimized for use with # `have_received`. With a normal double one has to stub methods in order # to be able to spy them. A spy automatically spies on all methods. def spy(*args) double(*args).as_null_object end # @overload instance_spy(doubled_class) # @param doubled_class [String, Class] # @overload instance_spy(doubled_class, name) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @overload instance_spy(doubled_class, stubs) # @param doubled_class [String, Class] # @param stubs [Hash] hash of message/return-value pairs # @overload instance_spy(doubled_class, name, stubs) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return InstanceVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` # against a specific class. If the given class name has been loaded, only # instance methods defined on the class are allowed to be stubbed. With # a normal double one has to stub methods in order to be able to spy # them. An instance_spy automatically spies on all instance methods to # which the class responds. def instance_spy(*args) instance_double(*args).as_null_object end # @overload object_spy(object_or_name) # @param object_or_name [String, Object] # @overload object_spy(object_or_name, name) # @param object_or_name [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @overload object_spy(object_or_name, stubs) # @param object_or_name [String, Object] # @param stubs [Hash] hash of message/return-value pairs # @overload object_spy(object_or_name, name, stubs) # @param object_or_name [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return ObjectVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` # against a specific object. Only instance methods defined on the object # are allowed to be stubbed. With a normal double one has to stub # methods in order to be able to spy them. An object_spy automatically # spies on all methods to which the object responds. def object_spy(*args) object_double(*args).as_null_object end # @overload class_spy(doubled_class) # @param doubled_class [String, Module] # @overload class_spy(doubled_class, name) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @overload class_spy(doubled_class, stubs) # @param doubled_class [String, Module] # @param stubs [Hash] hash of message/return-value pairs # @overload class_spy(doubled_class, name, stubs) # @param doubled_class [String, Class] # @param name [String/Symbol] name or description to be used in failure messages # @param stubs [Hash] hash of message/return-value pairs # @return ClassVerifyingDouble # # Constructs a test double that is optimized for use with `have_received` # against a specific class. If the given class name has been loaded, # only class methods defined on the class are allowed to be stubbed. # With a normal double one has to stub methods in order to be able to spy # them. An class_spy automatically spies on all class methods to which the # class responds. def class_spy(*args) class_double(*args).as_null_object end # Disables warning messages about expectations being set on nil. # # By default warning messages are issued when expectations are set on # nil. This is to prevent false-positives and to catch potential bugs # early on. def allow_message_expectations_on_nil RSpec::Mocks.space.proxy_for(nil).warn_about_expectations = false end # Stubs the named constant with the given value. # Like method stubs, the constant will be restored # to its original value (or lack of one, if it was # undefined) when the example completes. # # @param constant_name [String] The fully qualified name of the constant. The current # constant scoping at the point of call is not considered. # @param value [Object] The value to make the constant refer to. When the # example completes, the constant will be restored to its prior state. # @param options [Hash] Stubbing options. # @option options :transfer_nested_constants [Boolean, Array] Determines # what nested constants, if any, will be transferred from the original value # of the constant to the new value of the constant. This only works if both # the original and new values are modules (or classes). # @return [Object] the stubbed value of the constant # # @example # stub_const("MyClass", Class.new) # => Replaces (or defines) MyClass with a new class object. # stub_const("SomeModel::PER_PAGE", 5) # => Sets SomeModel::PER_PAGE to 5. # # class CardDeck # SUITS = [:Spades, :Diamonds, :Clubs, :Hearts] # NUM_CARDS = 52 # end # # stub_const("CardDeck", Class.new) # CardDeck::SUITS # => uninitialized constant error # CardDeck::NUM_CARDS # => uninitialized constant error # # stub_const("CardDeck", Class.new, :transfer_nested_constants => true) # CardDeck::SUITS # => our suits array # CardDeck::NUM_CARDS # => 52 # # stub_const("CardDeck", Class.new, :transfer_nested_constants => [:SUITS]) # CardDeck::SUITS # => our suits array # CardDeck::NUM_CARDS # => uninitialized constant error def stub_const(constant_name, value, options={}) ConstantMutator.stub(constant_name, value, options) end # Hides the named constant with the given value. The constant will be # undefined for the duration of the test. # # Like method stubs, the constant will be restored to its original value # when the example completes. # # @param constant_name [String] The fully qualified name of the constant. # The current constant scoping at the point of call is not considered. # # @example # hide_const("MyClass") # => MyClass is now an undefined constant def hide_const(constant_name) ConstantMutator.hide(constant_name) end # Verifies that the given object received the expected message during the # course of the test. On a spy objects or as null object doubles this # works for any method, on other objects the method must have # been stubbed beforehand in order for messages to be verified. # # Stubbing and verifying messages received in this way implements the # Test Spy pattern. # # @param method_name [Symbol] name of the method expected to have been # called. # # @example # invitation = double('invitation', accept: true) # user.accept_invitation(invitation) # expect(invitation).to have_received(:accept) # # # You can also use most message expectations: # expect(invitation).to have_received(:accept).with(mailer).once # # @note `have_received(...).with(...)` is unable to work properly when # passed arguments are mutated after the spy records the received message. def have_received(method_name, &block) Matchers::HaveReceived.new(method_name, &block) end # @method expect # Used to wrap an object in preparation for setting a mock expectation # on it. # # @example # expect(obj).to receive(:foo).with(5).and_return(:return_value) # # @note This method is usually provided by rspec-expectations. However, # if you use rspec-mocks without rspec-expectations, there's a definition # of it that is made available here. If you disable the `:expect` syntax # this method will be undefined. # @method allow # Used to wrap an object in preparation for stubbing a method # on it. # # @example # allow(dbl).to receive(:foo).with(5).and_return(:return_value) # # @note If you disable the `:expect` syntax this method will be undefined. # @method expect_any_instance_of # Used to wrap a class in preparation for setting a mock expectation # on instances of it. # # @example # expect_any_instance_of(MyClass).to receive(:foo) # # @note If you disable the `:expect` syntax this method will be undefined. # @method allow_any_instance_of # Used to wrap a class in preparation for stubbing a method # on instances of it. # # @example # allow_any_instance_of(MyClass).to receive(:foo) # # @note This is only available when you have enabled the `expect` syntax. # @method receive # Used to specify a message that you expect or allow an object # to receive. The object returned by `receive` supports the same # fluent interface that `should_receive` and `stub` have always # supported, allowing you to constrain the arguments or number of # times, and configure how the object should respond to the message. # # @example # expect(obj).to receive(:hello).with("world").exactly(3).times # # @note If you disable the `:expect` syntax this method will be undefined. # @method receive_messages # Shorthand syntax used to setup message(s), and their return value(s), # that you expect or allow an object to receive. The method takes a hash # of messages and their respective return values. Unlike with `receive`, # you cannot apply further customizations using a block or the fluent # interface. # # @example # allow(obj).to receive_messages(:speak => "Hello World") # allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow") # # @note If you disable the `:expect` syntax this method will be undefined. # @method receive_message_chain # @overload receive_message_chain(method1, method2) # @overload receive_message_chain("method1.method2") # @overload receive_message_chain(method1, method_to_value_hash) # # stubs/mocks a chain of messages on an object or test double. # # ## 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 `receive_message_chain` a code smell. Even though not all code smells # indicate real problems (think fluent interfaces), `receive_message_chain` still # results in brittle examples. For example, if you write # `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the # implementation calls `foo.baz.bar`, the stub will not work. # # @example # allow(double).to receive_message_chain("foo.bar") { :baz } # allow(double).to receive_message_chain(:foo, :bar => :baz) # allow(double).to receive_message_chain(:foo, :bar) { :baz } # # # Given any of ^^ these three forms ^^: # double.foo.bar # => :baz # # # Common use in Rails/ActiveRecord: # allow(Article).to receive_message_chain("recent.published") { [Article.new] } # # @note If you disable the `:expect` syntax this method will be undefined. # @private def self.included(klass) klass.class_exec do # This gets mixed in so that if `RSpec::Matchers` is included in # `klass` later, it's definition of `expect` will take precedence. include ExpectHost unless method_defined?(:expect) end end # @private def self.extended(object) # This gets extended in so that if `RSpec::Matchers` is included in # `klass` later, it's definition of `expect` will take precedence. object.extend ExpectHost unless object.respond_to?(:expect) end # @private def self.declare_verifying_double(type, ref, *args) if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? raise VerifyingDoubleNotDefinedError, "#{ref.description.inspect} is not a defined constant. " \ "Perhaps you misspelt it? " \ "Disable check with `verify_doubled_constant_names` configuration option." end RSpec::Mocks.configuration.verifying_double_declaration_callbacks.each do |block| block.call(ref) end declare_double(type, ref, *args) end # @private def self.declare_double(type, *args) args << {} unless Hash === args.last type.new(*args) end # This module exists to host the `expect` method for cases where # rspec-mocks is used w/o rspec-expectations. module ExpectHost end end end end