ExUnitJSON.Trace (ex_unit_json v0.6.0)

Copy Markdown View Source

Opt-in failure-only BEAM message tracing for mix test.json.

Adds a "flight recorder" to a test: while the test runs, the inter-process send/receive messages of the test process tree are captured into a bounded ring buffer. If the test fails, the messages (and a best-effort mailbox snapshot of still-alive processes) are emitted into the JSON output under a "trace" key; if it passes, the buffer is discarded. This surfaces the message flow that led to a failure for AI consumers debugging GenServer / Port / LiveView interactions.

Enabling

Tracing must start inside the test process (the formatter runs separately and only sees a test once it is already finished), so it is wired via a setup callback you add once to your shared ExUnit.Case template:

defmodule MyApp.Case do
  use ExUnit.CaseTemplate

  using do
    quote do
      setup {ExUnitJSON.Trace, :setup}
    end
  end
end

Then activate it per test or per module with a tag:

@moduletag trace_messages: true          # whole module
@tag trace_messages: true                # one test
@tag trace_messages: 200                 # one test, ring size 200

Without the tag the setup callback is a zero-cost no-op, so it is safe to wire globally.

What it can and cannot capture

The recorded message flow (the ring buffer, with relative timestamps) is the reliable signal. The mailbox snapshot is best-effort and marked approx: by the time a failing test's process dies, its own mailbox is gone, and only processes still alive near the failure can be sampled. There is no authoritative "what was unread when it crashed" on the BEAM — that is physically unrecoverable.

Requires OTP 27+ (for :trace dynamic sessions), already implied by the library's use of the built-in :json module.

Summary

Functions

ExUnit setup callback. Use as setup {ExUnitJSON.Trace, :setup}.

Functions

setup(context)

@spec setup(map()) :: :ok

ExUnit setup callback. Use as setup {ExUnitJSON.Trace, :setup}.

Reads :trace_messages from the test context. When falsy/absent it is a no-op. When truthy it starts a recorder tracing this test process and the ExUnit test supervisor (so start_supervised/2 children are covered). An integer value sets the ring-buffer size.