Skip to main content

pytest-sentry

PyPI License

pytest-sentry is a pytest plugin that uses Sentry to store and aggregate information about your testruns.

Screenshot of tracing data of a pytest test suite

This is what a test run will look like in Sentry.

Quickstart

Prerequisites

Configuration

You can configure pytest-sentry with environment variables:

Running

If you have pytest-rerunfailures plugin enabled you set the environment variables and run pytest as usual:

export PYTEST_SENTRY_DSN="https://xx@xxx.ingest.sentry.io/xxx"  # your DSN
export PYTEST_SENTRY_TRACES_SAMPLE_RATE=1
export PYTEST_SENTRY_PROFILES_SAMPLE_RATE=1
export SENTRY_ENVIRONMENT="test-suite"

pytest --reruns=5

Now all flaky tests will report to the configured DSN in Sentry.io including trace information and profiles of your tests and test fixtures in an Sentry environment calld test-suite.

Tracking flaky tests as errors

Let's say you have a testsuite with some flaky tests that randomly break your CI build due to network issues, race conditions or other stuff that you don't want to fix immediately. The known workaround is to retry those tests automatically, for example using pytest-rerunfailures.

One concern against plugins like this is that they just hide the bugs in your testsuite or even other code. After all your CI build is green and your code probably works most of the time.

pytest-sentry tries to make that choice a bit easier by tracking flaky test failures in a place separate from your build status. Sentry is already a good choice for keeping tabs on all kinds of errors, important or not, in production, so let's try to use it in testsuites too.

The prerequisite is that you already make use of pytest and pytest-rerunfailures in CI. Now install pytest-sentry and set the PYTEST_SENTRY_DSN environment variable to the DSN of a new Sentry project.

Now every test failure that is "fixed" by retrying the test is reported to Sentry, but still does not break CI. Tests that consistently fail will not be reported.

With PYTEST_SENTRY_ALWAYS_REPORT=1, every failed attempt is reported instead, each one exactly once. Error events are tagged with pytest.attempt (1-based) and pytest.final_attempt (true once pytest-rerunfailures will not retry the test again), so you can tell flaky retries apart from tests that failed every attempt, e.g. alert on pytest.result:failed pytest.final_attempt:true.

Tracking the performance of your testsuite

By default pytest-sentry will send Performance data to Sentry:

  • Fixture setup is reported as a segment span to Sentry, such that you can answer questions like "what is my slowest test fixture" and "what is my most used test fixture".

  • Calls to the test function itself are reported as a separate segment span such that you can find large, slow tests as well.

  • Fixture setup related to a particular test item will be in the same trace, i.e. will have same trace ID. There is no common parent span though. It is purposefully dropped (via the client's ignore_spans) to spare quota as it does not contain interesting information::

    pytest.runtest.protocol  [one time, not sent]
      pytest.fixture.setup [multiple times, sent]
      pytest.runtest.call [one time, sent]
    

    The trace is per-test-item. For correlating spans across an entire test run, use the automatically attached CI tags or attach some tag on your own.

To measure performance data, install pytest-sentry and set PYTEST_SENTRY_DSN, like with errors. By default, the extension will send all performance data to Sentry. If you want to limit the amount of data sent, you can set the PYTEST_SENTRY_TRACES_SAMPLE_RATE environment variable to a float between 0 and 1. This will cause only a random sample of traces to be sent to Sentry.

Spans can have noticeable runtime overhead over just reporting errors. To disable, use a marker::

import pytest
import pytest_sentry

pytestmarker = pytest.mark.sentry_client({"traces_sample_rate": 0.0})

Advanced Options

pytest-sentry supports marking your tests to use a different DSN, client or scope per-test. You can use this to provide custom options to the Client object from the Sentry SDK for Python:

import random
import pytest

from sentry_sdk import Scope
from pytest_sentry import Client

@pytest.mark.sentry_client(None)
def test_no_sentry():
    # Even though flaky, this test never gets reported to sentry
    assert random.random() > 0.5

@pytest.mark.sentry_client("MY NEW DSN")
def test_custom_dsn():
    # Use a different DSN to report errors for this one
    assert random.random() > 0.5

# Other invocations:

@pytest.mark.sentry_client(Client("CUSTOM DSN"))
@pytest.mark.sentry_client(lambda: Client("CUSTOM DSN"))
@pytest.mark.sentry_client(Scope(Client("CUSTOM DSN")))
@pytest.mark.sentry_client({"dsn": ..., "debug": True})

The Client class exposed by pytest-sentry only has different default integrations. It disables some of the error-capturing integrations to avoid sending random expected errors into your project.

Accessing the used Sentry client

You will notice that the global functions such as sentry_sdk.capture_message will not actually send events into the same DSN you configured this plugin with. That's because pytest-sentry goes to extreme lenghts to keep its own SDK setup separate from the SDK setup of the tested code.

pytest-sentry exposes the sentry_test_scope fixture whose return value is the isolation Scope being used to send events to Sentry. Use with use_isolation_scope(sentry_test_scope): to temporarily switch context. You can use this to set custom tags like so::

from sentry_sdk.scope import use_isolation_scope


def test_foo(sentry_test_scope):
    with use_isolation_scope(sentry_test_scope):
        sentry_sdk.set_tag("pull_request", os.environ["EXAMPLE_CI_PULL_REQUEST"])

Why all the hassle with the context manager? Just imagine if your tested application would start to log some (expected) errors on its own. You would immediately exceed your quota!

Always reporting test failures

You can always report all test failures to Sentry by setting the environment variable PYTEST_SENTRY_ALWAYS_REPORT=1.

This can be enabled for builds on the main or release branch, to catch certain kinds of tests that are flaky across builds, but consistently fail or pass within one testrun.

Development

The project is managed with uv, linted and formatted with Ruff and type-checked with ty:

uv sync
uv run ruff check .
uv run ruff format --check .
uv run ty check
PYTEST_SENTRY_ALWAYS_REPORT=1 uv run pytest tests

License

Licensed under 2-clause BSD, see LICENSE.

Metadata

Release files for pytest-sentry 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-sentry 0.7.0
File Size Uploaded Provenance
pytest_sentry-0.7.0.tar.gz 11.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-sentry 0.7.0
File Interpreter ABI Platform
pytest_sentry-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 25.5 kB

Release files / pytest_sentry-0.7.0.tar.gz

Download URL pytest_sentry-0.7.0.tar.gz
Size 11.5 kB
Tags Source
SHA-256 checksum
How to use checksums
be7e7eb7463314d55574c2a61778f2876fc0b62a26bd5558b9619b59af805059
BLAKE2b-256 checksum
How to use checksums
eb4bd6677d638622b74f37a6457dcdb6b8d8f9d7a2904e4a0c07d3ffa19d363b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.2

Release files / pytest_sentry-0.7.0-py3-none-any.whl

Download URL pytest_sentry-0.7.0-py3-none-any.whl
Size 14.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e99cbd3013db309edbc86c82195ecb464f76515b43681fbcad883c7eac34ad22
BLAKE2b-256 checksum
How to use checksums
9b4511a973a842175c98a1fdbf75ca5cb45580361449e2b23fb5a6e0f114944d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.2

Security for pytest-sentry 0.7.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page