根据 ‘C语言中文网’ 该链接下的文章学习整理,点击此处即可访问。本文对此进行了提炼精简并做了适当的修改,如有侵权请私信我删除。
PyUnit(unittest)是 Python 自带的单元测试框架,用于编写和运行可重复的测试。PyUnit 是 xUnit 体系的一个成员,xUnit 是众多测试框架的总称,PyUnit 主要用于进行白盒测试和回归测试。
PyUnit具有以下的好处及特征:
---------------------------------------------好处---------------------------------------------------------
- 通过PyUnit可以让测试具有持久性,测试和开发同步进行,测试代码和开发代码一同发布;
- 可以使测试代码和产品代码分离;
- 针对某一个类的测试代码只需要进行较少的改动,便可以应用于另一个类的测试;
- PyUnit 开放源代码,可以进行二次开发,方便对 PyUnit 的扩展。
---------------------------------------------特征---------------------------------------------------------
- 使用断言方法判断期望值和实际值的差异,返回 bool 值;
- 测试驱动设备可使用共同的初始化变量或实例;
- 测试包括结构便于组织和集成运行。
所有测试的本质其实都是一样的,都是通过给定参数执行函数,然后判断函数的实际输出结果和期望结果是否一致。
PyUnit 测试与其他 xUnit 的套路一样,基于断言机制来判断函数或方法输出结果和期望输出结果是否一致,测试用例提供参数来执行函数或方法,获取它们的执行结果,然后使用断言方法来判断该函数的或方法的输出结果与期望输出结果是否一致,如果一致则说明测试通过;如果不一致则说明测试不通过。
目前流行一种开发方式叫做 测试驱动开发,这种方式强调先编写测试用例,然后再编写函数和方法,即先给出开发标准,按着标准去开发。假如程序要开发满足 A 功能的 fun_a() 函数,采用测试驱动开发的步骤如下:
- 为 fun_a() 函数编写测试用例,根据业务要求使用大量不同的参数组合执行 fun_a() 函数,并断言该函数的执行结果与业务期望的执行结果匹配;
- 编写、修改 fun_a() 函数;
- 运行 fun_a() 函数的测试用例,如果测试用例不能完全通过;则重复第 2 步和第 3 步,直到 fun_a() 的所有测试用例全部通过。
一、PyUnit 的用法
以下的 python_unittest_function.py 模块作为被测试的程序。
# python_unittest_function.py
def one_equation(a, b):
'''
求方程 a * x + b = 0 的解
参数 a ----- 方程中变量的系数
参数 b ----- 方程中的常量
返回方程的解
:param a:
:param b:
:return:
'''
if a == 0:
raise ValueError('参数错误')
else:
return b / a
def two_equation(a, b, c):
'''
求方程 a * x * x + b * x + c = 0的解
参数 a ----- 方程中变量二次幂的系数
参数 b ----- 方程中变量的系数
参数 c ----- 方程中的常量
:param a:
:param b:
:param c:
:return:
'''
if a == 0:
raise ValueError('参数错误')
elif b * b - 4 * a * c < 0:
raise ValueError('方程再有理数范围无解')
elif b * b - 4 * a * c == 0:
return -b / (2 * a)
else:
r1 = (-b + (b * b - 4 * a * c) ** 0.5) / 2 / a
r2 = (-b - (b * b - 4 * a * c) ** 0.5) / 2 / a
return r1, r2
unittest 要求单元测试类必须继承 unittest.TestCase,该类中的测试方法需要满足如下要求:
- 测试方法应该没有返回值。
- 测试方法不应该有任何参数。
- 测试方法应以 test 开头。
Python 的 unittest模块可以非常方便地进行相关的测试,以下 的 python_unittest.py 为单元测试代码。
# python_unittest.py
import unittest
from .python_unittest_function import one_equation
from .python_unittest_function import two_equation
class TestFkMath(unittest.TestCase):
# 测试一元一次方程求解
def test_one_equation(self):
# 断言该方程求解应该为 -1.8
self.assertEqual(one_equation(5, -9), -1.8)
# 断言该方程求解应该为 -2.5
self.assertTrue(one_equation(4, -10) == -2.5, .00001)
# 断言该方程求解应该为 27/5
self.assertTrue(one_equation(4, 27) == 27/4)
# 断言当 a == 0时,断言引发 ValueError
with self.assertRaises(ValueError):
one_equation(0, 9)
# 测试一元二次方程求解
def test_two_equation(self):
r1, r2 = two_equation(1, -3, 2)
self.assertCountEqual((r1, r2), (1.0, 2.0), '求解错误')
r1, r2 = two_equation(2, -7, 6)
self.assertCountEqual((r1, r2), (1.5, 2.0), '求解错误')
# 断言只有一个解的情形
r = two_equation(1, -4, 4)
self.assertEqual(r, 2.0, '求解错误')
# 断言当 a == 0 时的情况,断言引发ValueError
with self.assertRaises(ValueError):
two_equation(0, 9, 3)
# 断言引发ValueError
with self.assertRaises(ValueError):
two_equation(4, 2, 3)
if __name__ == "__main__":
unittest.main()
unittest.TestCase 内置了大量的 assertXxx 方法来执行断言,其中最常用的断言方法如下:
断言方法 | 检查条件 |
---|---|
常用的断言方法 | |
assertEuqal(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 |
assertNot(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) |
assertNotInstance(a, b) | not isinstance(a, b) |
对异常、错误、警告和日志的断言 | |
assertRasise(exec, fun, *args, **kwds) | fun(*args, **kws)引发 exec 异常 |
assertRaiseRegex(exec, r, fun, *args, **kwds) | fun(*args, **kwds)引发 exec 异常,且异常信息匹配 r 正则表达式 |
assertWarns(warn, r, fun, *args, **kwds) | fun(*args, **kwds)引发 wran 的警告 |
assertWamsRegex(warn, r, fun, *args, **kwds) | fun(*args, **kwds) 引发 wran 警告,且警告信息匹配 r 正则表达式 |
assertLogs(logger, level) | With 语句块使用日志器生成 level 级别的日志 |
断言方法用于完成某种特定检查 | |
assertAlmostEqual(a, b) | round(a-b, 7) == 0 |
assertNotAlmostEqual(a, b) | round(a-b, 7) != 0 |
assertGreater(a, b) | a > b |
assertGreaterEqual(a, b) | a >= b |
assertLess(a, b) | a < b |
assertLessEqual(a, b) | a <= b |
assertRegex(s, r) | r.search(s) |
assertNotRegex(s, r) | not r.search(s) |
assertCountEqual(a, b) | a、b 两个序列包含的元素相同, 不管元素出现的顺序如何 |
包含特定类型:字符串、序列、列表、元组、集合、字典的断言方法 | |
assertMultiLineEqual(a, b) | 字符串(string) |
assertSequenceEqual(a, b) | 序列(sequence) |
assertListEqual(a, b) | 列表(list) |
assertTupleEqual(a, b) | 元组(tuple) |
assertSetEqual(a, b) | 集合(set 或 frozenset) |
assertDictEqual(a, b) | 字典(dict) |
二、运行测试
方法一:
通过代码调用测试用例。程序可以通过调用 unittest.main() 来运行当前源文件中的所有测试用例。例如,在上面的测试用例中增加如下代码:
if __name__ == "__main__":
unittest.main()
方法二:
使用 unittest 模块运行测试用例。使用该模块的语法格式如下:
python -m unittest 测试文件(如果不加的话则表示运行所有的测试用例)
测试执行完毕,可能出现以下结果,如部分截图:
- . : 代表测试通过
- F:代表测试失败,F 代表 failure
- E:代表测试出错, E 代表 error
- s:代表跳过该测试,s 代表 skip