How to use pytest to check that Error is NOT raised

Learn how to use pytest to check that error is not raised with practical examples, diagrams, and best practices. Covers python, pytest, raise development techniques with visual explanations.

Ensuring Code Robustness: How to Assert No Exceptions with pytest

Hero image for How to use pytest to check that Error is NOT raised

Learn how to effectively use pytest to verify that your Python code does NOT raise specific exceptions, ensuring stability and correctness in your applications.

When writing tests, it's common to assert that certain operations raise expected exceptions. However, an equally important aspect of testing is to ensure that your code doesn't raise an exception when it's not supposed to. This is crucial for verifying the stability and correct behavior of functions under normal operating conditions. This article will guide you through various pytest techniques to assert the absence of exceptions.

The Basic Approach: No pytest.raises

The simplest way to assert that no exception is raised is to simply execute the code without wrapping it in pytest.raises. If the code under test executes successfully without an unhandled exception, the test passes. If an unexpected exception occurs, the test will fail, indicating a problem.

import pytest

def safe_function(x):
    if x < 0:
        raise ValueError("Input cannot be negative")
    return x * 2

def test_safe_function_no_exception():
    # This test passes if no exception is raised
    result = safe_function(5)
    assert result == 10

def test_safe_function_with_exception():
    # This test expects a ValueError
    with pytest.raises(ValueError):
        safe_function(-1)

Basic test demonstrating successful execution and expected exception handling.

Asserting Specific Exceptions Are NOT Raised

While the basic approach works for any unexpected exception, sometimes you might want to explicitly state that a particular exception type should not be raised. This can be useful in scenarios where you've refactored code to prevent a specific error, or when you want to be very explicit about the expected behavior. Pytest doesn't have a direct pytest.does_not_raise context manager, but you can achieve this using a combination of try...except and pytest.fail.

import pytest

def process_data(data):
    if not isinstance(data, list):
        raise TypeError("Data must be a list")
    if not data:
        raise ValueError("Data list cannot be empty")
    return [item.upper() for item in data]

def test_process_data_no_type_error():
    try:
        process_data(["apple", "banana"])
    except TypeError:
        pytest.fail("TypeError was unexpectedly raised!")

def test_process_data_no_value_error():
    try:
        process_data(["one", "two"])
    except ValueError:
        pytest.fail("ValueError was unexpectedly raised!")

def test_process_data_expected_type_error():
    with pytest.raises(TypeError):
        process_data("not a list")

def test_process_data_expected_value_error():
    with pytest.raises(ValueError):
        process_data([])

Using try...except with pytest.fail to assert the absence of specific exceptions.

flowchart TD
    A[Start Test] --> B{Execute Code Under Test}
    B --> C{Exception Raised?}
    C -- Yes --> D{Is it an Expected Exception?}
    D -- Yes --> E[Test Passes (e.g., with pytest.raises)]
    D -- No --> F[Test Fails (Unexpected Exception)]
    C -- No --> G[Test Passes (No Exception)]

Decision flow for pytest exception handling.

Testing for No Exception in a More Generic Way

If you want to ensure that any exception is not raised, but still want to use a context manager-like syntax for clarity, you can create a custom helper. While pytest.raises is designed for expecting an exception, you can invert its logic or use a simple try...except block to catch any Exception and then fail the test.

import pytest

def potentially_problematic_function(value):
    if value == 0:
        return 1 / value # This will raise ZeroDivisionError
    if not isinstance(value, (int, float)):
        raise TypeError("Input must be a number")
    return value * 10

def test_no_exception_generic():
    try:
        potentially_problematic_function(5)
        potentially_problematic_function(10.5)
    except Exception as e:
        pytest.fail(f"An unexpected exception was raised: {e}")

def test_expected_zero_division_error():
    with pytest.raises(ZeroDivisionError):
        potentially_problematic_function(0)

def test_expected_type_error():
    with pytest.raises(TypeError):
        potentially_problematic_function("text")

A generic approach to ensure no exceptions are raised using try...except Exception.