python之异常和单元测试及文档测试

python中类似java和c++中一样,建立了一套异常体系,这样允许程序员能够捕获程序中出现的异常,然后简单处理或者继续抛出异常,从而保证程序的健壮性。
异常的语法比较简单,大体框架如下:

try:
    express
except oneException:
    solve exception
except twoException
    solve exception
finally:
    some post-work

express部分是我们预测可能会出现异常的部分,如果出现了异常,则由except捕获进行异常处理,如果无法处理则可以继续向上层抛出异常,跟其他语言类似,异常可以一层层的向上抛出,到达顶层后,由顶层决定如何处理该异常(可以写专门的异常处理代码,或者由于严重异常而终止程序)。
在except部分可以写多个分支来捕获不同的异常(如上),这样就能针对不同的异常做出不同的异常处理,但是这里需要注意的一点是:异常是有继承关系的,如果父类异常在前,而子类异常放在后的话,那么子类异常则永远无法捕捉到,这点是需要注意的。
下面给出python中内建异常的继承结构:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

下面主要说一下python中的测试部分,这主要包括两部分:单元测试和文档测试

1.单元测试

单元测试的话我们可以通过编写一个测试类来测试相应的代码,测试类只要继承了unittest类即可,这就要求我们首先要引入unittest模块。

首先写一个简单的计算器类,只能进行整数和浮点数的加减操作,文件命名为calculator.py,内容如下:

#!/usr/bin/env python


class Calculator(object):
    '''
    >>> c = Calculator()
    >>> c.add(1, 2)
    3
    >>> c.add(-1, 2)
    1
    >>> c.add(-1, -2)
    -3
    >>> c.add(1, '2')
    Traceback (most recent call last):
        ...
    TypeError: Only accept int and float type
    
    >>> c.minus(1, 2)
    -1
    >>> c.minus(-2, -3)
    1
    >>> c.minus(-3, 1)
    -4
    >>> c.minus('a', 1)
    Traceback (most recent call last):
        ...
    TypeError: Only accept int and float type
    '''
    def add(self, a, b):
        self.valid_type(a, b)
        return a + b

    def minus(self, a, b):
        self.valid_type(a, b)
        return a - b

    def valid_type(self, *vals):
        for val in vals:
            if not isinstance(val, (int, float)):
                raise TypeError('Only accept int and float type')


if __name__ == '__main__':
    pass

然后我们写一个单元测试类,来对Calculator这个类的两个功能进行测试,首先如上所说引入unittest模块,然后引入要测试的模块,最后再测试类中写测试代码,这里注意,把测试方法命名为test_***,作为测试方法,具体代码是test.py,文件内容如下:

#!/usr/bin/env python


import unittest
from calculator import Calculator #首先导入我们要测的类

class TestCal(unittest.TestCase):
    def test_add(self):
        cal = Calculator()
        self.assertEqual(cal.add(1, 2), 3)
        self.assertEqual(cal.add(-2, 1), -1)
        self.assertEqual(cal.add(0, 2), 2)
        self.assertEqual(cal.add(-1, -1), -2)
        with self.assertRaises(TypeError):
            cal.add('a', 1)

    def test_minus(self):
        cal = Calculator()
        self.assertEqual(cal.minus(1, 2), -1)
        self.assertEqual(cal.minus(2, 1), 1)
        self.assertEqual(cal.minus(9, 0), 9)
        self.assertEqual(cal.minus(-9, 8), -17)
        with self.assertRaises(TypeError):
            cal.minus('a', 'b')

if __name__ == '__main__':
    unittest.main()

然后我们在当前文件目录中运行python -m unittest test -v来执行test.py从而进行测试,如下为测试结果:

Trying:
    c = Calculator()
Expecting nothing
ok
Trying:
    c.add(1, 2)
Expecting:
    3
ok
Trying:
    c.add(-1, 2)
Expecting:
    1
ok
Trying:
    c.add(-1, -2)
Expecting:
    -3
ok
Trying:
    c.add(1, '2')
Expecting:
    Traceback (most recent call last):
            ...
    TypeError: Only accept int and float type
ok
Trying:
    c.minus(1, 2)
Expecting:
    -1
