单元测试的目的是对一个模块、一个函数或者一个类来进行正确性检验,如果单元测试通过,说明我们测试的对象能够正常工作。如果单元测试不通过,要么测试对象有bug,要么测试条件输入不正确。下面介绍python的几种测试框架。

1. doctest

doctest是python自带的标准模块,不需要额外安装。doctest 的编写过程就仿佛你真的在一个交互式 shell(比如 idle)中导入了要测试的模块,然后开始一条条地测试模块里的函数一样。实际上有很多人也是这么做的,他们写好一个模块后,就在 shell 里挨个测试函数,最后把 shell 会话复制粘贴成 doctest 用例。
使用doctest的两种方式:嵌入到源代码中和做成独立文件。

  1. 嵌入到源代码中
    doctest 模块会搜索那些看起来像交互式会话的 Python 代码片段,然后尝试执行并验证结果。

multi.py
执行结果:python multi.py

启动测试的方式是在 main 函数里调用了 doctest.testmod() 函数。其中,verbose 参数用于控制是否输出详细信息,默认为 False,如果不写,那么运行时不会输出任何东西,除非测试 fail。

而对于 main 函数另有他用的情况,则还可以通过命令行来启动测试:

python -m doctest -v unnecessary_math.py

注意:-m 表示引用一个模块,-v 等价于 verbose=True

2)独立文件模式

如果不想(或不能)把测试用例写进源代码里,则还可以使用一个独立的文本文件来保存测试用例。如将下面的内容存为test.txt

from multiimport multiply
 multiply(2,3)
 multiply(‘baka~’,3)

运行方法可以分为在 Python shell 里运行或者在系统 shell 里运行:

import doctest
 doctest.testfile(‘test.txt’)

2. unittest

unittest 与 doctest 一样也是 Python 发行版自带的包。

使用 unittest 的标准流程为:

从 unittest.TestCase 派生一个子类
在类中定义各种以 “test_” 打头的方法
通过 unittest.main() 函数来启动测试
unittest 的一个很有用的特性是 setUp() 和 tearDown() 方法,它们提供了为测试进行准备和扫尾工作的功能,这种功能很适合用在测试对象需要复杂执行环境的情况下。当类里面定义了 setUp() 方法的时候,测试程序会在执行每条测试项前先调用此方法;同样地,在全部测试项执行完毕后,tearDown() 方法也会被调用。

编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。对每一类测试都需要编写一个test_xxx()方法。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEquals()。

run: python test_um_test.py

如果我们写了很多个测试文件,当我们做回归测试的时候,一个一个地执行这些测试文件就太麻烦了。TestLoader.discover() 提供了一个可以在项目目录下自动搜索并运行测试文件的功能,并可以直接从命令行调用:

python -m unittest discover -s unittest/

discover 可用的参数有 4 个(-v -s -p -t),其中 -s 和 -t 都与路径有关,如上例中提前 cd 到项目路径的话这俩参数都可以忽略。-p 是 --pattern 的缩写,可用于匹配某一类文件名。-s 用于显示 print() 函数。

3. nose

nose 不再是 Python 官方发行版的标准包,需要额外安装。但它是unittest的扩展,而且让测试变得更简单。

nose 不使用特定的格式、不需要一个类容器,甚至不需要 import nose。看上去完全就是一个普通的模块文件嘛,甚至连 main 函数都不用。这里唯一需要一点必需的语法在于,测试用例的命名仍需以 test_ 开头。

运行的话:

apt-get install python-nose
nosetests test.py
或 python -m nose test.py

另外非常棒的一点是,nosetests 兼容对 doctest 和 unittest 测试脚本的解析运行。也就是说如果你的测试文件的语法是按照doctest或unittest语法去写的话,仍然可以用nosetests test.py去执行你的测试文件。另外nose支持覆盖率的同时检测,只需要在nosetests中以插件的方式加上coverage参数,就可以在做单元测试时,同时计算覆盖率,而不需要像unitest那样,需要提供额外的coverage函数。

4. pytest

pytest也是需要额外安装,pytest与unittest及其相似,区别仅在于

调用测试的命令不同,pytest 用的是 py.test
创建测试环境(setup/teardown)的 api 不同
pytest 的缺点:

它的 setup/teardown 语法与 unittest 的兼容性不如 nose 高,实现方式也不如 nose 直观

apt-get install python-pytest
py.test some_test.py –s

综上,我个人还是觉得unittest比较好用,一方面它是python自带的模块,不必额外安装,目前functest项目中已有的测试用例也提倡使用unittest。其基本使用方法总结如下:

1.import unittest
2.定义一个继承自unittest.TestCase的测试用例类
3.定义setUp和tearDown,在每个测试用例前后做一些辅助工作。
4.定义测试用例,名字以test开头。
5.一个测试用例应该只测试一个方面,测试目的和测试内容应很明确。主要是调用assertEqual、assertRaises等断言方法判断程序执行结果和预期值是否相符。
6.调用unittest.main()启动测试
7.如果测试未通过,会输出相应的错误提示。如果测试全部通过则不显示任何东西,这时可以添加-v参数显示详细信息。