• Day-10: 错误、调试和测试


      程序运行时,会遇到各种各样的错误。

      编写错误叫做bug,而另一类由于运行过程中无法预测的,比如写文件时,磁盘满了,写不进去;或者从网络抓取数据,网络突然掉了。这些错误称为异常,程序中需要对异常进行处理,使得程序能够运行下去。

    • 错误处理

      Python中,程序运行错误时,如果错误没有捕获,它会一直往上抛,最后被Python解释器捕获,打印一个错误。

    # err.py:
    def foo(s):
        return 10 / int(s)
    
    def bar(s):
        return foo(s) * 2
    
    def main():
        bar('0')
    
    main()
    $ python err.py
    Traceback (most recent call last):
      File "err.py", line 11, in <module>
        main()
      File "err.py", line 9, in main
        bar('0')
      File "err.py", line 6, in bar
        return foo(s) * 2
      File "err.py", line 3, in foo
        return 10 / int(s)
    ZeroDivisionError: integer division or modulo by zero

    从上到下,错误会一层层的反馈,直到显示最终出错的地方。

      try...except...finally...:常用这种方法来检查错误并捕捉到,同时进行相应的处理。

    try:
        print 'try...'
        r = 10 / 0
        print 'result:', r
    except ZeroDivisionError, e:
        print 'except:', e
    finally:
        print 'finally...'
    print 'END'
    try...
    except: integer division or modulo by zero
    finally...
    END

      注意到,错误类型有很多种,它们其实都是从BaseException类派生出来的,常见的错误类型和继承关系有:https://docs.python.org/2/library/exceptions.html#exception-hierarchy

    • 调试

      程序运行一次就成功的概率很小,基本上不超过1%。一般有如下的调试方法:

      第一种,直接在可能出错的地方print出来,但是后期会一个个删掉。

      第二种,使用断言来代替。

    # err.py
    def foo(s):
        n = int(s)
        assert n != 0, 'n is zero!'
        return 10 / n
    
    def main():
        foo('0')

    断言中,如果“n != 0”是错的,就抛出AssertionError,并显示后面的字符串。

      第三种,使用logging。

      logging有debug,info,warning,error等几个级别,从前到后优先级依次提高,即如果,指定level=WARNING后,debug和info就不起作用了。这样一样,就可以输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。

      logging的另一个好处是通过简单的配置,一条语句可以同时输出到不同的地方,,比如console和文件。

    # err.py
    import logging
    logging.basicConfig(level=logging.INFO) s
    = '0' n = int(s) logging.info('n = %d' % n) print 10 / n
    $ python err.py
    INFO:root:n = 0
    Traceback (most recent call last):
      File "err.py", line 8, in <module>
        print 10 / n
    ZeroDivisionError: integer division or modulo by zero

      第四种调试方式,就是调试器pdb,让程序以单步方式运行,可以随时查看运行状态。

    # err.py
    s = '0'
    n = int(s)
    print 10 / n

    然后,以参数-m pdb启动,单步运行

    $ python -m pdb err.py
    > /Users/michael/Github/sicp/err.py(2)<module>()
    -> s = '0'
    (Pdb) l
      1     # err.py
      2  -> s = '0'
      3     n = int(s)
      4     print 10 / n
    [EOF]

    输入n单步运行下一步

    (Pdb) n
    > /Users/michael/Github/sicp/err.py(3)<module>()
    -> n = int(s)
    (Pdb) n
    > /Users/michael/Github/sicp/err.py(4)<module>()
    -> print 10 / n

    输入p 变量名来查看变量状态。

    (Pdb) p s
    '0'
    (Pdb) p n
    0

    输入q结束运行

    (Pdb) n
    ZeroDivisionError: 'integer division or modulo by zero'
    > /Users/michael/Github/sicp/err.py(4)<module>()
    -> print 10 / n
    (Pdb) q

      另外在合适的地方,设置pdb.set_trace(),可以作为断点。

    # err.py
    import pdb
    
    s = '0'
    n = int(s)
    pdb.set_trace() # 运行到这里会自动暂停
    print 10 / n
    $ python err.py 
    > /Users/michael/Github/sicp/err.py(7)<module>()
    -> print 10 / n
    (Pdb) p n
    0
    (Pdb) c
    Traceback (most recent call last):
      File "err.py", line 7, in <module>
        print 10 / n
    ZeroDivisionError: integer division or modulo by zero

    到达断点时,进入pdb调试器。

      最后,还有方便的IDE调试器。

    • 单元测试

      单元测试,顾名思义,就是对一个部分测试,可以是一个模块、一个函数或者一个类。它的目的是保证该单元能够实现原先规划的功能,为之后的整体调试做准备。

      例如,现有模块mydict.py,对它的要求是实现如下功能:

    >>> d = Dict(a=1, b=2)
    >>> d['a']
    1
    >>> d.a
    1

    mydict.py代码如下:

    class Dict(dict):
    
        def __init__(self, **kw):
            super(Dict, self).__init__(**kw)
    
        def __getattr__(self, key):
            try:
                return self[key]
            except KeyError:
                raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    
        def __setattr__(self, key, value):
            self[key] = value

    编写的单元测试,需要引入unittest模块,编写的mydict_test.py如下:

    import unittest
    
    from mydict import Dict
    
    class TestDict(unittest.TestCase):
    
        def test_init(self):  # 测试初始化功能
            d = Dict(a=1, b='test')
            self.assertEquals(d.a, 1)
            self.assertEquals(d.b, 'test')
            self.assertTrue(isinstance(d, dict))
    
        def test_key(self):  # 测试key的功能
            d = Dict()
            d['key'] = 'value'
            self.assertEquals(d.key, 'value')
    
        def test_attr(self):  # 测试属性功能
            d = Dict()
            d.key = 'value'
            self.assertTrue('key' in d)
            self.assertEquals(d['key'], 'value')
    
        def test_keyerror(self):  # 测试key错误的功能
            d = Dict()
            with self.assertRaises(KeyError):
                value = d['empty']
    
        def test_attrerror(self):  # 测试属性错误的功能
            d = Dict()
            with self.assertRaises(AttributeError):
                value = d.empty

      编写的单元测试类,从unittest.TestCase继承。其中,只有以test开头的方法是测试方法。

      运行单元测试时,可以在测试文件中加入:

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

    然后run。

      另一种,在命令行中输入命令:

    $ python -m unittest mydict_test
    .....
    ----------------------------------------------------------------------
    Ran 5 tests in 0.000s
    
    OK

    第二种方法,可以一次运行多个测试文件,比较方便。

      setUp与tearDown:在每个测试方法前后分别被执行,避免在测试代码中重复加入代码。

      最后,单元测试要考虑到异常,代码不能过于复杂,以免本身就有bug。

    • 文档测试

      Python中可以提供实例文档,在文件中编写特定格式的注释,调用doctest判断程序是否会像注释中那样的运行。

    class Dict(dict):
        '''
        Simple dict but also support access as x.y style.
    
        >>> d1 = Dict()
        >>> d1['x'] = 100
        >>> d1.x
        100
        >>> d1.y = 200
        >>> d1['y']
        200
        >>> d2 = Dict(a=1, b=2, c='3')
        >>> d2.c
        '3'
        >>> d2['empty']
        Traceback (most recent call last):
            ...
        KeyError: 'empty'
        >>> d2.empty
        Traceback (most recent call last):
            ...
        AttributeError: 'Dict' object has no attribute 'empty'
        '''
        def __init__(self, **kw):
            super(Dict, self).__init__(**kw)
    
        def __getattr__(self, key):
            try:
                return self[key]
            except KeyError:
                raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    
        def __setattr__(self, key, value):
            self[key] = value
    
    if __name__=='__main__':
        import doctest
        doctest.testmod()

    然后run。如果什么都没输出,就说明编写的doctest运行都是正确的。

      注:本文为学习廖雪峰Python入门整理后的笔记

  • 相关阅读:
    python自动化测试学习笔记-8单元测试unittest模块
    python自动化测试学习笔记-8多线程
    python自动化测试学习笔记-7面向对象编程,类,继承,实例变量,邮件
    python自动化测试学习笔记-6excel操作xlwt、xlrd、xlutils模块
    android 拍照上传文件 原生定位
    golang 固定worker 工作池
    小程序报错x509: certificate signed by unknown authority
    PostgreSQL 添加postgis插件实现获取经纬度间的距离
    微信小程序使用mqtt mpvue mosquito
    golang 调用顺丰API接口测试
  • 原文地址:https://www.cnblogs.com/likely-kan/p/7517924.html
Copyright © 2020-2023  润新知