Back to Repositories

Testing Log Subscriber Implementation in Searchkick

This test suite validates logging functionality in the Searchkick gem, focusing on various search operations and their corresponding log outputs. It ensures proper logging behavior for create, update, destroy, bulk operations, and search functionality.

Test Coverage Overview

The test suite provides comprehensive coverage of Searchkick’s logging capabilities across different operations.

  • CRUD operation logging tests (create, update, destroy)
  • Bulk operation logging verification
  • Reindexing logging for both full and partial collections
  • Search operation logging including single and multi-search scenarios

Implementation Analysis

The testing approach utilizes Minitest framework with a structured setup for log capture and verification. Each test case follows a consistent pattern of performing an operation and asserting the expected log output.

The implementation leverages Ruby’s StringIO for log capture and ActiveSupport’s LogSubscriber for logging infrastructure.

Technical Details

  • Testing Framework: Minitest
  • Logging Infrastructure: ActiveSupport::LogSubscriber
  • Helper Methods: capture_logs for log isolation
  • Test Data Generation: create_products helper method

Best Practices Demonstrated

The test suite exemplifies several testing best practices including proper test isolation, helper method extraction, and comprehensive coverage of edge cases.

  • Isolated log capture mechanism
  • Clean test setup and teardown
  • Consistent assertion patterns
  • Modular helper methods

ankane/searchkick

test/log_subscriber_test.rb

            
require_relative "test_helper"

class LogSubscriberTest < Minitest::Test
  def test_create
    output = capture_logs do
      Product.create!(name: "Product A")
    end
    assert_match "Product Store", output
  end

  def test_update
    product = Product.create!(name: "Product A")
    output = capture_logs do
      product.reindex(:search_name)
    end
    assert_match "Product Update", output
  end

  def test_destroy
    product = Product.create!(name: "Product A")
    output = capture_logs do
      product.destroy
    end
    assert_match "Product Remove", output
  end

  def test_bulk
    output = capture_logs do
      Searchkick.callbacks(:bulk) do
        Product.create!(name: "Product A")
      end
    end
    assert_match "Bulk", output
    refute_match "Product Store", output
  end

  def test_reindex
    create_products
    output = capture_logs do
      Product.reindex
    end
    assert_match "Product Import", output
    assert_match '"count":3', output
  end

  def test_reindex_relation
    products = create_products
    output = capture_logs do
      Product.where.not(id: products.last.id).reindex
    end
    assert_match "Product Import", output
    assert_match '"count":2', output
  end

  def test_search
    output = capture_logs do
      Product.search("product").to_a
    end
    assert_match "Product Search", output
  end

  def test_multi_search
    output = capture_logs do
      Searchkick.multi_search([Product.search("product")])
    end
    assert_match "Multi Search", output
  end

  private

  def create_products
    Searchkick.callbacks(false) do
      3.times.map do
        Product.create!(name: "Product A")
      end
    end
  end

  def capture_logs
    previous_logger = ActiveSupport::LogSubscriber.logger
    io = StringIO.new
    begin
      ActiveSupport::LogSubscriber.logger = ActiveSupport::Logger.new(io)
      yield
      io.rewind
      output = io.read
      previous_logger.debug(output) if previous_logger
      puts output if ENV["LOG_SUBSCRIBER"]
      output
    ensure
      ActiveSupport::LogSubscriber.logger = previous_logger
    end
  end
end