笔记更新于2019年12月4日,

摘要:各种调试方法介绍assert、logging、调试器pdb;单元测试unittest的编写方法、如何运行单元测试;文档测试doctest的编写

写在前面:为了更好的学习python,博主记录下自己的学习路程。本学习笔记基于廖雪峰的Python教程,如有侵权,请告知删除。欢迎与博主一起学习Pythonヽ( ̄▽ ̄)ノ

目录

调试与测试

调试

• print

• 断言 assert

• logging

• 调试器 pdb

• pdb.set_trace( )

• IDE

单元测试 unittest

• 单元测试编写

• 单元测试方法

• 运行单元测试

• setUp( )和tearDown( )

文档测试 doctest

调试与测试

调试

程序编写的过程中会出现各种意想不到的bug,想要一次性写好并成功运行几乎不可能。我们需要知道在运行过程中哪些变量可能会出错,在编写过程中要有一套调试程序的手段来修复bug。下面介绍在Python中常见的调试手段。

• print

最简单粗暴的方法是print,只要在把可能会出错的变量打印出来即可。

def fn(s):

n = s

print('>>> n = %d' % n)

return 10 / n

def main():

fn(0)

main()

>>> n = 0

Traceback (most recent call last):

...

ZeroDivisionError: division by zero

这样我们就能知道是n = 0导致的错误。这种方法有个很大的问题就是,在程序编写完之后会留下大量的垃圾信息,不好处理。

• 断言 assert

在上面凡是用到print的地方都可以用assert来代替。

assert后面加一个判断语句,该判断为正确时程序正常运行,反之出现错误,打印紧跟的字符串。

def fn(s):

n = s

assert n != 0, '>>> n = 0'

return 10 / n

def main():

fn(0)

main()

运行结果:

Traceback (most recent call last):

...

AssertionError: >>> n = 0

assert 即声明、断言n应该不等于0,但结果n等于0,则断言失败,抛出AssertionError错误,并打印出紧跟的字符串'>>> n = 0'。

相比print,assert可以通过-O参数来关闭,关闭之后所有的assert语句相当于pass。如把上面代码保存为err.py文件,在python解释器中运行:

python -O err.py

运行结果:

Traceback (most recent call last):

...

ZeroDivisionError: division by zero

可见assert被关闭了,解释器打印出了ZeroDivisionError而不是AssertionError。

• logging

logging也是可以把错误输出。

import logging

s = 0

n = s

logging.info('n = %d' % n)

print(10 / n)

运行之后发现出了ZeroDivisionError之外没有其他信息。这是因为logging的信息输出是有级别限制的,我们需要设置级别。

在import logging之后加上:

logging.basicConfig(level=logging.INFO)

这时候再运行:

INFO:root:n = 0

Traceback (most recent call last):

...

ZeroDivisionError: division by zero

显示的错误信息就有logging.info之后的信息了。

logging的好处是允许你指定记录信息的级别。一共有五个级别,从小到大依次是:debug,info,warning,error,critical。默认的level是warning,只有高于等于warning级别的信息才会被打印,当然可以修改level,这就是为什么一开始没有打印信息。

logging的另一个好处是可以通过简单的配置,把信息输出到文件中(后面会介绍如何配置)。

• 调试器 pdb

pdb,即python debugger,Python调试器。调试器可以让程序逐步运行,并查看运行状态。

先写一个简单的py文件:

# err.py

s = '0'

n = int(s)

print(10 / n)

在命令行模式以参数-m pdb启动err.py:

python -m pdb err.py

启动pdb之后,会自动定位到下一步要执行的代码“s = '0'”,然后在(pdb)之后等待输入。

输入字母l命令可以查看err.py的全部代码:

(Pdb) l

1 # err.py

2 -> s = '0'

3 n = int(s)

4 print(10 / n)

输入字母n可以单步执行代码:

(Pdb) n

> c:\users\administrator\err.py(3)()

-> n = int(s)

(Pdb) n

> c:\users\administrator\err.py(4)()

-> print(10 / n)

输入字母p加变量名可以查看该变量:

(Pdb) p s

'0'

(Pdb) p n

0

输入字母q结束调试:

(Pdb) q

• pdb.set_trace( )

set_trace即放置断点。在可能出错的地方添加语句pdb.set_trace()就可以设置一个断点

