一、概念
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
二、doctest
doctest模块提供了一个工具,扫描模块并根据程序中内嵌的文档字符串执行测试。
测试构造就是简单地将他的输出结果剪切并粘贴到文档字符串中。
通过用户提供的例子,强化文档,允许doctest模块确认代码结果与文档是否一致。
嵌入源码
def average(values):
'''
>>> print(average([20, 30, 70]))
40.0
>>> print(average([10, 15, 20]))
15.0
'''
return sum(values)/len(values)
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=True)
测试用例的位置必须放在整个模块文件的开头,或者紧接着对象(模块、函数、类)声明语句的下一行。也就是可以被 doc 这个属性引用到的地方。并非像普通注释一样写在哪里都可以。另:verbose 参数用于控制是否输出详细信息,默认为 False,如果不写,那么运行时不会输出任何东西,除非测试 fail。
输出结果
Trying:
print(average([20, 30, 70]))
Expecting:
40.0
ok
Trying:
print(average([10, 15, 20]))
Expecting:
15.0
ok
1 items had no tests:
main
1 items passed all tests:
2 tests in main.average
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
保存为独立文件
如果不想(或不能)把测试用例写进源代码里,则还可以使用一个独立的文本文件来保存测试用例。
python -m doctest -v example.txt
上述命令中,-m表示引入module,-v表示verbose值为True,输出测试的结果。
测试模块:test.py
def multiply(a, b):
return a * b
测试用例:example.txt
>>> from py3stdlib import multiply
>>> multiply(3, 4)
12
>>> multiply('a', 3)
'aaa'
在shell中运行
python3 -m doctest -v example.txt
输出结果
Trying:
from py3stdlib import multiply
Expecting nothing
ok
Trying:
multiply(3, 4)
Expecting:
12
ok
Trying:
multiply(‘a’, 3)
Expecting:
‘aaa’
ok
1 items passed all tests:
3 tests in example.txt
3 tests in 1 items.
3 passed and 0 failed.
Test passed.
三、unittest
使用 unittest 的标准流程为:
- 从 unittest.TestCase 派生一个子类
- 在类中定义各种以 “test_” 打头的方法
- 通过 unittest.main() 函数来启动测试
unittest 的一个很有用的特性是 TestCase 的 setUp() 和 tearDown() 方法,它们提供了为测试进行准备和扫尾工作的功能,听起来就像上下文管理器一样。这种功能很适合用在测试对象需要复杂执行环境的情况下。
测试模块:test.py
def multiply(a, b):
return a * b
测试用例:test.py
#! /usr/bin/python3
# coding=utf-8
import unittest
from py3stdlib import multiply
class TestM(unittest.TestCase):
def setUp(self):
pass
def test_3_4(self):
self.assertEqual(multiply(5,6),30)
def test_str(self):
self.assertEqual(multiply('a',3),'aaa')
if __name__ == "__main__":
unittest.main()
直接在shell运行即可
python3 test.py
输出结果
unittest中有很多方法,本例中用到了assertEqual() 方法。除此之外还有 NotEqual、Is(Not)None、True(False)、Is(Not)Instance 等针对变量值的校验方法;还有一些如 assertRaises()、assertRaisesRegex() 等针对异常、警告和 log 的检查方法;以及如 assertAlmostEqual() 等。
四、nose(推荐使用)
nose不是Python官方发行的标准包,是unittest的扩展。
语法:测试方法必须以test_开头。
测试模块:py3stdlib.py
def multiply(a, b):
return a * b
测试用例:test.py
#! /usr/bin/python3
# coding=utf-8
from py3stdlib import multiply
def test_num():
assert multiply(3,4)==12
def test_str():
assert multiply('a',3)=='aaa'
安装nosepip3 install nose
,执行测试python3 -m nose test.py
5、pytest
参考文档
与nose的区别:
- 调用测试的命令不同,pytest 用的是 $ py.test
- 创建测试环境(setup/teardown)的 api 不同
测试文件:test.py
测试指令:py.test some_test.py -s
其中**-s**用于显示print函数
#! /usr/bin/python3
# coding=utf-8
import pytest
@pytest.fixture(scope='function')
def setup_function(request):
def teardown_function():
print("teardown_function called.")
request.addfinalizer(teardown_function)
print('setup_function called.')
@pytest.fixture(scope='module')# 通过显示指定score参数来选择使用的pytest.fixture装饰器
# 有效的 scope 参数限于:'function','module','class','session',默认为 function。
def setup_module(request):
def teardown_module():
print("teardown_module called.")
request.addfinalizer(teardown_module)
print('setup_module called.')
def test_1(setup_function):
print('Test_1 called.')
def test_2(setup_module):
print('Test_2 called.')
def test_3(setup_module):
print('Test_3 called.')
小结
Part 1单元测试的含义
单元测试可以有效地测试某个程序模块的行为;
测试用例要覆盖常用的输入组合、边界条件和异常;
测试代码要非常简单,如果测试代码太复杂,那么测试代码本身可能就有bug;
测试通过并不意味着无bug,但不通过是肯定有bug.
Part2 四种测试框架的对比
doctest
官方发行。嵌入源码和独立文件。简单,但是无用功太多。
unittest
官方发行。TestCase 的 setUp() 和 tearDown() 方法提供了为测试进行准备和扫尾工作的功能,这种功能很适合用在测试对象需要复杂执行环境的情况下。
nose
不需要使用特定类模板,与unittest兼容性高,实现方式简单,基本隐藏了各种实现细节。nose 不使用特定的格式、不需要一个类容器,不需要额外的API。
pytest
不需要使用特定类模板的角度上,有彩色输出。但它的 setup/teardown 语法与 unittest 的兼容性不如 nose 高,实现方式也不如 nose 直观。
综上所述,比较推荐使用nose。