Back to Repositories

Testing Mongoid Association Detection in Bullet

This test suite evaluates the Bullet gem’s association detection capabilities for Mongoid, focusing on various relationship types including embeds_many, has_many, belongs_to, and has_one. It verifies Bullet’s ability to detect eager loading issues and unused preloaded associations in Mongoid models.

Test Coverage Overview

The test suite provides comprehensive coverage of Mongoid association detection scenarios:
  • Embeds_many relationships between posts and users
  • Has_many relationships for posts/comments and categories/posts/entries
  • Belongs_to relationships for comment/post associations
  • Has_one relationships between companies and addresses

Implementation Analysis

The testing approach utilizes RSpec to systematically verify Bullet’s detection capabilities:
  • Tests both preloaded and non-preloaded association scenarios
  • Validates unused preload detection
  • Implements scope-based preloading tests
  • Uses Mongoid-specific includes and eager loading patterns

Technical Details

Key technical components include:
  • RSpec testing framework
  • Bullet::Detector::Association module
  • Bullet::Detector::UnusedEagerLoading checker
  • Mongoid ORM integration
  • Custom matchers for association validation

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Isolated test contexts for different association types
  • Comprehensive edge case coverage
  • Clear test descriptions and expectations
  • Consistent testing patterns across different scenarios
  • Proper setup and teardown of test data

flyerhzm/bullet

spec/integration/mongoid/association_spec.rb

            
# frozen_string_literal: true

require 'spec_helper'

if mongoid?
  describe Bullet::Detector::Association do
    context 'embeds_many' do
      context 'posts => users' do
        it 'should detect nothing' do
          Mongoid::Post.all.each { |post| post.users.map(&:name) }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end
      end
    end

    context 'has_many' do
      context 'posts => comments' do
        it 'should detect non preload posts => comments' do
          Mongoid::Post.all.each { |post| post.comments.map(&:name) }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Post, :comments)
        end

        it 'should detect preload post => comments' do
          Mongoid::Post.includes(:comments).each { |post| post.comments.map(&:name) }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should detect unused preload post => comments' do
          Mongoid::Post.includes(:comments).map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Post, :comments)

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should not detect unused preload post => comments' do
          Mongoid::Post.all.map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end
      end

      context 'category => posts, category => entries' do
        it 'should detect non preload with category => [posts, entries]' do
          Mongoid::Category.all.each do |category|
            category.posts.map(&:name)
            category.entries.map(&:name)
          end
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Category, :posts)
          expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Category, :entries)
        end

        it 'should detect preload with category => posts, but not with category => entries' do
          Mongoid::Category.includes(:posts).each do |category|
            category.posts.map(&:name)
            category.entries.map(&:name)
          end
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).not_to be_detecting_unpreloaded_association_for(
            Mongoid::Category,
            :posts
          )
          expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Category, :entries)
        end

        it 'should detect preload with category => [posts, entries]' do
          Mongoid::Category.includes(:posts, :entries).each do |category|
            category.posts.map(&:name)
            category.entries.map(&:name)
          end
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should detect unused preload with category => [posts, entries]' do
          Mongoid::Category.includes(:posts, :entries).map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Category, :posts)
          expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Category, :entries)

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should detect unused preload with category => entries, but not with category => posts' do
          Mongoid::Category.includes(:posts, :entries).each { |category| category.posts.map(&:name) }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Mongoid::Category, :posts)
          expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Category, :entries)

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end
      end

      context 'post => comment' do
        it 'should detect unused preload with post => comments' do
          Mongoid::Post.includes(:comments).each { |post| post.comments.first.name }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_unused_preload_associations_for(Mongoid::Post, :comments)

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should detect preload with post => comments' do
          Mongoid::Post.first.comments.map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end
      end

      context 'scope preload_comments' do
        it 'should detect preload post => comments with scope' do
          Mongoid::Post.preload_comments.each { |post| post.comments.map(&:name) }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should detect unused preload with scope' do
          Mongoid::Post.preload_comments.map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Post, :comments)

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end
      end
    end

    context 'belongs_to' do
      context 'comment => post' do
        it 'should detect non preload with comment => post' do
          Mongoid::Comment.all.each { |comment| comment.post.name }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(Mongoid::Comment, :post)
        end

        it 'should detect preload with one comment => post' do
          Mongoid::Comment.first.post.name
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should detect preload with comment => post' do
          Mongoid::Comment.includes(:post).each { |comment| comment.post.name }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should not detect preload with comment => post' do
          Mongoid::Comment.all.map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should detect unused preload with comments => post' do
          Mongoid::Comment.includes(:post).map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Comment, :post)

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end
      end
    end

    context 'has_one' do
      context 'company => address' do
        if Mongoid::VERSION !~ /\A3.0/
          it 'should detect non preload association' do
            Mongoid::Company.all.each { |company| company.address.name }
            Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
            expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

            expect(Bullet::Detector::Association).to be_detecting_unpreloaded_association_for(
              Mongoid::Company,
              :address
            )
          end
        end

        it 'should detect preload association' do
          Mongoid::Company.includes(:address).each { |company| company.address.name }
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should not detect preload association' do
          Mongoid::Company.all.map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end

        it 'should detect unused preload association' do
          criteria = Mongoid::Company.includes(:address)
          criteria.map(&:name)
          Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
          expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Mongoid::Company, :address)

          expect(Bullet::Detector::Association).to be_completely_preloading_associations
        end
      end
    end

    context 'call one association that in possible objects' do
      it 'should not detect preload association' do
        Mongoid::Post.all
        Mongoid::Post.first.comments.map(&:name)
        Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
        expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

        expect(Bullet::Detector::Association).to be_completely_preloading_associations
      end
    end
  end
end