# err.py

import pdb

s = '0'

n = int(s)

pdb.set_trace() # 运行到这里会自动暂停

print(10 / n)

当代码运行到pdb.set_trace()时,就会暂停并自动进入pdb调试环境。可执行上面介绍的命令进行调试。输入c继续运行。

这种方法比pdb的单步调试效率要高一点。

• IDE

Integrated Development Environment,集成开发环境,即IDE。用于程序开发环境的应用程序。

IDE一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。

目前比较好的python IDE有以下几种:

pycharm

Eclipse + PyDev

Visual Studio + PTVS

单元测试 unittest

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

比如我们想要测试abs函数,给出下面的测试用例:

输入正数,1、100、0.1,期待输出1、10、0.1;

输入负数,-1、-100、-0.1,期待输出1、10、0.1;

输入0,期待输出0;

输入非数值类型,'a'、[ ]、{ },期待输出TypeError

如果abs函数通过了上面的测试,我们就认为abs函数是能够正常运行的。

在编写程序时,有一个测试用例,让程序的行为始终符合测试用例的逻辑,那么就能极大可能地保证程序的正确性。

下面引用廖雪峰官方网站里面的一个例子,来介绍Python中单元测试的编写。

• 单元测试编写

如我们想编写一个Dict类,用途与内置的dict一样,但可以通过属性来访问,像这样:

>>> d = Dict(a=1, b=2)

>>> d['a']

1

>>> d.a

1

于是编写一个mydict:

class Dict(dict):

def __init__(self, **kw):

super().__init__(**kw) # Dict类的实例属性继承dict

def __getattr__(self, key): # 当访问实例key属性,而没有key属性时,调用该方法

try:

return self[key] # 尝试返回键key对应的值value

except KeyError:

raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

# 若返回失败,显示Dict没有该属性

def __setattr__(self, key, value): # 设置一个赋值方法setattr

self[key] = value

然后开始编写单元测试模块mydict_test.py。

首先我们需要引入Python自带的unittest模块和编写DIct模块,然后定义一个测试类,这个类继承unittest.TestCase

import unittest

from mydict import Dict

class TestDice(unittest.TestCase):

pass

• 单元测试方法

接下来是写测试方法,对每一类测试都需要编写一个test_xxx()方法,以test开头的方法是测试方法,不以test开头的方法不被认为是测试方法,测试的时候是不会被执行。

在继承的unittest.TestCase中有许多内置的条件判断方法,我们直接调用来进行测试。下面是三个常用的测试方法:

assertEqual( )传入两个参数,一个是需要执行的对象,一个是对象执行后期望返回的结果。

self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等

assertTrue( ),期望括号里面返回的值为True。

self.assertTrue(abs(-1) == 1)

assertRaises( ):,期望冒号后的语句抛出括号里面的错误。

with self.assertRaises(TypeError):

abs('a')

我们用上面的三种测试方法来编写mydict_test.py (以下代码部分除注释外转自廖雪峰的官方网站)

import unittest

from mydict import Dict

class TestDict(unittest.TestCase):

def test_init(self): # 测试Dict的实例

d = Dict(a=1, b='test') # 创建一个Dict的实例

self.assertEqual(d.a, 1) # 测试是否能通过属性来访问值

self.assertEqual(d.b, 'test') # 测试是否能通过属性来访问值

self.assertTrue(isinstance(d, dict)) # 测试实例d是否为dict类

def test_key(self): # 测试key属性

d = Dict() # 创建一个Dict的实例

d['key'] = 'value' # 给d添加键key和值value

self.assertEqual(d.key, 'value') # 测试能否通过属性key来访问值value

def test_attr(self): # 测试setattr方法

d = Dict()

d.key = 'value'

self.assertTrue('key' in d)

self.assertEqual(d['key'], 'value')

def test_keyerror(self): # 测试访问不存在的key时,打印KeyError

d = Dict()

with self.assertRaises(KeyError):

value = d['empty']

def test_attrerror(self): # 测试通过属性访问不存在的key时,打印AttributeError

d = Dict()

with self.assertRaises(AttributeError):

value = d.empty

自此我们完成了单元测试的编写,接下来就可以运行单元测试了。

• 运行单元测试

