===============================
14.3 在单元测试中测试异常情况
===============================

----------
问题
----------
你想写个测试用例来准确的判断某个异常是否被抛出。

----------
解决方案
----------
对于异常的测试可使用 ``assertRaises()`` 方法。
例如，如果你想测试某个函数抛出了 ``ValueError`` 异常，像下面这样写：

.. code-block:: python

    import unittest

    # A simple function to illustrate
    def parse_int(s):
        return int(s)

    class TestConversion(unittest.TestCase):
        def test_bad_int(self):
            self.assertRaises(ValueError, parse_int, 'N/A')

如果你想测试异常的具体值，需要用到另外一种方法：

.. code-block:: python

    import errno

    class TestIO(unittest.TestCase):
        def test_file_not_found(self):
            try:
                f = open('/file/not/found')
            except IOError as e:
                self.assertEqual(e.errno, errno.ENOENT)

            else:
                self.fail('IOError not raised')

----------
讨论
----------
``assertRaises()`` 方法为测试异常存在性提供了一个简便方法。
一个常见的陷阱是手动去进行异常检测。比如：

.. code-block:: python

    class TestConversion(unittest.TestCase):
        def test_bad_int(self):
            try:
                r = parse_int('N/A')
            except ValueError as e:
                self.assertEqual(type(e), ValueError)

这种方法的问题在于它很容易遗漏其他情况，比如没有任何异常抛出的时候。
那么你还得需要增加另外的检测过程，如下面这样：

.. code-block:: python

    class TestConversion(unittest.TestCase):
        def test_bad_int(self):
            try:
                r = parse_int('N/A')
            except ValueError as e:
                self.assertEqual(type(e), ValueError)
            else:
                self.fail('ValueError not raised')

``assertRaises()`` 方法会处理所有细节，因此你应该使用它。

``assertRaises()`` 的一个缺点是它测不了异常具体的值是多少。
为了测试异常值，可以使用 ``assertRaisesRegex()`` 方法，
它可同时测试异常的存在以及通过正则式匹配异常的字符串表示。例如：

.. code-block:: python

    class TestConversion(unittest.TestCase):
        def test_bad_int(self):
            self.assertRaisesRegex(ValueError, 'invalid literal .*',
                                           parse_int, 'N/A')

``assertRaises()`` 和 ``assertRaisesRegex()``
还有一个容易忽略的地方就是它们还能被当做上下文管理器使用：

.. code-block:: python

    class TestConversion(unittest.TestCase):
        def test_bad_int(self):
            with self.assertRaisesRegex(ValueError, 'invalid literal .*'):
                r = parse_int('N/A')

但你的测试涉及到多个执行步骤的时候这种方法就很有用了。

