Back to Repositories

Testing Bullet Gem Core Operations in flyerhzm/bullet

This test suite validates the core functionality of the Bullet gem, a performance optimization tool for Ruby applications. It comprehensively tests enabling/disabling features, debug output, safelist management, and notification handling.

Test Coverage Overview

The test suite provides extensive coverage of Bullet’s key operational features.

Key areas tested include:
  • Enable/disable functionality with state verification
  • Debug output handling and environment-based configuration
  • Safelist management for special class names
  • Notification system for performance issues
Integration points cover ORM interactions and rack environment handling.

Implementation Analysis

The testing approach uses RSpec’s context-based structure to organize related test cases hierarchically. The implementation leverages RSpec’s expect syntax and mock objects for isolation testing.

Notable patterns include:
  • Before/after hooks for test state management
  • Nested contexts for related test scenarios
  • Mock object usage for external dependencies
  • Environment variable manipulation for debug testing

Technical Details

Testing tools and configuration:
  • RSpec as the testing framework
  • StringIO for stdout capture
  • Environment variable configuration for debug mode
  • Mock objects for notification testing
  • Rack environment simulation for request handling

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices through organized, focused test cases.

Notable practices include:
  • Isolated test contexts with proper setup/teardown
  • Comprehensive edge case coverage
  • Clear test descriptions and expectations
  • Proper mocking of external dependencies
  • Consistent test structure and organization

flyerhzm/bullet

spec/bullet_spec.rb

            
# frozen_string_literal: true

require 'spec_helper'

describe Bullet, focused: true do
  subject { Bullet }

  describe '#enable' do
    context 'enable Bullet' do
      before do
        # Bullet.enable
        # Do nothing. Bullet has already been enabled for the whole test suite.
      end

      it 'should be enabled' do
        expect(subject).to be_enable
      end

      context 'disable Bullet' do
        before { Bullet.enable = false }

        it 'should be disabled' do
          expect(subject).to_not be_enable
        end

        context 'enable Bullet again without patching again the orms' do
          before do
            expect(Bullet::Mongoid).not_to receive(:enable) if defined?(Bullet::Mongoid)
            expect(Bullet::ActiveRecord).not_to receive(:enable) if defined?(Bullet::ActiveRecord)
            Bullet.enable = true
          end

          it 'should be enabled again' do
            expect(subject).to be_enable
          end
        end
      end
    end
  end

  # Testing the aliases.
  describe '#enabled' do
    context 'enable Bullet' do
      before do
        # Bullet.enable
        # Do nothing. Bullet has already been enabled for the whole test suite.
      end

      it 'should be enabled' do
        expect(subject).to be_enabled
      end

      context 'disable Bullet' do
        before { Bullet.enabled = false }

        it 'should be disabled' do
          expect(subject).to_not be_enabled
        end

        context 'enable Bullet again without patching again the orms' do
          before do
            expect(Bullet::Mongoid).not_to receive(:enabled) if defined?(Bullet::Mongoid)
            expect(Bullet::ActiveRecord).not_to receive(:enabled) if defined?(Bullet::ActiveRecord)
            Bullet.enabled = true
          end

          it 'should be enabled again' do
            expect(subject).to be_enabled
          end
        end
      end
    end
  end

  describe '#start?' do
    context 'when bullet is disabled' do
      before(:each) { Bullet.enable = false }

      it 'should not be started' do
        expect(Bullet).not_to be_start
      end
    end
  end

  describe '#debug' do
    before(:each) { $stdout = StringIO.new }

    after(:each) { $stdout = STDOUT }

    context 'when debug is enabled' do
      before(:each) { ENV['BULLET_DEBUG'] = 'true' }

      after(:each) { ENV['BULLET_DEBUG'] = 'false' }

      it 'should output debug information' do
        Bullet.debug('debug_message', 'this is helpful information')

        expect($stdout.string).to eq("[Bullet][debug_message] this is helpful information
")
      end
    end

    context 'when debug is disabled' do
      it 'should output debug information' do
        Bullet.debug('debug_message', 'this is helpful information')

        expect($stdout.string).to be_empty
      end
    end
  end

  describe '#add_safelist' do
    context "for 'special' class names" do
      it 'is added to the safelist successfully' do
        Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
        expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
      end
    end
  end

  describe '#delete_safelist' do
    context "for 'special' class names" do
      it 'is deleted from the safelist successfully' do
        Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
        Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
        expect(Bullet.safelist[:n_plus_one_query]).to eq({})
      end
    end

    context 'when exists multiple definitions' do
      it 'is deleted from the safelist successfully' do
        Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
        Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
        Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
        expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
        expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
      end
    end
  end

  describe '#perform_out_of_channel_notifications' do
    let(:notification) { double }

    before do
      allow(Bullet).to receive(:for_each_active_notifier_with_notification).and_yield(notification)
      allow(notification).to receive(:notify_out_of_channel)
    end

    context 'when called with Rack environment hash' do
      let(:env) { { 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/path', 'QUERY_STRING' => 'foo=bar' } }

      context "when env['REQUEST_URI'] is nil" do
        before { env['REQUEST_URI'] = nil }

        it 'should notification.url is built' do
          expect(notification).to receive(:url=).with('GET /path?foo=bar')
          Bullet.perform_out_of_channel_notifications(env)
        end
      end

      context "when env['REQUEST_URI'] is present" do
        before { env['REQUEST_URI'] = 'http://example.com/path' }

        it "should notification.url is env['REQUEST_URI']" do
          expect(notification).to receive(:url=).with('GET http://example.com/path')
          Bullet.perform_out_of_channel_notifications(env)
        end
      end
    end
  end
end