Python的错误调试以及单元测试unittest

错误处理:

程序运行的过程中如果发生了错误,就可以事先约定返回一个错误代码,但是用错误码表示是否出错十分的不方便,所以高级语言通常都内置了一套try...except...finally的错误处理机制,Python也有这样的错误处理机制:

try:
    print('try...')
    r = 10 / 0
    print('result:', r)
except ZeroDivisionError as e:  # try运行过程中如果哪一行出错,就不会执行后续代码,直接跳转到这个代码块
    print('except:', e)
except ZeroDivisionError as e:  # 可以有多个except来捕获不同的错误
    print('ZeroDivisionError:', e)
else:  #当没有错误发生的时候,会执行else语句块
    print('no error!')
finally:  # 如果有finally语句块,就不管对错一定会执行这段代码
    print('finally...')
print('END')

Python的错误是class,所有的错误都继承自BaseException,所以每次捕获一个类型的错误,也会捕获这个类型错误的所有子类:
点击这里看错误类型和继承关系

记录错误使用logging模块:
既然我们能捕获错误,那么我们就可以把错误堆栈记录下来,事后分析错误原因,同时让程序继续执行下去,Python内置的logging模块就可以记录错误信息。

import logging

def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    try:
        bar('0')
    except Exception as e:  # 程序打印完错误会继续执行并正常退出
        logging.exception(e)

main()
print('END')

logging可以通过配置把错误记录到日志文件里面

抛出错误:
捕获错误就是捕获到一个错误class的一个实例,所以错误都是有意创建并抛出的,所以我们也可以使用自己编写的函数抛出错误:

class FooError(ValueError):
    pass

def foo(s):
    n = int(s)
    if n==0:
        raise FooError('invalid value: %s' % s)
    return 10 / n

foo('0')

这里注意尽量使用Python的内置错误类型


调试:

第一种方法:print
调试的最简单办法就是用print()打印出来看看,但是这样运行结果里面会包含很多垃圾信息

第二种方法:断言

def foo(s):
    n = int(s)
    assert n != 0, 'n is zero!'  # 这里断言的意思是n应该不等于0,否则就抛出AssertionError的错误,并打印后面的‘n is zero'
    return 10 / n

def main():
    foo('0')

这样输出结果还是会有很多的垃圾信息,在启动Python解释器的时候可以用-0来关闭assert:python -O err.py
这样关闭后,可以把所有的assert语句看成是pass

第三种方法:logging
logging不会输出错误,而且可以输出到文件:

import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)

logging不会抛出错误,可以输出到文件,还可以允许指定记录信息的级别,有debug, info, warning, error等几个级别,最后统一控制输出哪个级别的信息。

第四种方法:pdb 单步运行
让程序以单步运行的方式运行,可以设置断点,可以用IDE设置断点更为方便。



单元测试,unittest

单元测试用来对一个模块,函数或者类来进行正确性检验

打比方,有一个函数abs(),我们可以有一个测试用例:输入正数如1, 0.5之类的,期待返回值与输入值相同。

例子: mydict.py

class Dict(dict):

    def __init__(self, **kw):
        super().__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

然后我们编写单元测试:mydict_test.py

import unittest
from mydict import Dict
class TestDict(unittest.TestCase):  # 编写一个测试类,从unittest.TestCase继承
    def test_init(self):  # 以test开头的就是测试方法,不然就不是且不会被执行
        d = Dict(a=1, b='test')
        self.assertEqual(d.a, 1)
        self.assertEqual(d.b, 'test')
        self.assertTrue(isinstance(d, dict))

    def test_key(self):
        d = Dict()
        d['key'] = 'value'
        self.assertEqual(d.key, 'value')  # 这是最常用的断言,希望输出与我们预期值相等

    def test_attr(self):
        d = Dict()
        d.key = 'value'
        self.assertTrue('key' in d)
        self.assertEqual(d['key'], 'value')

    def test_keyerror(self):
        d = Dict()
        with self.assertRaises(KeyError):   # 另一种重要的断言就是期待抛出指定类型的error
            value = d['empty']  # 这里通过访问不存在的key时,断言会抛出KeyError

    def test_attrerror(self):
        d = Dict()
        with self.assertRaises(AttributeError):
            value = d.empty

这里最常用的断言就是assertEqual()
self.assertEqual(abs(-1), 1)

运行单元测试:
推荐使用在命令行通过参数-m unittest直接运行单元测试:
$ python -m unittest mydict_test

setUp & tearDown:
可以在单元测试中编写两个特殊的方法:setUp()tearDown 方法,这两个方法会在每调用一个测试方法的前后分别被执行,所以前者可以用来连接数据库,后者用来关闭数据库。

猜你喜欢

转载自blog.csdn.net/qq_43355223/article/details/83986068
今日推荐