View Source Sentry.Test (Sentry v13.2.0)
Utilities for testing Sentry reports.
Usage
This module provides helpers that set up a local HTTP server (via Bypass) so that
Sentry SDK calls in your tests hit a local endpoint instead of the real Sentry API.
Events are captured via the existing before_send and before_send_log callbacks
and stored in an isolated ETS table per test, preserving the full struct data.
Bypass and NimbleOwnership Required
This module requires
bypassas a test dependency:{:bypass, "~> 2.0", only: [:test]}
Examples
The simplest way to use this module is with the setup_sentry/1 function:
defmodule MyApp.ErrorReportingTest do
use ExUnit.Case, async: true
setup do
Sentry.Test.setup_sentry()
end
test "reports exceptions to Sentry" do
try do
raise "boom"
rescue
e -> Sentry.capture_exception(e)
end
assert [%Sentry.Event{} = event] = Sentry.Test.pop_sentry_reports()
assert event.original_exception == %RuntimeError{message: "boom"}
end
endYou can also use start_collecting_sentry_reports/0 as an ExUnit setup callback
for backwards compatibility:
setup :start_collecting_sentry_reportsAssertion Helpers
See Sentry.Test.Assertions for convenient assertion functions that reduce
boilerplate when validating captured events, transactions, and logs.
Summary
Functions
Allows pid_to_allow to collect events back to owner_pid's test scope.
Cleans up test resources associated with owner_pid.
Collects decoded envelopes sent to a Bypass collector.
Collects check-in payloads sent through a Bypass envelope collector.
Collects events sent through a Bypass envelope collector.
Collects log items sent through a Bypass envelope collector.
Collects metric batch payloads sent through a Bypass envelope collector.
Collects transactions sent through a Bypass envelope collector.
Decodes a raw envelope binary into a list of {header, item} tuples.
Extracts check-in payloads from decoded envelope item lists.
Extracts event payloads from decoded envelope item lists.
Extracts log item payloads from decoded envelope item lists.
Extracts metric batch payloads from decoded envelope item lists.
Extracts transaction payloads from decoded envelope item lists.
Pops all the collected log events from the current process.
Pops all the collected metrics from the current process.
Pops all the collected events from the current process.
Pops all the collected transactions from the current process.
Sets up a Bypass envelope collector that forwards envelope bodies to the test process as messages.
Sets up a Bypass instance and configures Sentry for testing.
Starts an isolated, per-test Sentry.TelemetryProcessor and wires it
into the current test's config scope.
Starts collecting events.
Starts collecting events from the current process.
Functions
Allows pid_to_allow to collect events back to owner_pid's test scope.
Use this when an unrelated process — one that does not appear in the
current test's $callers chain — needs to have its captured events
routed into this test's collector. Typical examples include Broadway
workers, processes started by phoenix_test_playwright, or
long-lived GenServers that outlive the calling test process.
pid_to_allow may be a pid or a zero-arity function returning a pid;
the function form is resolved on call and is convenient when the pid
is not known until later.
This function is idempotent for the same owner_pid. It raises
ArgumentError when owner_pid has not yet called setup_sentry/1
(or start_collecting_sentry_reports/0), and raises when a different
live test scope already owns pid_to_allow.
Cleanup is automatic: allow entries are removed when the test exits
via the same on_exit callback registered by setup_sentry/1.
Example
setup do
Sentry.Test.setup_sentry()
end
test "events from a Broadway worker are captured" do
{:ok, worker_pid} = MyApp.Worker.start_link()
:ok = Sentry.Test.allow_sentry_reports(self(), worker_pid)
send(worker_pid, :do_work_that_reports)
assert_receive {:done, _}
assert [%Sentry.Event{}] = Sentry.Test.pop_sentry_reports()
end
@spec cleanup(pid()) :: :ok
Cleans up test resources associated with owner_pid.
Deprecated
This function is deprecated and will be removed in v14.0.0. Cleanup is now handled automatically when the owning test process exits.
@spec collect_envelopes(reference(), pos_integer(), keyword()) :: [[{map(), map()}]]
Collects decoded envelopes sent to a Bypass collector.
Returns a list of decoded envelope item lists. Each element is the result
of decode_envelope!/1 for one HTTP request.
Options
:timeout- timeout in ms to wait for each envelope (default: 1000)
collect_sentry_check_ins(ref, expected_count, opts \\ [])
View Source (since 13.0.0)@spec collect_sentry_check_ins(reference(), pos_integer(), keyword()) :: [map()]
Collects check-in payloads sent through a Bypass envelope collector.
This is a high-level helper combining collect_envelopes/3 and extract_check_ins/1.
Use this instead of manually destructuring [[{header, body}]] from collect_envelopes/3.
Options
:timeout- timeout in ms to wait for each envelope (default: 1000)
Examples
ref = setup_bypass_envelope_collector(bypass, type: "check_in")
Sentry.capture_check_in(status: :ok, monitor_slug: "my-job")
[check_in] = collect_sentry_check_ins(ref, 1)
assert check_in["status"] == "ok"
collect_sentry_events(ref, expected_count, opts \\ [])
View Source (since 13.0.0)@spec collect_sentry_events(reference(), pos_integer(), keyword()) :: [map()]
Collects events sent through a Bypass envelope collector.
This is a high-level helper combining collect_envelopes/3 and extract_events/1.
Use this instead of collect_envelopes(ref, count) |> extract_events().
Options
:timeout- timeout in ms to wait for each envelope (default: 1000)
Examples
ref = setup_bypass_envelope_collector(bypass)
trigger_event()
[event] = collect_sentry_events(ref, 1)
collect_sentry_logs(ref, expected_count, opts \\ [])
View Source (since 13.0.0)@spec collect_sentry_logs(reference(), pos_integer(), keyword()) :: [map()]
Collects log items sent through a Bypass envelope collector.
This is a high-level helper combining collect_envelopes/3 and extract_log_items/1.
Use this instead of collect_envelopes(ref, count) |> extract_log_items().
Options
:timeout- timeout in ms to wait for each envelope (default: 1000)
Examples
ref = setup_bypass_envelope_collector(bypass)
Logger.info("something happened")
[log] = collect_sentry_logs(ref, 1)
collect_sentry_metric_items(ref, expected_count, opts \\ [])
View Source (since 13.0.0)@spec collect_sentry_metric_items(reference(), pos_integer(), keyword()) :: [map()]
Collects metric batch payloads sent through a Bypass envelope collector.
This is a high-level helper combining collect_envelopes/3 and extract_metric_items/1.
Use this instead of collect_envelopes(ref, count) |> extract_metric_items().
Each returned map has an "items" key containing the individual metric maps.
Options
:timeout- timeout in ms to wait for each envelope (default: 1000)
Examples
ref = setup_bypass_envelope_collector(bypass, type: "trace_metric")
Sentry.Metrics.count("button.clicks", 1)
[batch] = collect_sentry_metric_items(ref, 1)
[metric] = batch["items"]
assert metric["name"] == "button.clicks"
collect_sentry_transactions(ref, expected_count, opts \\ [])
View Source (since 13.0.0)@spec collect_sentry_transactions(reference(), pos_integer(), keyword()) :: [map()]
Collects transactions sent through a Bypass envelope collector.
This is a high-level helper combining collect_envelopes/3 and extract_transactions/1.
Use this instead of collect_envelopes(ref, count) |> extract_transactions().
Options
:timeout- timeout in ms to wait for each envelope (default: 1000)
Examples
ref = setup_bypass_envelope_collector(bypass)
run_traced_job()
[tx] = collect_sentry_transactions(ref, 1)
Decodes a raw envelope binary into a list of {header, item} tuples.
Extracts check-in payloads from decoded envelope item lists.
Extracts event payloads from decoded envelope item lists.
Extracts log item payloads from decoded envelope item lists.
Extracts metric batch payloads from decoded envelope item lists.
Each returned map has an "items" key containing the individual
metric maps for that batch. This mirrors the structure of extract_log_items/1.
Extracts transaction payloads from decoded envelope item lists.
@spec pop_sentry_logs(pid()) :: [Sentry.LogEvent.t()]
Pops all the collected log events from the current process.
Returns a list of all Sentry.LogEvent structs that have been collected.
After this function returns, the collected log events are cleared but
collection continues.
Logs are Asynchronous
Log events flow through the
TelemetryProcessorpipeline asynchronously. You may need to add a small delay before calling this function to ensure all log events have been processed by thebefore_send_logcallback.
@spec pop_sentry_metrics(pid()) :: [Sentry.Metric.t()]
Pops all the collected metrics from the current process.
Returns a list of all Sentry.Metric structs that have been collected.
After this function returns, the collected metrics are cleared but
collection continues.
Metrics are Asynchronous
Metric events flow through the
TelemetryProcessorpipeline asynchronously. You may need to add a small delay before calling this function to ensure all metrics have been processed by thebefore_send_metriccallback.
@spec pop_sentry_reports(pid()) :: [Sentry.Event.t()]
Pops all the collected events from the current process.
Returns a list of all Sentry.Event structs that have been collected from the
current process and all child processes spawned from it. After this function
returns, the collected events are cleared but collection continues.
Examples
iex> Sentry.Test.start_collecting_sentry_reports()
:ok
iex> Sentry.capture_message("Oops")
{:ok, ""}
iex> [%Sentry.Event{} = event] = Sentry.Test.pop_sentry_reports()
iex> event.message.formatted
"Oops"
@spec pop_sentry_transactions(pid()) :: [Sentry.Transaction.t()]
Pops all the collected transactions from the current process.
Returns a list of all Sentry.Transaction structs that have been collected.
After this function returns, the collected transactions are cleared but
collection continues.
Examples
iex> Sentry.Test.start_collecting_sentry_reports()
:ok
iex> Sentry.send_transaction(Sentry.Transaction.new(%{span_id: "123", start_timestamp: "2024-10-12T13:21:13", timestamp: "2024-10-12T13:21:13", spans: []}))
{:ok, ""}
iex> [%Sentry.Transaction{}] = Sentry.Test.pop_sentry_transactions()
setup_bypass_envelope_collector(bypass, opts \\ [])
View Source (since 13.0.0)Sets up a Bypass envelope collector that forwards envelope bodies to the test process as messages.
Uses Bypass.stub (not Bypass.expect) to be resilient to stray requests
from background processes (e.g., OpenTelemetry span processor).
Use with collect_envelopes/3 to retrieve the decoded envelopes.
Options
:type- when set, only envelopes containing an item of this type (e.g.,"event","transaction","log") are forwarded to the test process. Envelopes not matching the type are silently dropped.
@spec setup_sentry(keyword()) :: %{ :bypass => term(), :telemetry_processor => atom(), optional(:ref) => reference() }
Sets up a Bypass instance and configures Sentry for testing.
Opens a Bypass on a random port, configures the DSN to point to it,
wires up before_send / before_send_log callbacks to capture structs
in an isolated ETS table, and starts a per-test Sentry.TelemetryProcessor
(via setup_telemetry_processor/1) so that assertions work for events
that travel through the TelemetryProcessor pipeline (logs, metrics, or
send_result: :none).
Returns a map with :bypass and :telemetry_processor for use in test
context. The :telemetry_processor value is the atom name of the
per-test processor and can be used to stop_supervised!/1 and start
a custom-configured one when needed.
Options
Any extra Sentry config options (e.g., dedup_events: false, traces_sample_rate: 1.0)
will be forwarded to the test config.
The reserved :telemetry_processor option is not forwarded to the test
config. Instead, its value (a keyword list) is passed to the per-test
Sentry.TelemetryProcessor (e.g. buffer_configs, buffer_capacities,
scheduler_weights, transport_capacity). This replaces the need to
manually stop_supervised!/1 and re-start_supervised!/2 the processor.
The reserved :collect_envelopes option is not forwarded to the test
config either. When set, a Bypass envelope collector is wired up
automatically and its reference is returned under the :ref key:
true— set up the collector with no options;- a keyword list — forwarded to
setup_bypass_envelope_collector/2(e.g.[type: "check_in"]to only collect a given item type).
This collapses the common bypass = setup_sentry(...); ref = setup_bypass_envelope_collector(bypass) two-step into one call.
Examples
setup do
Sentry.Test.setup_sentry()
end
setup do
Sentry.Test.setup_sentry(dedup_events: false)
endConfiguring the per-test processor (e.g. a smaller log batch size):
setup do
Sentry.Test.setup_sentry(
telemetry_processor: [buffer_configs: %{log: %{batch_size: 1}}]
)
endCollecting envelopes directly as the ExUnit setup return:
setup do
Sentry.Test.setup_sentry(collect_envelopes: true, traces_sample_rate: 1.0)
end
test "...", %{ref: ref} do
# ...
end
Starts an isolated, per-test Sentry.TelemetryProcessor and wires it
into the current test's config scope.
This is called automatically by setup_sentry/1 and
start_collecting_sentry_reports/0, so most users do not need to invoke
it directly. It is exposed for tests that want to perform the setup
without opening a Bypass.
The helper:
- starts a fresh
Sentry.TelemetryProcessorunder the ExUnit test supervisor with a unique name, - allows the scheduler PID in
Sentry.Test.Configso that per-test config overrides reach it, - stores the processor name in the process dictionary under
:sentry_telemetry_processorso thatSentry.TelemetryProcessor.add/1and friends route to it.
Returns the processor name (an atom).
Must be called from within an ExUnit test because it uses
ExUnit.Callbacks.start_supervised!/2 for automatic cleanup.
Options
tp_opts is a keyword list forwarded to the per-test
Sentry.TelemetryProcessor child spec (e.g. buffer_configs,
buffer_capacities, scheduler_weights, transport_capacity).
Idempotency depends on tp_opts:
- with no
tp_opts, an already-registered live processor (for example one started bySentry.Case) is reused and its name returned; - with
tp_opts, an already-registered live processor is stopped and restarted under the same name with the given options, so callers no longer need tostop_supervised!/1+start_supervised!/2manually.
@spec start_collecting(keyword()) :: :ok
Starts collecting events.
Deprecated
This function is deprecated and will be removed in v14.0.0. Use
setup_sentry/1instead.
The :owner, :cleanup, and :key options are no longer supported and are ignored.
@spec start_collecting_sentry_reports(map()) :: :ok
Starts collecting events from the current process.
This function configures Sentry for testing using the default Bypass instance (started at application boot). It can be used as an ExUnit setup callback:
setup :start_collecting_sentry_reportsThe context parameter is ignored — it exists so this function can be used
as an ExUnit setup callback.