bullet
bullet copied to clipboard
Wrong `Eager Loading Detected` if use `before_all` or `let_it_be`
Hi, zm
recently I start refactoring our test, move large object creation from let
to let_it_be, which will change object creation from before_each
hook into before_all
hook, then I found some tests failed due to eager loading detected
, like below
RSpec.describe Post, type: :model do
# post has_many comments
# comment belongs_to post
# creator belongs_to comment
let_it_be(:post) { create(:post) }
let_it_be(:comment) { create(:comment, post: post)}
# before(:all) do # same as let_it_be
# puts "start before all"
# @post = create(:post)
# @comment = create(:comment, post: @post)
# puts "end before all"
# end
it 'ok' do
creator = create(:creator, comment_id: comment.id)
comment.post # ok
end
it 'raise N+1 query issue' do
creator = create(:creator, comment: comment)
comment.post # will raise eager loading Comment => [:post]
end
end
which will raise error USE eager loading detected, Comment => [:post], Add to your query: .includes([:post])
, reproducable repo is here. I don't think the detection is correct :(
I read the source code of bullet and found the issue is the object created in before_all
hook didn't add into Bullet::Detector::NPlusOneQuery::impossible_objects
, because Bullet.start_request is invoked in before_each
hook.
# n_plus_one_query.rb
def add_impossible_object(object)
return unless Bullet.start? # will return when object creation in `before_all` hook
return unless Bullet.n_plus_one_query_enable?
return unless object.bullet_primary_key_value
Bullet.debug('Detector::NPlusOneQuery#add_impossible_object', "object: #{object.bullet_key}")
impossible_objects.add object.bullet_key
end
# spec_helper.rb, will start Bullet in before_each hook.
if Bullet.enable?
RSpec.configure do |config|
config.before do
Bullet.start_request
end
config.after do
Bullet.perform_out_of_channel_notifications if Bullet.notification?
Bullet.end_request
end
end
end
so my questions are:
- is there a way to avoid
eager loading detected
error when I usebefore_all
orlet_it_be
? I really want to move some large object creation out of each test, which will significantly improve performance - no matter the problem is solved or not, I hope the documentation can cover this scenario, it may help other people on it :)