python (4) - 错误处理、调试、单元测试、文档测试

python 基础知识整理学习

继上一篇,python基础知识(三)

错误处理、调试与测试

提前捕获并处理代码中可能出现的错误,使代码更健壮。

错误处理

代码中报错会中断程序,不会继续执行。

# 打印输出未定义的变量,报错后不会输出后面的数字
print(a)
print(2)

输出错误:
在这里插入图片描述
try…except…finally
错误捕获,记录错误,方便查询

try:
    print(a)
except NameError as e:
    print("变量未声明,请检查")
finally:
    print("继续执行!")
print(2)

说明:

  • 刚才的错误类型为NameError,使用except语句捕获并处理;
  • 所有的错误类型都继承BaseException基类,错误捕获得从子类到父类,层层向上捕获。传递门
  • 错误会被层层传递,主要在于函数的调用,在哪调用错误会先从哪抛出,一直抛给系统,程序终止。

logging记录
通过logging可以把错误记录到错误日志中,方便日后查看。也可以打印出来,程序不会被终止。

# logging 记录日志
import logging
try:
    print(10/0)
except ZeroDivisionError as e:
    logging.exception(e)

print(0/10)

说明:

  • logging类型记录包括:debug/error/info/warning
  • 默认的logging.info()是不会输出的。通过配置logging.basicConfig(level=logging.INFO)

自定义错误
系统内置的错误类型不能满足工作,还可以自定义错误类型,并抛出。

# 自定义错误类型
class ConflictValueError(ValueError):
    pass
name = input("please input your name \n")
try:
    if name == "admin":
        raise ConflictValueError("you can't use this name")
except ConflictValueError as e:
    logging.exception(e)

print("ok")

说明:

  • 尽可能使用系统内置的错误类型,自定义类型继承系统的某一个类型错误BaseException
  • 错误捕获处理一定是有助于问题排查。
调试

功能测试,问题排查。

  • print()打印结果输出,标记;
  • 断言assert,断言失败会抛出AssertionError错误
    # 断言
    assert name!="test","测试"
    
    print("大神,你好")
    
    说明:
    • 断言抛出错误后,程序终止执行
    • 通过执行时加入参数python -O four.py忽略断言。
  • logging日志输出记录。
  • 调试器pdb
    python -m pdb **.py启动调试,按步执行。
    说明:
    • l查看代码,会显示当前执行位置。
    • n执行下一步。
    • p 变量名来查看已执行的语句中的变量的值。
    • q结束退出。
    • c程序开始正常执行。
  • pdb.set_trace()设置断点,程序执行到这停止。
    需要导入import pdb
单元测试

编写一个简单的用户名校验类,自定义各种错误类型,放置在单独的文件four_form.py

# 自定义表单校验错误类
class ConflictValueError(ValueError):
    pass
class ExistError(ValueError):
    pass
class PureNumberError(ValueError):
    pass
class LenError(ValueError):
    pass
# 表单用户名校验 、 不能是纯数字、不能有特殊符号、必须6位以上
import re
class ValidForm():
    def __init__(self,**ot):
        self.form = ot
    def exist_name(self):
        form = self.form
        if "name" in form:
            pass
        else:
            raise ExistError("必须填写用户名!") 
    def conflict_name(self):
        form = self.form
        self.exist_name()
        if form["name"] in ["admin","test","login"]:
            raise ConflictValueError("你不能使用这个名字")
    def pureNumber_name(self):
        form = self.form
        self.conflict_name()
        if re.search(r'^\d+$',str(form["name"]))!=None:
            raise PureNumberError("不能是纯数字!")
    def len_name(self):
        form = self.form
        self.pureNumber_name()
        if len(form["name"]) <6:
            raise LenError("用户名不少于6个字符!")
    def isAvailable(self):
        self.len_name()
        return True
# 测试方法
# ValidForm(**{"name":"232"}).isAvailable()

编写测试函数,单独的文件four_test.py

import unittest

from four import ValidForm,ExistError,ConflictValueError,LenError

class TestValidForm(unittest.TestCase):
    # 在每个测试方法开始前调用 before
    def setUp(self):
        print("start")
    def test_existError(self):
        with self.assertRaises(ExistError):
            ValidForm().exist_name()

    def test_conflicError(self):
        with self.assertRaises(ConflictValueError):
            ValidForm(**{"name":"admin"}).conflict_name()
	@unittest.skip("将不会被调用")
    def test_lenError(self):
        with self.assertRaises(LenError):
            ValidForm(**{"name":"abc"}).len_name()
    # 方法调用后执行
    def tearDown(self):
        print("end")

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

说明:

  • 依赖模块unittest,需导入;
  • 不同文件中,需要调用用到的模块from ... import **
  • unittest.main()提供了在命令行运行测试脚本的接口,直接运行python four_test.py
  • setUp() 每个测试方法执行前都会去调用;tearDown()测试方法执行完后调用
  • unittest内部提供了断言的方法,如assertRaises()/assertEqual()/assertTrue(),
  • 注解可以改变方法的表现,比如@unittest.skip() 使得这个测试方法不会被调用;
文档测试

以注释的方式写测试用例,然后指定执行注释中可执行的代码:

import re
class ValidForm():
    '''
    valid name
    >>> ValidForm().exist_name()
    Traceback (most recent call last):
    ...
    ExistError: 必须填写用户名!
    >>> ValidForm(**{"name":"admin"}).conflict_name()
    Traceback (most recent call last):
    ...
    ConflictValueError: 你不能使用这个名字
    >>> ValidForm(**{"name":"admin_123"}).isAvailable()
    True
    '''
   def __init__(self,**ot):
        self.form = ot
   # 省略.....

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

说明:

  • >>> **必须有空格,才会当做可执行命令执行;
  • 期望一定要和执行的结果一致;不一致时会报错Expected: ... Got: ...
  • 当符合期望时,执行没有任何输出。改错一处ValidForm(**{"name":"ad_min"}).conflict_name()
    执行结果和期待的不一致:
    在这里插入图片描述
  • doctest只会在命令行模式下执行。>>>标识决定了执行环境。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

heroboyluck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值