运行单元测试有两种方法,一种是在单元测试模块最后加上两行代码:

if __name__ == '__main__':

unittest.main()

然后直接运行mydict_test.py即可。

另一种是在命令行模式中通过参数-m unittest运行单元测试:

python -m unittest mydict_test

.....

----------------------------------------------------------------------

Ran 5 tests in 0.000s

OK

推荐后一种方法,因为这样可以批量运行多种单元测试。

通过测试的话就会打印出“OK”。

• setUp( )和tearDown( )

补充两个单元测试方法setUp( )和tearDown( )。这两个方法会分别在每调用一个测试方法的前后分别被执行。如在上面的单元测试中,我们加上这两个方法:

class TestDict(unittest.TestCase):

def setUp(self):

print('setUp...')

def tearDown(self):

print('tearDown...')

输出结果就变成:

python -m unittest mydict_test

.....

setUP...

tearDowm...

setUP...

tearDowm...

setUP...

tearDowm...

setUP...

tearDowm...

setUP...

tearDowm...

.

-------------------------------------------------------------------

Ran 5 tests in 0.000s

OK

可见每个方法都前后执行了一次setUp( )和tearDown( )。

这有什么用呢?比如我们的测试方法是需要连接数据库,每个测试方法都要添进行连接与关闭数据库的操作就很麻烦,这时候可以通过setUp( )来连接数据库和tearDown( )来关闭数据库,从而减少了许多重复的代码。

文档测试 doctest

Python中内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。

比如我们写这样一段代码:

def abs(n):

'''

Function to get absolute value of number.

Example:

>>> abs(1)

1

>>> abs(-1)

1

>>> abs(0)

0

>>> abs('a')

Traceback (most recent call last):

...

TypeError: bad operand type for abs(): 'str'

'''

return n if n >= 0 else (-n)

通过单引号'''与‘’’括起的内容是注释,此时我们可以通过doctest模块来进行测试。

doctest会严格按照Python交互式命令行的输入和输出来判断测试结果是否正确。

遇到>>>时即开始执行代码,有指示符>>>为输入,没有指示符>>>的为输出。当测试异常时,可用...来表示其中的输出。

我们来编写test.py并进行文档测试。

# -*- coding: utf-8 -*-

def fact(n):

'''

Calculate 1*2*...*n

>>> fact(1) #这里输入fact(1)

1 #期待输出的值为1

>>> fact(10)

3628800

>>> fact(-1)

Traceback (most recent call last): #当遇到错误时期待输出的错误信息

...

ValueError

