Testing Unused Eager Loading Detection in Bullet
This test suite validates the UnusedEagerLoading detector in the Bullet gem, which identifies and reports unnecessary eager loading of associations in Rails applications. The tests ensure proper detection and notification of unused preloaded associations to help optimize database queries.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
flyerhzm/bullet
spec/bullet/detector/unused_eager_loading_spec.rb
# frozen_string_literal: true
require 'spec_helper'
using Bullet::Ext::Object
module Bullet
module Detector
describe UnusedEagerLoading do
before(:all) do
@post = Post.first
@post2 = Post.all[1]
@post3 = Post.last
end
context '.call_associations' do
it 'should get empty array if eager_loadings' do
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
end
it 'should get call associations if object and association are both in eager_loadings and call_object_associations' do
UnusedEagerLoading.add_eager_loadings([@post], :association)
UnusedEagerLoading.add_call_object_associations(@post, :association)
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
[:association]
)
end
it 'should not get call associations if not exist in call_object_associations' do
UnusedEagerLoading.add_eager_loadings([@post], :association)
expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to be_empty
end
end
context '.diff_object_associations' do
it 'should return associations not exist in call_association' do
expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
[:association]
)
end
it 'should return empty if associations exist in call_association' do
UnusedEagerLoading.add_eager_loadings([@post], :association)
UnusedEagerLoading.add_call_object_associations(@post, :association)
expect(
UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
).to be_empty
end
end
context '.check_unused_preload_associations' do
let(:paths) { %w[/dir1 /dir1/subdir] }
it 'should create notification if object_association_diff is not empty' do
UnusedEagerLoading.add_object_associations(@post, :association)
allow(UnusedEagerLoading).to receive(:caller_in_project).and_return(paths)
expect(UnusedEagerLoading).to receive(:create_notification).with(paths, 'Post', [:association])
UnusedEagerLoading.check_unused_preload_associations
end
it 'should not create notification if object_association_diff is empty' do
UnusedEagerLoading.add_object_associations(@post, :association)
UnusedEagerLoading.add_eager_loadings([@post], :association)
UnusedEagerLoading.add_call_object_associations(@post, :association)
expect(
UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))
).to be_empty
expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', [:association])
UnusedEagerLoading.check_unused_preload_associations
end
it 'should create call stack for notification' do
UnusedEagerLoading.add_object_associations(@post, :association)
expect(UnusedEagerLoading.send(:call_stacks).registry).not_to be_empty
end
end
context '.add_eager_loadings' do
it 'should add objects, associations pair when eager_loadings are empty' do
UnusedEagerLoading.add_eager_loadings([@post, @post2], :associations)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
[@post.bullet_key, @post2.bullet_key],
:associations
)
end
it 'should add objects, associations pair for existing eager_loadings' do
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
[@post.bullet_key, @post2.bullet_key],
:association1
)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include(
[@post.bullet_key, @post2.bullet_key],
:association2
)
end
it 'should merge objects, associations pair for existing eager_loadings' do
UnusedEagerLoading.add_eager_loadings([@post], :association1)
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association2)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post2.bullet_key], :association2)
end
it 'should vmerge objects recursively, associations pair for existing eager_loadings' do
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
UnusedEagerLoading.add_eager_loadings([@post, @post3], :association1)
UnusedEagerLoading.add_eager_loadings([@post, @post3], :association2)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post2.bullet_key], :association1)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post3.bullet_key], :association1)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post3.bullet_key], :association2)
end
it 'should delete objects, associations pair for existing eager_loadings' do
UnusedEagerLoading.add_eager_loadings([@post, @post2], :association1)
UnusedEagerLoading.add_eager_loadings([@post], :association2)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association1)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post.bullet_key], :association2)
expect(UnusedEagerLoading.send(:eager_loadings)).to be_include([@post2.bullet_key], :association1)
end
end
end
end
end