第7章 调试与测试
7.1 调试方法
7.1.1 利用 print调试程序
-
当程序输出结果与预期不一致时,在可能出错的位置利用
print语句输出关键的变量,查看其取值与预期是否一致 -
例:可变默认参数陷阱
1 2 3 4 | |
1 | |
1 | |
1 | |
1 | |
1 | |
- 可以怀疑问题出在
lst参数上
1 2 3 4 5 | |
1 | |
1 2 3 4 5 6 7 | |
1 | |
1 2 3 4 5 6 7 | |
- 原因:可变参数的默认值也是一个变量,而且多次调用函数使用的是同一个默认参数
7.1.2 利用 logging调试程序
print的缺点- 调试的
print语句需删除或注释掉 - 与其他输出信息混杂在一块影响输出结果的判断
logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等- 可以通过设置不同的日志等级来对输出信息进行控制;
- 可以定制输出信息的格式;
-
可以方便地将不同类型的输出信息输出到不出的位置,例如文件
-
Logger对象 logging模块的功能主要通过Logger对象实现Logger对象不用直接实例化,利用getLogger函数可以获取一个Logger对象getLogger函数有一个参数name,用于指定Logger对象的名称。相同的name会返回同一个Logger对象Logger对象的主要方法fatalcriticalerrorwarninfo-
debug -
basicConfig用于配置Loger对象,主要参数包括 level:日志信息的等级format:日志信息输出格式- 日志信息的等级
FATAL:致命错误;CRITICAL:特别严重的错误,如内存耗尽、磁盘空间为空等,一般很少使用;ERROR:一般的错误,如输入/输出操作失败;WARNING:发生很重要的事件,但是并不是错误时,如用户登录密码错误INFO:处理请求或者状态变化等日常事务-
DEBUG:调试过程中使用DEBUG等级 -
格式化字符串
| 属性名称 | 格式 | 说明 |
|---|---|---|
name |
%(name) | Logger对象名 |
asctime |
%(asctime)s | 精确到毫秒的日志事件时间 |
filename |
%(filename)s | 日志文件名 |
pathname |
%(pathname)s | 日志文件的全路径名称 |
funcName |
%(funcName)s | 日志输出所在的函数 |
levelname |
%(levelname)s | 日志的等级 |
levelno |
%(levelno)s | 日志等级信息 |
lineno |
%(lineno)d | 日志输出在代码中的行号 |
module |
%(module)s | 日志输出所在的模块名 |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
1 2 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
1 2 | |
7.1.3 pdb调试器
pdb是Python内置的交互式调试器,它支持在源代码行级别设置断点、单步执行、列出源代码、查看变量值等功能- 运行
pdb调试器的方法 - 命令方式:在命令行中执行
python -m pdb source_code.py pdb.set_trace():在源代码中导入pdb模块,然后在代码中希望设置断点的位置放置pdb.set_trace()-
breakpoint(): 使用方法与pdb.set_trace相似。但是,breakpoint是一个内置函数,不必导入pdb模块 -
pdb常用命令
| 命令 | 命令简写 | 功能 |
|---|---|---|
list |
l |
列出代码 |
next |
n |
单步执行,不进入函数内部 |
step |
s |
单步执行,进入函数内部 |
where |
w |
查看所在的位置 |
print |
p |
输出变量值 |
args |
a |
打印当前函数的参数 |
continue |
c |
继续运行,直到遇到断点或者程序结束 |
return |
r |
一直运行到函数返回 |
jump |
j |
跳转到指定行数运行 |
break |
b |
添加断点/列出断点 |
clearl |
cl |
清除断点 |
disable |
d |
禁用断点 |
enable |
-- | 启用断点 |
tbreak |
-- | 设置临时断点(断点处只中断一次) |
condition |
-- | 条件断点 |
help |
h |
帮助 |
quit |
q |
退出pdb |
利用IDE调试
- 常用的Python开发工具或IDE环境如PyCharm、Spyder、PyDev、VSCode等都有强大的调试功能,便用更加简单便捷
7.2 异常处理
7.2.1 异常的原因
- 程序运行过程中由于没有考虑到的意外情况而引发的错误
- 常见导致异常的原因
- 数据类型不匹配、文件不存在、网络连接错误、除运算中分母为零、下标越界等
- Python解释器在检测到异常之后,会根据异常的类型及异常信息,将其包装成一个异常对象
1 | |
1 2 3 4 5 6 7 8 9 | |
- 主动抛出异常
raise
1 | |
1 2 3 4 5 6 7 8 9 | |
1 | |
1 2 3 4 5 6 7 8 9 | |
7.2.2 断言
assert语句- 用于判断一个表达式是否为真
- 当表达式为
True对程序的执行没有影响,当表达式False时触发AssertionError异常 - 语法形式
相当于
1assert 表达式 [, 异常信息]1 2
if not expression: raise AssertionError
1 2 | |
1 | |
1 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
7.2.3 异常处理
异常处理的过程
- 异常处理基本方式
1 2 3 4
try: 语句块1 except 异常类型: 语句块2 - 异常处理的过程
- 首先,执行“语句块1”中的语句。
- 若没有发生异常则跳过
except子句,执行后续代码; - 若发生异常,则跳过“语句块1”的其余代码,执行
except子句; except子句判断“语句块1”中发生的异常的类型与“异常类型”是否一致- 若不一致则继续抛出异常,程序崩溃退出;
- 若一致,则执行“语句块2”。“语句块2”中往往会输出错语信息,若有必要会中断程序运行,或者转而调用其他函数;
- 当
except子句执行完之后,继续执行后续代码。
1 2 3 4 5 6 | |
1 | |
1 | |
1 | |
1 2 3 4 5 6 7 | |
获取异常实例
except子句中可使用as来获取异常类的实例
1 2 3 4 5 6 | |
1 | |
1 2 3 4 5 6 7 8 | |
捕获多种异常
- 下面的代码,可能触发的异常有
ZeroDivisionError和AssertionError
1 2 3 | |
- 使用更高级别的异常类
ZeroDivisionError是ArithmeticError的子类,而ArithmeticError是Exception的子类;AssertionError也是Exception的子类- 这种方法往往也会捕获到预期之外的异常,从而使得程序中的相关错误无法被发现
1 2 3 4 5 6 | |
1 | |
1 | |
1 | |
1 | |
- 异常元组
1 2 3 4 5 6 | |
- 使用多个
except子句
1 2 3 4 5 6 7 8 9 10 | |
else子句
1 2 3 4 5 6 | |
else子句中的“语句块3”
1 2 3 4 5 6 7 8 9 | |
1 2 3 4 5 6 7 8 | |
finally子句
1 2 3 4 5 6 | |
finally子句中的“代码块4”无论“语句块1”是否出现异常都会被执行
7.2.4 异常的类型
内置异常类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
自定义异常
- 自定义异常类通常派生自
Exception类,并利用raise语句在满足一定条件的情况下主动触发 - 大多数自定义异常类都仅用于确定程序错误的原因并显示异常信息,往往不需要定义复杂的功能
1 2 3 4 5 6 | |
1 | |
1 | |
1 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
7.3 单元测试*
7.3.1 单元测试的概念及工具
- 单元测试(Unit Testing)是软件开发中使用的一种重要的自动化测试方法,也是测试驱动开发的必要过程
- 测试单元
- 在单元测试中,每个测试单元仅关注一个较小的、独立的功能
- 在面向过程编程中,测试单元可以是一个程序、函数或者过程
- 在面向对象编程中,常常以方法作为测试单元。
常用单元测试框架
unittest- Python标准库中自带的单元测试框架
1 2 3 4 5 6 7 8 9 10 11 | |
nosenose是Python的一种第三方测试框架pip install nose
1 2 3 4 5 6 7 8 9 10 | |
py.testpip install pytest
1 2 3 4 5 6 7 8 9 10 11 | |
7.3.2 unittest基础
- 测试用例(Test Case)
- 测试用例是独立的测试流程。在测试用例中,向测试目标输入特定的数据,检查返回结果与预期是否一致来验证程序的正确性
- 测试设施(Test Fixture)
- 测试设施用于搭建和清理测试环境。测试用例中不同的测试方法可能会需要一些共用的资源,例如文件、数据库连接、输入数据等。测试设施在测试方法执行之前准备这些资源,并在测试方法运行结束后进行清理
- 测试套件(Test Suite)
- 是一组测试用例或者其他测试套件的集合
- 测试加载器(Test Loader)
- 用于从类和模型中创建测试套件
- 测试运行器(Test Runner)
-
负责执行测试并控制测试结果输出
-
unittest模块中常用的类或函数包括: unittest.TestCase- 所有测试用例类的基类;
unittest.main()- 该函数可以将一个单元测试模块变为可直接运行的测试脚本,它使用
TestLoader类来搜索所有包含在该模块中命名以test开头的测试方法并自动执行他们。执行的默认顺序是根据方法名的ASCII码顺序;
- 该函数可以将一个单元测试模块变为可直接运行的测试脚本,它使用
unittest.TestSuite- 测试套件类;
unittest.TextTestRunner- 该类中的
run方法运行测试套件中的测试用例;
- 该类中的
unittest.defaultTestLoader- 该类中的
discover方法根据匹配条件自动搜索测试目录中的测试用例文件,并将查找到的测试用例组装为测试套件;
- 该类中的
unittest.skip- 装饰器,用于屏蔽测试用例中的暂时不需执行的测试方法。
7.3.3 创建测试用例
-
通过继承自
unittest.TestCase类可创建一个测试用例 -
文件
math_method.py
1 2 3 4 5 | |
- 文件
test_case.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
- 常用断言方法
| 断言方法 | 功能 |
|---|---|
assertEqual(a, b) |
a == b |
assertNotEqual(a, b) |
a != b |
assertTrue(x) |
bool(x) is True |
assertFalse(x) |
bool(x) is False |
assertIs(a, b) |
a is b |
assertIsNot(a, b) |
a is not b |
assertIsNone(x) |
x is None |
assertIsNotNone(x) |
x is not None |
assertIn(a, b) |
a in b |
assertNotIn(a, b) |
a not in b |
assertIsInstance(a, b) |
isinstance(a, b) |
assertNotIsInstance(a, b) |
not isinstance(a, b) |
assertRaises(exc, fun, *args, **kwds) |
fun(*args, **kwds)抛出异常 exc |
assertGreater(a, b) |
a > b |
assertGreaterEqual(a, b) |
a >= b |
assertLess(a, b) |
a < b |
assertLessEqual(a, b) |
a <= b |
assertListEqual(a, b) |
列表a与b相等 |
assertTupleEqual(a, b) |
元组a与b相等 |
assertSetEqual(a, b) |
集合a与b相等 |
assertDictEqual(a, b) |
字典a与b相等 |
7.3.4 运行测试用例
- 模块内执行
-
在测试用例所在的模块中添加如下代码,直接运行模块
1 2
if __name__ == '__main__': unittest.main() -
命令行运行
python -m unittest test_module- 扩展名
.py可以省去
- 扩展名
python -m unittest test_module.TestCaseClass
7.3.5 测试套件的创建与执行
- 测试代码
1 2 3 4 5 6 7 8 9 10 | |
- 运行结果
1 2 3 4 5 6 7 8 | |
- 利用测试套件控制测试方法的执行顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
7.3.6 测试设施
unittest中测试用例的测试设施通过方法setUp和tearDown实现setUp方法在所有测试方法执行之前运行,用于准备所有测试方法共同的资源tearDown方法在所有测试方法执行毕后执行,用于作一些清理工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
7.4 文档测试
- doctest
- 是Python标准库自带的一种测试工具,可以用于简单的单元测试
- 其工作原理是在函数或类的文档字符串中寻找测试用例并执行,比较输出结果与期望值是否相匹配
7.4.1 文档测试用例
- 文档字符串中“
>>>”符号之 后为待执行的测试代码,紧接着为测试代码的预期输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
7.4.2 运行文档测试
- 模块内运行
- 在模块中添加如下代码,即可运行文档测试
1 2 3 | |
- 命令行运行
python -m unittest doc_test_module- 扩展名
.py不是必须的
- 扩展名
本页面的全部内容在 生信资料 bio.0594codes.cn 和 莆田青少年编程俱乐部 0594codes.cn 协议之条款下提供,附加条款亦可能应用