Explain Codes LogoExplain Codes Logo

How to properly assert that an exception gets raised in pytest?

python
pytest
exception-handling
testing
Anton ShumikhinbyAnton Shumikhin·Dec 29, 2024
TLDR

pytest.raises is the go-to tool for asserting exceptions in pytests. Encapsulate the relevant code block to assert the specific exception.

Example:

def test_divide_by_zero(): with pytest.raises(ZeroDivisionError): result = 1 / 0

This test clears only if ZeroDivisionError is thrown when dividing by zero.

Examining the exception details

Extracting the exception details is critical for in-depth assertions or analysis. Utilize the as keyword in the context manager to achieve this:

Example:

def test_zero_division(): with pytest.raises(ZeroDivisionError) as exc_info: result = 1 / 0 # Oops, that's not allowed in mathematics assert "division by zero" in str(exc_info.value)

Here, exc_info is holding that golden information about the exception. You can access the actual exception via exc_info.value. Useful for checking the error message or other exception attributes.

Testing exception absence

At times, you may want to ensure a block of code does NOT trigger any exception. By default, pytest does this implicitly if your test passes. However, if you want to make it explicit:

Example:

def test_should_not_raise(): try: # This innocent code shouldn't raise an exception innocent_function() except Exception as e: pytest.fail(f"Pardon, but why on earth did this exception {e} occur?")

While it's not a good practice compared to simply letting the test pass without exceptions, it clearly documents the Test-Innocence.

Focusing on specific exception messages

In some cases, exception messages need to be as specific as an Oxford English professor. For these scenarios, pytest.raises combines with the match parameter for asserting message correctness:

Example:

def test_specific_message(): with pytest.raises(ValueError, match=r'.* 42 .*'): raise ValueError("Somewhere along the Hitchhiker's tour, we lost 42")

The test will clear only if the error message carries '42'. Highly precise way to assert the exact exception type and its content.

Tackling foreign exceptions

When your code interacts with foreign systems or libraries, ensure to handle exceptions pointing to issues beyond your control.

Asserting an exception raised by an external library informs about a potential alien bug, rather than a flaw in your own awesome code.

Example:

def test_external_library_error_handling(): with pytest.raises(ExternalLibraryError): problematic_function_call() # Cross your fingers and pray

Clean and effective way to separate errors within your control from those beyond!