杰瑞科技汇

assertRaises 如何正确捕获 Python 异常?

Of course! The term assertraises isn't a built-in Python function, but it's almost certainly a reference to pytest.raises, which is the standard and most popular way to test for exceptions in Python.

assertRaises 如何正确捕获 Python 异常?-图1
(图片来源网络,侵删)

Let's break down how to test for exceptions correctly, covering both the modern pytest approach and the older, built-in unittest approach.


The Best Practice: Using pytest.raises

pytest is a powerful testing framework, and its raises context manager is the cleanest and most "Pythonic" way to write these tests.

Basic Usage

You use the with statement and wrap the code you expect to raise an exception inside the pytest.raises() block.

# A simple function we want to test
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b
# A test file, e.g., test_divide.py
import pytest
def test_division_by_zero():
    # The code inside the 'with' block is expected to raise a ValueError
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        divide(10, 0)
# You can also just check the type of the exception
def test_division_by_zero_type_check():
    with pytest.raises(ValueError): # No 'match' needed
        divide(10, 0)

How It Works

  • If the code inside the with block raises the specified exception, the test passes.
  • If the code inside the with block raises a different exception or no exception at all, the test fails.
  • The match argument is very useful. It uses a regular expression to check the error message, making your tests more precise and robust.

A More Complete Example

Let's imagine you have a file my_module.py:

assertRaises 如何正确捕获 Python 异常?-图2
(图片来源网络,侵删)
# my_module.py
import re
class User:
    def __init__(self, username, email):
        if not isinstance(username, str) or len(username) < 3:
            raise ValueError("Username must be a string with at least 3 characters")
        if not re.match(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", email):
            raise ValueError("Invalid email format")
        self.username = username
        self.email = email

And your test file test_my_module.py:

# test_my_module.py
import pytest
from my_module import User
def test_valid_user_creation():
    """Test that creating a valid user works."""
    user = User("testuser", "test@example.com")
    assert user.username == "testuser"
    assert user.email == "test@example.com"
def test_invalid_username():
    """Test that a short username raises a ValueError."""
    with pytest.raises(ValueError, match="Username must be a string with at least 3 characters"):
        User("a", "test@example.com")
def test_invalid_email():
    """Test that a bad email format raises a ValueError."""
    with pytest.raises(ValueError, match="Invalid email format"):
        User("testuser", "not-an-email")
def test_no_exception_raised():
    """Test that a valid user does NOT raise an exception."""
    # This test will pass if no exception is raised inside the block
    with pytest.raises(AssertionError): # We expect this to fail
        User("validuser", "valid@example.com")

The Alternative: Using unittest.TestCase

If you're working within the standard unittest framework, you would use the assertRaises() method. It works a bit differently.

Basic Usage

The assertRaises() method is a context manager, just like pytest.raises.

# test_divide_unittest.py
import unittest
from my_module import divide # Using the same divide function from before
class TestDivision(unittest.TestCase):
    def test_division_by_zero(self):
        # The context manager catches the exception
        with self.assertRaises(ValueError):
            divide(10, 0)
    def test_division_by_zero_with_message(self):
        # To check the message, you need to access the exception object
        with self.assertRaises(ValueError) as context:
            divide(10, 0)
        # Now you can inspect the exception
        self.assertEqual(str(context.exception), "Cannot divide by zero")
if __name__ == '__main__':
    unittest.main()

How It Works

  • You call self.assertRaises(ExpectedException) as a context manager.
  • The exception object is captured in the variable you assign (e.g., as context).
  • You then need to manually check the message using str(context.exception).

Summary: pytest.raises vs. unittest.assertRaises

Feature pytest.raises unittest.assertRaises Winner
Readability with pytest.raises(ValueError, match="..."): ... with self.assertRaises(ValueError) as context: pytest
Message Matching Built-in match argument with regex. Requires capturing the exception and manually checking str(context.exception). pytest
Setup No class inheritance needed. Just write a function. Must inherit from unittest.TestCase. pytest
Community Extremely popular and widely used in the Python community. Part of the standard library, so always available. Tie

Conclusion

When someone mentions "assertraises" in a Python context, they are almost certainly referring to the concept of testing for exceptions.

assertRaises 如何正确捕获 Python 异常?-图3
(图片来源网络,侵删)
  • For new projects or if you can choose a library, use pytest.raises. It's more concise, readable, and powerful.
  • If you are constrained to the standard library or working on a legacy unittest-based project, use unittest.TestCase.assertRaises.

For 99% of modern Python development, pytest is the recommended tool for testing, including for checking exceptions.

分享:
扫描分享到社交APP
上一篇
下一篇