Scope
All rspec-mocks constructs have a per-example lifecycle. Message expectations are verified after each example. Doubles, method stubs, stubbed constants, etc. are all cleaned up after each example. This ensures that each example can be run in isolation, and in any order.
It is perfectly fine to set up doubles, stubs, and message expectations in a
before(:example)
hook, as that hook is executed in the scope of the example:
before(:example) do
allow(MyClass).to receive(:foo)
end
Since before(:context)
runs outside the scope of any individual example, usage of
rspec-mocks features is not supported there. You can, however, create a temporary scope in
any arbitrary context, including in a before(:context)
hook, using
RSpec::Mocks.with_temporary_scope { }
.
Cannot create doubles in a before(:context)
hook
Given a file named “beforecontextspec.rb” with:
RSpec.describe "Creating a double in a before(:context) hook" do
before(:context) do
@dbl = double(:foo => 13)
end
it "fails before it gets to the examples" do
expect(@dbl.foo).to eq(13)
end
end
When I run rspec before_context_spec.rb
Then it should fail with:
The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported.
Use with_temporary_scope
to create and use a double in a before(:context)
hook
Given a file named “withtemporaryscope_spec.rb” with:
RSpec.describe "Creating a double in a before(:context) hook" do
before(:context) do
RSpec::Mocks.with_temporary_scope do
dbl = double(:foo => 13)
@result = dbl.foo
end
end
it "allows a double to be created and used from within a with_temporary_scope block" do
expect(@result).to eq(13)
end
end
When I run rspec with_temporary_scope_spec.rb
Then the examples should all pass.
Doubles cannot be reused in another example
Given a file named “leaktestdouble_spec.rb” with:
class Account
class << self
attr_accessor :logger
end
def initialize
@balance = 0
end
attr_reader :balance
def credit(amount)
@balance += amount
self.class.logger.log("Credited $#{amount}")
end
end
RSpec.describe Account do
it "logs each credit" do
Account.logger = logger = double("Logger")
expect(logger).to receive(:log).with("Credited $15")
account = Account.new
account.credit(15)
end
it "keeps track of the balance" do
account = Account.new
expect { account.credit(10) }.to change { account.balance }.by(10)
end
end
When I run rspec leak_test_double_spec.rb
Then it should fail with the following output:
2 examples, 1 failure |
1) Account keeps track of the balance |
Failure/Error: self.class.logger.log(“Credited $#{amount}”) |
# |