This rule raises an issue when assertion statements, assertion methods, or fluent assertions are placed within a try block that catches
AssertionError exceptions without properly handling or re-raising them.
Assertions in Python raise an AssertionError when they fail. This includes built-in assert statements,
unittest assertion helpers such as assertEqual, and fluent assertions from libraries such as assertpy. This is
the mechanism testing frameworks use to detect test failures.
When you place assertions within a try-except block that catches AssertionError (or its parent class Exception), and you
do not re-raise the exception or validate its properties, you create a silent failure scenario. The assertion can fail, but the exception gets
swallowed by the except block, causing the test to pass when it should have failed.
This defeats the entire purpose of writing assertions in tests. The test appears to pass, giving false confidence that the code works correctly, when in reality the assertion failed and detected a problem.
The root cause of this anti-pattern is usually a misunderstanding of exception handling in tests. Developers might add try-except blocks around assertions thinking they need to "handle" exceptions, but assertions are meant to propagate upward to the test framework.
This issue commonly occurs when:
assertpy are treated like ordinary code inside a try-except AssertionError
blockWhen assertions are silently caught and ignored, tests provide false confidence in the codebase. Code that should fail tests will appear to pass, allowing bugs to reach production.
This can lead to:
The assertion (self.fail(…)) raises an AssertionError, which is immediately caught by the surrounding except
AssertionError block and silently discarded. As a result, the test can never fail, even when do_something() does not behave as
expected.
import unittest
class MyTest(unittest.TestCase):
def test_should_raise_error(self):
try:
self.do_something()
self.fail("Expected an AssertionError!") # Noncompliant
except AssertionError:
pass
def do_something(self):
raise AssertionError("Something went wrong")
This variation has the same problem: the AssertionError is caught locally and ignored. The test appears to check the exception
details, but it never validates anything about the caught error.
import unittest
class MyTest(unittest.TestCase):
def test_error_message(self):
try:
self.do_something()
self.fail("Expected an AssertionError!") # Noncompliant
except AssertionError:
pass # Exception caught but not validated
def do_something(self):
raise AssertionError("Something went wrong")
Use the proper assertion context managers provided by the testing framework. The assertRaises context manager is designed specifically
for testing that exceptions are raised, without accidentally catching assertion failures.
import unittest
class MyTest(unittest.TestCase):
def test_should_raise_error(self):
with self.assertRaises(AssertionError):
self.do_something()
def do_something(self):
raise AssertionError("Something went wrong")
If you need to verify specific properties of the exception, you can access the exception object through the context manager and make assertions about it after the context exits. This ensures the exception is properly raised while still allowing you to validate its details.
import unittest
class MyTest(unittest.TestCase):
def test_error_message(self):
with self.assertRaises(AssertionError) as context:
self.do_something()
self.assertEqual(str(context.exception), "Something went wrong")
def do_something(self):
raise AssertionError("Something went wrong")
The assert False statement raises an AssertionError, which is then swallowed by the surrounding except
AssertionError block. Because of that, the test can never fail when do_something() does not raise the expected error.
import pytest
def test_should_raise_error():
try:
do_something()
assert False, "Expected an AssertionError!" # Noncompliant
except AssertionError:
pass
def do_something():
raise AssertionError("Something went wrong")
Here too, the caught AssertionError is never validated or re-raised. The test silently succeeds instead of checking that the expected
failure really happened.
import pytest
def test_error_details():
try:
do_something()
assert False, "Expected an AssertionError!" # Noncompliant
except AssertionError:
pass # Exception details not validated
def do_something():
raise AssertionError("Something went wrong")
When using pytest, use the pytest.raises context manager to properly test for exceptions. This ensures that assertion failures are not
accidentally caught.
import pytest
def test_should_raise_error():
with pytest.raises(AssertionError):
do_something()
def do_something():
raise AssertionError("Something went wrong")
To validate specific exception properties with pytest, access the exception info from the context manager. You can then make assertions about the exception message or other attributes.
import pytest
def test_error_details():
with pytest.raises(AssertionError) as exc_info:
do_something()
assert str(exc_info.value) == "Something went wrong"
def do_something():
raise AssertionError("Something went wrong")
With assertpy, fluent assertions still raise AssertionError. Catching that exception in the surrounding
try-except block can hide a failing test.
from assertpy import assert_that
def test_result():
try:
result = get_result()
assert_that(result).is_equal_to("expected") # Noncompliant
except AssertionError:
pass
Keep the assertpy assertion outside the try-except block so test failures are reported normally.
from assertpy import assert_that
def test_result():
result = get_result()
assert_that(result).is_equal_to("expected")