'''

if n < 1:

raise ValueError()

if n == 1:

return 1

return n * fact(n - 1)

if __name__ == '__main__':

import doctest

doctest.testmod()

需要注意的是,在第一个>>>之前可以进行函数的描述,在>>>之后的内容就会被doctest执行。

最后三行代码表示,只有在命令行直接运行该文件时会进行测试,而被引用时不会进行测试,所以不必担心doctest会在非测试环境下运行。

运行这个文件会发现什么都没有输出,这就说明编写的doctest运行都是正确的。如果测试代码与函数运行结果不一致,则会出错,比如我们将

>>> fact(10)

3628800

改为:

>>> fact(10)

362880

运行结果:

**********************************************************************

File "test.py", line 11, in __main__.fact

Failed example:

fact(10)

Expected:

362880

Got:

3628800

**********************************************************************

1 items had failures:

1 of 3 in __main__.fact

***Test Failed*** 1 failures.

显示fact(10)的期待值为362880,而实际值为3628800。

以上就是本节的全部内容,感谢你的阅读。

下一节内容:20.IO编程

有任何问题与想法,欢迎评论与吐槽。

和博主一起学习Python吧( ̄▽ ̄)~*

python文档测试_【Python入门】19.调试器pdb、单元测试unittest和文档测试doctest相关推荐

  1. 第三百六十二节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)基本的索引和文档CRUD操作、增、删、改、查...

    第三百六十二节,Python分布式爬虫打造搜索引擎Scrapy精讲-elasticsearch(搜索引擎)基本的索引和文档CRUD操作.增.删.改.查 elasticsearch(搜索引擎)基本的索引 ...

  2. 【Python基础】Python调试器pdb

    Python调试器pdb 1. pdb简介 2. pdb调试 2.1 pdb常用命令 2.2 pdb实例 更新历史: 2022年12月6日完成初稿 最近在写项目代码,其中需要在Vscode上写pyth ...

  3. 【Python】Python调试器pdb

    Python调试器pdb使用 PDB调用启动方法 PDB常用命令 PDB调用启动方法 pdb有2种用法: 非侵入式(不用额外修改源代码,在命令行下直接运行就能调试) python3 -m pdb fi ...

  4. python时钟编程教程_编程入门16:Python时间操作

    Python标准库包含有一个time模块用于基本的时间处理,其中的time()函数会读取系统时钟并返回float类型的Unix纪元"时间戳"(Timestamp),即当前时间距离国 ...

  5. python数据科学手册_小白入门Python数据科学

    前言 本文讲解了从零开始学习Python数据科学的全过程,涵盖各种工具和方法 你将会学习到如何使用python做基本的数据分析 你还可以了解机器学习算法的原理和使用 说明 先说一段题外话.我是一名数据 ...

  6. python查看excel编码格式_[Python]实现处理读写xlsx xls excel文件格式(含中文处理方法)...

    最近有个需求要处理excel 格式的数据,数据量比较大.用传统的语言似乎不太好处理,于是改用python实现,这里记录一下实现过程. 首先,科普一下xlsx xls的excel文件区别是什么. xls ...

  7. python输出文本居中_#python PIL ImageDraw text 文本居中#

    python pip pil有什么东西 你所问的问题实是属1.先参考[教程]Python中的内置的和方的模块搞懂PIL是属于第三方Python模块2.再参考:[待完善][总结]Python安装第三方的 ...

  8. python 打包 小文件_[Python][小知识][NO.5] 使用 Pyinstaller 打包成.exe文件

    1.安装 pyinstaller 插件 cmd命令:pip install PyInstaller PS . o.o 不知道 easy_install 的百度吧. 2.pyinstaller 简介 他 ...

  9. python 批量移动文件_[Python] 自动化办公 批量命名和移动文件

    转载请注明:陈熹 chenx6542@foxmail.com (简书号:半为花间酒) 若公众号内转载请联系公众号:早起Python 这篇文章能学到的主要内容: 基于 os glob 和 shutil ...

最新文章

  1. 02-线性结构4 Pop Sequence
  2. quality center 支持的平台
  3. 结合泛型与模板的STL.NET
  4. Android Linux内核编译调试
  5. Microsoft Speech SDK 5.1
  6. 一个回滚段收缩的实例
  7. XposedNoRebootModuleSample 不需要频繁重启调试的Xposed 模块源码例子
  8. HLSL bytecode to GLSL编译器的第一步
  9. 【Siddhi】Siddhi 4.2.40版本之前的一个Bug
  10. 乌班图服务器系统怎么配ip,Ubuntu server14-04 服务器双网卡怎么配置静态ip
  11. html js轮播图无白链接,JavaScript:100%原生js实现左右切换的轮播图(无延迟加载)...
  12. 一些设计上的基本常识
  13. 【元胞自动机】基于matlab元胞自动机模拟SEIR传播模型【含Matlab源码 2156期】
  14. bodymovin导出没有html5,AE脚本-导出json格式的Web动画工具 Bodymovin v5.7.1 + 使用教程...
  15. golang的优劣与前景分析
  16. Linux Mint系统常用软件安装
  17. java web pring log4j_springmvc之log4j
  18. PPT无法打开,打开后总是修复的解决方法
  19. 我的游戏学习日志3——三国志GBA
  20. 【系统篇 / 域】❀ 06. Windows10 加入域 ❀ Windows Server 2016

热门文章

  1. 学习Java的第一天
  2. 【数据挖掘】pandas使用手册
  3. 现在外包软件公司越来越多,说说在您身边的外包都是怎样的?
  4. 消费者购买动机的类型及对购买行为的作用
  5. 汇编工具DOSBOX和MASM的分享与使用
  6. 数据结构和常用排序算法复杂度
  7. 非抢占的高响应比优先调度算法
  8. 马云——数学1分的落榜考生
  9. git服务器 修改url,如何更改Git Remote的URL
  10. ubuntu 15.10 如何下载bt种子文件 rtorrent的安装使用方法