Reputation: 2622
I'm unable to replicate this locally, but for some reason I am getting the following error when running tests in CircleCi:
<Double Mylogger> was originally created in one example but has leaked into another example and can no longer be used. rspec-mocks' doubles are designed to only last for one example, and you need to create a new one in each example you wish to use it for.
Here is a simplified version of my code:
# frozen_string_literal: true
describe 'my_rake_task' do
let(:my_log) { Mylogger.new }
subject { Rake::Task['my_rake_task'].execute }
describe 'one' do
context 'logs' do
let(:logs) do
[
['My message one'],
['My message two'],
]
end
after { subject }
it 'correctly' do
logs.each { |log| expect(my_log).to receive(:info).with(*log) }
end
end
end
describe 'two' do
context 'logs' do
let(:logs) do
[
['My message three'],
['My message four'],
]
end
after { subject }
it 'correctly' do
logs.each { |log| expect(my_log).to receive(:info).with(*log) }
end
end
end
end
Why is it saying MyLogger is a double? Why would it be leaking?
Upvotes: 2
Views: 960
Reputation: 1317
Mostly commonly this sort of thing is caused by storing something in a class variable. You will have to figure out where that is, and how to clear it or avoid using a class variable - it could be in your class or in a gem.
Best practice, where using a class variable or an external system is causing inter-test issues, is to clean this sort of thing between tests, if possible. For example ActionMailer::Base.deliveries
and Rails.cache
are common things that should be cleared. You should also clear Faker::UniqueGenerator
or RequestStore
if you're using those gems, and I'm sure there are more.
Once you have found the class variable, if it's in your code, and you have determined a class variable is the correct approach, you can add a reset
or clear
class method to the class and call it in a before(:each)
RSpec block in your spec_helper.rb
or rails_helper.rb
.
Note that while a lot of things will clear themselves automatically between tests (such as RSpec mocks), and make you think this is all automatic, in practice it is often anything but.
Tests will only remain independent by (a) mostly making use of objects created in the tests and mostly only storing data in there and (b) ensuring anything else is cleared between tests by your explicit code or within the responsible gem.
This can be especially annoying when dealing with external third-party systems, which rarely provide an API to clear a staging environment, and hence sometimes require considerable care even when using the vcr
gem.
Upvotes: 1
Reputation: 11099
The reason that the error is saying that MyLogger
is a double is because it is one. When you call expect(my_log).to receive
or allow(my_log).to receive
, you transform the instance into a partial-double.
As for why my_log
is leaking: it's impossible to tell from the code that you posted. In order to cause a leak, some code either in your rake task or in the spec itself would need to be injecting my_log
into some global state, like a class variable.
Upvotes: 1