Testing

New with Semantic Logger v4.13.

When writing tests we want to verify that the correct log messages, metrics, etc. are being created by the application. Since Semantic Logger uses a global logging mechanism we can’t use the regular logging to verify logging events.

Minitest helpers

To add the Minitest helper methods, add the following line to test_helper.rb:

Minitest::Test.include SemanticLogger::Test::Minitest

Example test file:

require_relative "test_helper"

class UserTest < ActiveSupport::TestCase
  describe User do
    it "logs message" do
      messages = semantic_logger_events do
        # Captures all log events during this block
        User.new.enable!
      end

      # Confirm the number of expected messages
      assert_equal 2, messages.count, messages

      # Confirm that the first log message includes the following elements
      assert_semantic_logger_event(
        messages[0],
        level:   :info,
        message: "User enabled"
      )

      # Confirm that the second log message includes the following elements
      assert_semantic_logger_event(
        messages[1],
        level:   :debug,
        message: "Completed"
      )
    end
  end
end

For more examples on testing log messages, see Rails Semantic Logger tests.

Capturing Events

The method semantic_logger_events returns all captured log events that occurred during the supplied block.

messages = semantic_logger_events do
  # ... Captures all log events during this block
end

By default semantic_logger_events captures all log events. To narrow the events to only those captured by a specific class, supply the class name. For Example:

messages = semantic_logger_events(ApiClient) do
  # ... Only captures ApiClient log events created during this block.
end

Verifying a log event

Use assert_semantic_logger_event to verify that a single log event has all the required attributes.

assert_semantic_logger_event(
  messages[0],
  level:   :info,
  message: "User enabled",
)

All arguments other than event are optional:

Instead of asserting an exact match on the message, a partial match can be supplied using message_includes. For example:

assert_semantic_logger_event(
  messages[0],
  level:            :info,
  message_includes: "enabled"
)

To verify a partial payload, and ignore any extra keys in the payload, use payload_includes to specify the partial payload.

Example, asserts the entire payload is an exact match:

assert_semantic_logger_event(
  messages[0],
  level:   :info,
  message: "User enabled",
  payload: {
    first_name: "Jack",
    last_name:  "Jones"
  }
)

Example, asserts a partial payload matches:

assert_semantic_logger_event(
  messages[0],
  level:            :info,
  message:          "User enabled",
  payload_includes: {
    first_name: "Jack"
  }
)

Other testing frameworks

If you use another testing framework and would like to contribute helper methods similar to the ones supplied in Semantic Logger for Minitest, we would welcome a pull request.

The approach is to stub out the Semantic Logger and replace it with an instance of SemanticLogger::Test::CaptureLogEvents. It looks and feels like a regular logging class, except that it retains the log events in memory. The raw events are captured so that tests are not affected by configured appenders or their formats.

Define a special test logger to capture log events:

let(:logger) { SemanticLogger::Test::CaptureLogEvents.new }

To capture just the log events from a specific class, stub the logger on that class:

User.stub(:logger, logger) do
  # Capture all logging calls to the User logger.
end

Or, to capture all log events from all classes, stub the global logger:

SemanticLogger::Logger.stub(:processor, logger) do
  # Capture all log events during the block.
end

The log events are now available in logger.events.

Next: Appenders ==>