This rule raises an issue when a test uses a try-except block combined with fail() to verify whether an exception is raised or not raised.

Why is this an issue?

Using try-except blocks combined with fail() to test for the presence or absence of exceptions is an anti-pattern.

Modern testing frameworks like pytest and Python’s built-in unittest provide dedicated assertion methods that make exception testing cleaner and more explicit. These methods eliminate boilerplate code, clearly communicate test intent, and make it easier to assert on exception details.

The try-except-fail() pattern has several drawbacks:

Dedicated exception assertions solve these problems by providing a declarative way to express exception expectations.

What is the potential impact?

Using try-except with fail() instead of dedicated exception assertions reduces test code quality and maintainability:

How to fix it in unittest

Code examples

Noncompliant code example

Replace try-except blocks that use fail() with assertRaises used as a context manager. This built-in method from Python’s unittest framework provides a clean way to verify exception behavior.

When you expect no exception, simply call the code directly without wrapping it in a try-except block. If an unexpected exception is raised, the test framework will catch it and fail the test automatically.

When you expect an exception, use assertRaises as a context manager. This allows you to capture the exception and make additional assertions on its properties.

import unittest

class UserServiceTest(unittest.TestCase):
    def test_no_exception_thrown(self):
        try:
            self.user_service.register_user(self.valid_user)
        except ValidationError:
            self.fail("Should not have thrown any exception")  # Noncompliant

When verifying that an exception is raised, use assertRaises as a context manager. This provides access to the exception object for further assertions.

import unittest

class UserServiceTest(unittest.TestCase):
    def test_exception_is_thrown(self):
        try:
            self.user_service.register_user(self.invalid_user)
            self.fail("Expected ValidationError to be thrown")  # Noncompliant
        except ValidationError as e:
            self.assertEqual("Invalid email", str(e))

Compliant solution

Replace try-except blocks that use fail() with assertRaises used as a context manager. This built-in method from Python’s unittest framework provides a clean way to verify exception behavior.

When you expect no exception, simply call the code directly without wrapping it in a try-except block. If an unexpected exception is raised, the test framework will catch it and fail the test automatically.

When you expect an exception, use assertRaises as a context manager. This allows you to capture the exception and make additional assertions on its properties.

import unittest

class UserServiceTest(unittest.TestCase):
    def test_no_exception_thrown(self):
        # Simply call the method - unittest will fail if an exception is raised
        self.user_service.register_user(self.valid_user)

When verifying that an exception is raised, use assertRaises as a context manager. This provides access to the exception object for further assertions.

import unittest

class UserServiceTest(unittest.TestCase):
    def test_exception_is_thrown(self):
        with self.assertRaises(ValidationError) as context:
            self.user_service.register_user(self.invalid_user)

        self.assertEqual("Invalid email", str(context.exception))

How to fix it in pytest

Code examples

Noncompliant code example

Replace try-except blocks with pytest.raises used as a context manager. Pytest is the most popular Python testing framework and provides excellent exception testing support.

When you expect no exception, simply call the code directly. Pytest will fail the test if an unexpected exception occurs.

When you expect an exception, use pytest.raises as a context manager to verify the exception type and optionally inspect its properties.

import pytest

def test_no_exception_thrown(user_service, valid_user):
    try:
        user_service.register_user(valid_user)
    except ValidationError:
        pytest.fail("Should not have thrown any exception")  # Noncompliant

When verifying that an exception is raised with pytest, use pytest.raises as a context manager. This gives you access to the exception object for additional assertions using the match parameter or the value attribute.

import pytest

def test_exception_is_thrown(user_service, invalid_user):
    try:
        user_service.register_user(invalid_user)
        pytest.fail("Expected ValidationError to be thrown")  # Noncompliant
    except ValidationError as e:
        assert str(e) == "Invalid email"

Compliant solution

Replace try-except blocks with pytest.raises used as a context manager. Pytest is the most popular Python testing framework and provides excellent exception testing support.

When you expect no exception, simply call the code directly. Pytest will fail the test if an unexpected exception occurs.

When you expect an exception, use pytest.raises as a context manager to verify the exception type and optionally inspect its properties.

def test_no_exception_thrown(user_service, valid_user):
    # Simply call the method - pytest will fail if an exception is raised
    user_service.register_user(valid_user)

When verifying that an exception is raised with pytest, use pytest.raises as a context manager. This gives you access to the exception object for additional assertions using the match parameter or the value attribute.

import pytest

def test_exception_is_thrown(user_service, invalid_user):
    with pytest.raises(ValidationError) as exc_info:
        user_service.register_user(invalid_user)

    assert str(exc_info.value) == "Invalid email"

Resources

Documentation