require 'rspec/mocks' module RSpec module Mocks Space.class_eval do undef print_out_of_example_deprecation def print_out_of_example_deprecation # The simple "are you outside of an example" detection in rspec-mocks # does not properly handle our dog fooding here in rspec core, where we define # example groups and examples from within an example and run them. # As a result, we get `SystemStackError` in a few specs that are mocking # `RSpec.deprecate` and creating nested examples, but we don't care about # this warning here in rspec-core. # # This redefintion silences it. end end end module Core # Because rspec-core dog-foods itself, rspec-core's spec suite has # examples that define example groups and examples and run them. The # usual lifetime of an RSpec::Mocks::Proxy is for one example # (the proxy cache gets cleared between each example), but since the # specs in rspec-core's suite sometimes create test doubles and pass # them to examples a spec defines and runs, the test double's proxy # must live beyond the inner example: it must live for the scope # of wherever it got defined. Here we implement the necessary semantics # for rspec-core's specs: # # - #verify_all and #reset_all affect only mocks that were created # within the current scope. # - Mock proxies live for the duration of the scope in which they are # created. # # Thus, mock proxies created in an inner example live for only that # example, but mock proxies created in an outer example can be used # in an inner example but will only be reset/verified when the outer # example completes. class SandboxedMockSpace < ::RSpec::Mocks::Space def self.sandboxed orig_space = RSpec::Mocks.space RSpec::Mocks.space = RSpec::Core::SandboxedMockSpace.new RSpec::Core::Example.class_eval do alias_method :orig_run, :run def run(*args) RSpec::Mocks.space.sandboxed do orig_run(*args) end end end yield ensure RSpec::Core::Example.class_eval do remove_method :run alias_method :run, :orig_run remove_method :orig_run end RSpec::Mocks.space = orig_space end class Sandbox attr_reader :proxies def initialize @proxies = Set.new end def verify_all @proxies.each { |p| p.verify } end def reset_all @proxies.each { |p| p.reset } end end def initialize @sandbox_stack = [] super end def sandboxed @sandbox_stack << Sandbox.new yield ensure @sandbox_stack.pop end def verify_all return super unless sandbox = @sandbox_stack.last sandbox.verify_all end def reset_all return super unless sandbox = @sandbox_stack.last sandbox.reset_all end def proxy_for(object) new_proxy = !proxies.has_key?(object.__id__) proxy = super if new_proxy && sandbox = @sandbox_stack.last sandbox.proxies << proxy end proxy end end end end