tl;dr
While it doesn't appear to be documented very well, following on from @PJSCopeland's comment, it appears you can use RSpec::Mocks.space.registered?
to check if a 'mock proxy' exists:
# eg. allow(Rails.configuration.action_controller).to receive(:allow_forgery_protection).and_return(true)
RSpec::Mocks.space.registered?(Rails.configuration.action_controller)
# => true
RSpec::Mocks.space.registered?(Rails.configuration)
# => false
Going Deeper
In a pry debug session we can see:
ls RSpec::Mocks
# => RSpec::Mocks.methods: allow_message configuration error_generator expect_message setup space teardown verify with_temporary_scope
ls RSpec::Mocks.space
# => RSpec::Mocks::Space#methods: any_instance_mutex any_instance_proxy_for any_instance_recorder_for any_instance_recorders any_instance_recorders_from_ancestry_of constant_mutator_for ensure_registered new_scope proxies proxies_of proxy_for proxy_mutex register_constant_mutator registered? reset_all superclass_proxy_for verify_all
Looking at https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/space.rb#L127-L129 we can see that registered?
checks proxies.key?
, where proxies
is an attr on the RSpec::Space
class:
module RSpec
module Mocks
# ..snip..
class Space
attr_reader :proxies, :any_instance_recorders, :proxy_mutex, :any_instance_mutex
# ..snip..
def registered?(object)
proxies.key?(id_for object)
end
# ..snip..
end
end
end
In pry, we can see that proxies
is a Hash
that maps an id number (created by id_for
) to the RSpec::Mocks::Proxy
instance representing that mock:
RSpec::Mocks.space.proxies.class
# => Hash
RSpec::Mocks.space.proxies.map { |k,v| v.class }
# => [RSpec::Mocks::PartialClassDoubleProxy, RSpec::Mocks::PartialDoubleProxy]
As you can see from the available methods, this would give us pretty deep access to play around with the mocks if we wanted to:
ls RSpec::Mocks.space.proxies.map { |k,v| v }.first
# RSpec::Mocks::Proxy#methods: add_message_expectation add_stub as_null_object build_expectation check_for_unexpected_arguments ensure_implemented has_negative_expectation? messages_arg_list method_double_if_exists_for_message null_object? object prepended_modules_of_singleton_class raise_missing_default_stub_error raise_unexpected_message_error received_message? record_message_received remove_stub remove_stub_if_present replay_received_message_on verify
# RSpec::Mocks::PartialDoubleProxy#methods: add_simple_expectation add_simple_stub message_received reset visibility_for
# RSpec::Mocks::PartialClassDoubleProxyMethods#methods: original_method_handle_for
Example: Resetting a mocked value
For example, if we wanted to reset
this mock back to it's unmocked default value, we can use the RSpec::Mocks.space.proxy_for
helper to find our mock, then reset
it:
# when
# Rails.configuration.action_controller.allow_forgery_protection == false
# and
# allow(Rails.configuration.action_controller).to receive(:allow_forgery_protection).and_return(true)
RSpec::Mocks.space.registered?(Rails.configuration.action_controller)
# => true
Rails.configuration.action_controller.allow_forgery_protection
# => true
RSpec::Mocks.space.proxy_for(Rails.configuration.action_controller).reset
Rails.configuration.action_controller.allow_forgery_protection
# => false
Notice however that the even though the mock value has been reset, the mock remains registered?
:
RSpec::Mocks.space.registered?(Rails.configuration.action_controller)
# => true