require 'rspec/support/spec/prevent_load_time_warnings' RSpec.describe RSpec::Mocks do it_behaves_like 'a library that issues no warnings when loaded', 'rspec-mocks', 'require "rspec/mocks"', # Must be required before other files due to how our autoloads are setup. # (Users won't hit this problem because they won't require all the files # individually in whatever order the file system returns) 'require "rspec/mocks/any_instance"' describe ".verify" do it "delegates to the space" do foo = double expect(foo).to receive(:bar) expect do RSpec::Mocks.verify end.to raise_error(RSpec::Mocks::MockExpectationError) RSpec::Mocks.teardown # so the mocks aren't re-verified after this example end end describe ".teardown" do it "resets method stubs" do string = "foo" allow(string).to receive(:bar) RSpec::Mocks.teardown expect { string.bar }.to raise_error(NoMethodError) end it "does not put rspec-mocks into an inconsistent state when called extra times" do RSpec::Mocks.teardown RSpec::Mocks.teardown RSpec::Mocks.teardown string = "foo" expect { allow(string).to receive(:bar) }.to raise_error(RSpec::Mocks::OutsideOfExampleError) RSpec::Mocks.setup allow(string).to receive(:bar).and_return(:baz) expect(string.bar).to eq(:baz) end end describe ".setup" do it 'starts a new space scope that is later removed by .teardown' do old_space = RSpec::Mocks.space RSpec::Mocks.setup new_space = RSpec::Mocks.space expect(new_space).not_to equal(old_space) RSpec::Mocks.teardown expect(RSpec::Mocks.space).to equal(old_space) end end describe ".configuration" do it 'returns a memoized configuration instance' do expect(RSpec::Mocks.configuration).to be_a(RSpec::Mocks::Configuration) expect(RSpec::Mocks.configuration).to be(RSpec::Mocks.configuration) end end describe ".with_temporary_scope" do context "in a before(:all) with a stubbed double" do before(:all) do RSpec::Mocks.with_temporary_scope do @calculator = double allow(@calculator).to receive(:add) { |a, b| a + b } @sum = @calculator.add(3, 4) end capture_error { @calculator.add(1, 10) } end it 'allows the stubbed double to be used' do expect(@sum).to eq(7) end it 'does not allow the double to be used in the examples' do expect { @calculator.add(1, 2) }.to raise_error(RSpec::Mocks::ExpiredTestDoubleError) end it 'does not allow the double to be used after the scope in before(:all)' do expect(@error).to be_a(RSpec::Mocks::OutsideOfExampleError) end end context "in a before(:all) with a stubbed const" do before(:all) do RSpec::Mocks.with_temporary_scope do stub_const("ValueX", 3) stub_const("ValueY", 4) @sum = ValueX + ValueY end capture_error { ValueX + ValueY } end it 'allows the stubbed constants to be used' do expect(@sum).to eq(7) end it 'does not allow the stubbed constants to be used in the examples' do expect(defined?(ValueX)).to be_falsey expect(defined?(ValueY)).to be_falsey end it 'does not allow the stubbed constants to be used after the scope in before(:all)' do expect(@error).to be_a(NameError) expect(@error.message).to include("ValueX") end end context "in a before(:all) with a unmet mock expectation" do before(:all) do capture_error do RSpec::Mocks.with_temporary_scope do calculator = double expect(calculator).to receive(:add) end end end it 'fails with a mock expectation error' do expect(@error).to be_a(RSpec::Mocks::MockExpectationError) end end context "in a before(:all) with an any_instance stub" do before(:all) do RSpec::Mocks.with_temporary_scope do allow_any_instance_of(String).to receive(:sum_with) { |val, x| val + x } @sum = "foo".sum_with("bar") end capture_error { "you".sum_with("me") } end it 'allows the stub to be used' do expect(@sum).to eq("foobar") end it 'does not allow the double to be used in the examples' do expect { "foo".sum_with("baz") }.to raise_error(NameError, /sum_with/) end it 'does not allow the double to be used after the scope in before(:all)' do expect(@error).to be_a(NameError) expect(@error.message).to include("sum_with") end end it 'tears down even if an error occurs' do foo = Object.new expect { RSpec::Mocks.with_temporary_scope do allow(foo).to receive(:bar) raise "boom" end }.to raise_error("boom") expect(foo).not_to respond_to(:bar) end it 'does not verify if an error occurs before the block completes' do expect { RSpec::Mocks.with_temporary_scope do foo = Object.new expect(foo).to receive(:bar) raise "boom" end }.to raise_error("boom") # rather than MockExpectationError end def capture_error yield rescue Exception => @error end end context "when there is a `let` declaration that overrides an argument matcher" do let(:boolean) { :from_let } before do expect(RSpec::Mocks::ArgumentMatchers.method_defined?(:boolean)).to be true end it 'allows the `let` definition to win' do expect(boolean).to eq(:from_let) end end end