Back to Repositories

Testing Custom Logging Implementation in Postal

This test suite implements a custom logging utility for testing purposes, providing controlled logging functionality with tagging support and verification capabilities. It enables developers to track and validate log messages during test execution.

Test Coverage Overview

The test suite provides comprehensive coverage for a custom TestLogger implementation.

Key functionality tested includes:
  • Log level handling (info, debug, warn, error)
  • Message tagging and grouping
  • Log message verification
  • Print control functionality
Edge cases cover message matching with both string and regex patterns.

Implementation Analysis

The testing approach utilizes Ruby’s metaprogramming capabilities to dynamically define logging methods for different severity levels. The implementation leverages a GroupSet class for managing tagged logging contexts and provides flexible message matching through both exact string matches and regular expressions.

The pattern demonstrates Ruby’s method_missing and define_method patterns for DRY code implementation.

Technical Details

Testing tools and configuration:
  • Custom TestLogger class for isolated logging
  • Klogger::GroupSet integration for tag management
  • Dynamic method generation for log levels
  • Message storage in memory for verification
  • Flexible matching system for log validation

Best Practices Demonstrated

The test implementation showcases several quality practices in testing methodology.

Notable practices include:
  • Isolation of logging functionality for testing
  • Clean separation of concerns
  • Flexible verification mechanisms
  • DRY principle application through metaprogramming
  • Maintainable and extensible design patterns

postalserver/postal

spec/helpers/test_logger.rb

            
# frozen_string_literal: true

class TestLogger

  def initialize
    @log_lines = []
    @group_set = Klogger::GroupSet.new
    @print = false
  end

  def print!
    @print = true
  end

  def add(level, message, **tags)
    @group_set.groups.each do |group|
      tags = group[:tags].merge(tags)
    end

    @log_lines << { level: level, message: message, tags: tags }
    puts message if @print
    true
  end

  [:info, :debug, :warn, :error].each do |level|
    define_method(level) do |message, **tags|
      add(level, message, **tags)
    end
  end

  def tagged(**tags, &block)
    @group_set.call_without_id(**tags, &block)
  end

  def log_line(match)
    @log_lines.reverse.each do |log_line|
      return log_line if match.is_a?(String) && log_line[:message] == match
      return log_line if match.is_a?(Regexp) && log_line[:message] =~ match
    end
    nil
  end

  def has_logged?(match)
    !!log_line(match)
  end

end