Error paths are where bugs hide, and they need tests as much as the happy path.
To assert that code raises, use assertRaises as a context manager:
with self.assertRaises(ValueError):
parse_age("-3")The test fails if no exception, or a different one, is raised. Capture the exception to inspect it: with self.assertRaises(ValueError) as cm: then check cm.exception. assertRaisesRegex(ValueError, "negative") also matches the message with a regex search.
Keep the with block small: only the call that should raise. If setup code inside the block raises the same exception type by accident, the test passes for the wrong reason.
Related helpers with the same shape:
assertWarns(DeprecationWarning)andassertWarnsRegexcheck warnings.assertLogs("myapp", level="WARNING")captures log records emitted on a logger (and its children);cm.outputholds formatted lines such as'WARNING:myapp:disk low'. The test fails if nothing is logged.assertNoLogs(3.10+) asserts that nothing is logged.
Exception-group code can be tested by catching ExceptionGroup and inspecting .exceptions.
Example
import logging, unittest, warnings
def parse_age(text: str) -> int:
age = int(text)
if age < 0:
raise ValueError(f"negative age: {age}")
if age > 150:
logging.getLogger("app").warning("suspicious age %d", age)
return age
class TestParseAge(unittest.TestCase):
def test_negative(self):
with self.assertRaises(ValueError) as cm:
parse_age("-3")
self.assertIn("negative", str(cm.exception))
def test_not_a_number(self):
self.assertRaisesRegex(ValueError, "invalid literal", parse_age, "x")
def test_logs_suspicious(self):
with self.assertLogs("app", level="WARNING") as cm:
parse_age("200")
self.assertEqual(cm.output, ["WARNING:app:suspicious age 200"])
unittest.main(exit=False) # ... Ran 3 tests ... OKRemember
- with self.assertRaises(Exc) as cm: checks an exception; cm.exception holds it
- assertRaisesRegex also matches the message
- assertWarns, assertLogs and assertNoLogs cover warnings and logging
Watch out for
- Putting several statements in an assertRaises block, so the wrong line raising still passes the test.