This rule raises an issue when a test for an expected exception contains multiple method calls, making it unclear which call is expected to raise the exception.

Why is this an issue?

When writing tests for exception handling, clarity is essential. If you chain multiple method calls within the code being tested for an exception, it becomes ambiguous which specific call is expected to raise that exception.

For example, both parse_payment(payload) and normalize() could raise a ValueError:

with pytest.raises(ValueError):
    parse_payment(payload).normalize()

The test does not make it explicit which method is expected to raise the exception. This ambiguity makes the test harder to read and maintain.

By isolating the method call you want to test, you make your test explicit and unambiguous. This improves readability and maintainability, and makes your test suite a better specification of expected behavior.

What is the potential impact?

Writing tests with multiple method calls when testing for exceptions reduces test quality:

How to fix it in pytest

When using pytest’s pytest.raises() context manager, extract intermediate method calls into separate variables before the assertion. This ensures only the final method call that you want to test is inside the exception-testing block.

Code examples

Noncompliant code example

import pytest

def test_normalize_rejects_negative_amount():
    with pytest.raises(ValueError):  # Noncompliant
        parse_payment(payload).normalize()

Compliant solution

import pytest

def test_normalize_rejects_negative_amount():
    payment = parse_payment(payload)
    with pytest.raises(ValueError):
        payment.normalize()

How to fix it in unittest

When using unittest’s assertRaises() method, extract intermediate method calls before the assertion. This makes it clear which specific method is being tested for the exception.

Code examples

Noncompliant code example

import unittest

class TestPayment(unittest.TestCase):
    def test_normalize_rejects_negative_amount(self):
        self.assertRaises(ValueError, lambda: parse_payment(payload).normalize())  # Noncompliant

Compliant solution

import unittest

class TestPayment(unittest.TestCase):
    def test_normalize_rejects_negative_amount(self):
        payment = parse_payment(payload)
        self.assertRaises(ValueError, payment.normalize)

Resources

Documentation