Testing logging can be tricky but recently I’ve learned that Elixir’s ExUnit ships with a CaptureLog module that makes it easy to test logging behavior. I was really happy to see this built-in!

Let’s say you have a function that processes a template and logs errors when something goes wrong. You want to test both the error handling and ensure the right messages are being logged. Here’s a simplified version of such a function:

def render_template(%SourceFile{is_template: true} = source_file, vars) do
  case Solid.parse(source_file.content) do
    {:ok, template} ->
      # Template parsing succeeded, render it...

    {:error, reason} ->
      Logger.error("Template parsing failed", reason: reason)
      {:error, :template_parsing_failed}
  end
end

Without ExUnit.CaptureLog, testing this would result in error messages cluttering your test output, even though those errors are expected as part of the test.

ExUnit.CaptureLog allows you to:

  1. Capture log messages during test execution
  2. Assert against the content of those messages
  3. Keep your test output clean by suppressing expected error logs

Here’s how I used it:

defmodule YourTest do
  use ExUnit.Case

  import ExUnit.CaptureLog

  test "handles invalid template syntax" do
    file = %SourceFile{
      content: "{{ unclosed tag",
      is_template: true
    }

    # Capture logs during template rendering
    log = capture_log(fn ->
      assert {:error, "Template parsing failed: " <> _} =
        render_template(file, %{})
    end)

    # Assert against the captured log content
    assert log =~ "Template parsing failed"
    assert log =~ "expected end of string"
    assert log =~ "{{ unclosed tag"
  end
end

Breaking Down the Test

Let’s see what’s happening:

  1. First, we import the helper function:
import ExUnit.CaptureLog
  1. We wrap the code that generates logs in a capture_log function:
log = capture_log(fn ->
  # Code that generates logs
end)
  1. The capture_log function:
  • Takes a function as an argument
  • Executes that function
  • Captures any log output during execution
  • Returns the captured log as a string
  1. We can then make assertions about the log content using string matching:
assert log =~ "Template parsing failed"

Benefits

Using CaptureLog provides several advantages:

  1. Clean Test Output: Error logs don’t pollute your test output, making it easier to spot real test failures.

  2. Explicit Verification: You can verify that your error handling is not just returning the right values, but also logging the right information.

  3. Better Documentation: The test clearly shows what log messages are expected when errors occur.

ExUnit.CaptureLog is a great tool for testing logging behavior in your Elixir applications. It helps you write more comprehensive tests while keeping your test output clean. Next time you need to test code that generates logs, remember that you can capture and verify those logs as part of your test assertions.

Today I learned!

Further Reading