ok
Trying:
    c.minus(-2, -3)
Expecting:
    1
ok
Trying:
    c.minus(-3, 1)
Expecting:
    -4
ok
Trying:
    c.minus('a', 1)
Expecting:
    Traceback (most recent call last):
            ...
    TypeError: Only accept int and float type
ok
4 items had no tests:
    __main__
    __main__.Calculator.add
    __main__.Calculator.minus
    __main__.Calculator.valid_type
1 items passed all tests:
   9 tests in __main__.Calculator
9 tests in 5 items.
9 passed and 0 failed.
Test passed.

C:\Users\Win7\Desktop\python>python learn.py -v
test_add (__main__.TestDict) ... ok
test_minus (__main__.TestDict) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

C:\Users\Win7\Desktop\python>python -m unittest test -v
test_add (test.TestCal) ... ok
test_minus (test.TestCal) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

如上,测试结果中的信息非常清晰,期望结果是什么,得到的实际结果是什么,是否通过测试,最后还有个总体数据,即总共运行了几组测试,通过了几组。非常详细。

2.文档测试

文档测试比较简单,我们正常的在函数或者类内头部加一些注释,这可以表明该类或者函数该如何使用,可以通过一些特定的工具将其抽出来组成文档。同时也可以用来测试,如上那个Calculator类中,我已经写了注释,这样我们可以将注释中的代码作为测试用例来运行,通过预期输出与实际输出的对比来达到测试的目的。

#!/usr/bin/env python


class Calculator(object):
    '''
    >>> c = Calculator()
    >>> c.add(1, 2)
    3
    >>> c.add(-1, 2)
    1
    >>> c.add(-1, -2)
    -3
    >>> c.add(1, '2')
    Traceback (most recent call last):
        ...
    TypeError: Only accept int and float type
    
    >>> c.minus(1, 2)
    -1
    >>> c.minus(-2, -3)
    1
    >>> c.minus(-3, 1)
    -4
    >>> c.minus('a', 1)
    Traceback (most recent call last):
        ...
    TypeError: Only accept int and float type
    '''
    def add(self, a, b):
        self.valid_type(a, b)
        return a + b

    def minus(self, a, b):
        self.valid_type(a, b)
        return a - b

    def valid_type(self, *vals):
        for val in vals:
            if not isinstance(val, (int, float)):
                raise TypeError('Only accept int and float type')


if __name__ == '__main__':
    import doctest
    doctest.testmod()

这样我们可以直接运行python calculator.py -v来查看文档测试的结果,如下是测试结果:

Trying:
    c = Calculator()
Expecting nothing
ok
Trying:
    c.add(1, 2)
Expecting:
    3
ok
Trying:
    c.add(-1, 2)
Expecting:
    1
ok
Trying:
    c.add(-1, -2)
Expecting:
    -3
ok
Trying:
    c.add(1, '2')
Expecting:
    Traceback (most recent call last):
            ...
    TypeError: Only accept int and float type
ok
Trying:
    c.minus(1, 2)
Expecting:
    -1
ok
Trying:
    c.minus(-2, -3)
Expecting:
    1
ok
Trying:
    c.minus(-3, 1)
Expecting:
    -4
ok
Trying:
    c.minus('a', 1)
Expecting:
    Traceback (most recent call last):
            ...
    TypeError: Only accept int and float type
ok
4 items had no tests:
    __main__
    __main__.Calculator.add
    __main__.Calculator.minus
    __main__.Calculator.valid_type
1 items passed all tests:
   9 tests in __main__.Calculator
9 tests in 5 items.
9 passed and 0 failed.
Test passed.

跟单元测试类似的测试结果,可以帮助我们分析哪些用例通过测试,哪些没有通过测试。

个人感觉python提供的两种测试确实是非常方便,这样我们在写代码的同时就可以同时写一些测试类,对代码进行测试,这也比较符合测试驱动开发的开发模型。

本文遵从CC3.0协议转载请注明:转自凌风技术站

本文标题:python之异常和单元测试及文档测试

本文链接地址:http://www.iaccepted.net/python/tutorial/51.html

相关文章



发表评论

电子邮件地址不会被公开。 必填项已用*标注