defcuboid_volume(l):return(l*l*l)
length =[2,1.1,-2.5,2j,'two']for i inrange(len(length)):print("The volume of cuboid:",cuboid_volume(length[i]))技术支持
The volume of cuboid: 8
The volume of cuboid: 1.3310000000000004
The volume of cuboid: -15.625
The volume of cuboid: (-0-8j)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-67e1c37a435f> in <module>
1 for i in range(len(length)):
----> 2 print ("The volume of cuboid:",cuboid_volume(length[i]))
<ipython-input-2-f50464fd88da> in cuboid_volume(l)
1 def cuboid_volume(l):
----> 2 return (l*l*l)
TypeError: can't multiply sequence by non-int of type 'str'技术支持
结果分析
上面的输出能帮助您直观理解为何需要为代码编写单元测试。上述代码中有三处明显不正确:
其一,正方体体积为负数,
其二,正方体体积为复数,
最后,代码导致 TypeError,因为无法将字符串(非 int)进行乘法。
第三个问题还好触发了错误,而前两个虽然不合逻辑(体积不可能为负或复数),却仍然“成功”了。
单元测试通常作为独立代码写在不同文件中,命名也有多种约定。您可以使用 代码/单元名 + test(下划线分隔),或 test + 代码/单元名(下划线分隔)。
F
======================================================================
FAIL: test_volume (test_volume_cuboid.TestCuboid)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\hda3kor\Documents\Unit_Testing_Python\test_volume_cuboid.py", line 15, in test_volume
self.assertAlmostEqual(cuboid_volume(5.5),0)
AssertionError: 166.375 != 0 within 7 places (166.375 difference)
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)技术支持
F.
======================================================================
FAIL: test_input_value (test_volume_cuboid.TestCuboid)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\hda3kor\Documents\Unit_Testing_Python\test_volume_cuboid.py", line 17, in test_input_value
self.assertRaises(TypeError, cuboid_volume, True)
AssertionError: TypeError not raised by cuboid_volume
----------------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)技术支持
unittest.main(argv=[''],verbosity=0, exit=False)
Output:
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
<unittest.main.TestProgramat0x1de02774348>技术支持
详细程度 1
unittest.main(argv=[''],verbosity=1, exit=False)
Output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
<unittest.main.TestProgramat0x1de027a1cc8>技术支持
详细程度 2
unittest.main(argv=[''],verbosity=2, exit=False)
Output:
test_input_value (__main__.TestCuboid) ... ok
test_volume (__main__.TestCuboid) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
<unittest.main.TestProgramat0x1de027a8308>技术支持
Help on function assertCountEqual in unittest.TestCase:
unittest.TestCase.assertCountEqual = assertCountEqual(self, first, second, msg=None)
An unordered sequence comparison asserting that the same elements,
regardless of order. If the same element occurs more than once,
it verifies that the elements occur the same number of times.
self.assertEqual(Counter(list(first)),
Counter(list(second)))
Example:
- [0, 1, 1] and [1, 0, 1] compare equal.
- [0, 0, 1] and [0, 1] compare unequal.技术支持
单元测试是一种软件测试方法,通过对源代码的各个独立单元进行多种测试,判断其是否可用,从而评估并确保代码质量。
通常在开发过程完成后,开发者会将已知可行且有用的判定标准或期望结果编码进测试脚本,以验证特定单元的正确性。在执行测试用例时,各类框架会记录未通过任一标准的测试,并在摘要中报告。
开发者应编写自动化测试脚本,以确保每个部分或单元都符合其设计并按预期运行。
Python 单元测试
尽管为代码编写手动测试既繁琐又耗时,但 Python 内置的单元测试框架让这一切轻松了许多。
Python 的单元测试框架称为
unittest,随 Python 一同提供。单元测试让您的代码更具前瞻性,因为您会预先考虑到代码可能失败或产生缺陷的情形。虽然不可能预测所有情况,但仍能覆盖大多数。
一个单元可以划分为多种类别:
编写单元测试的最佳方式是,从代码中最小的可测试单元开始,然后再转向其他单元,观察该最小单元如何与其他单元交互。这样,您就能逐步为应用构建起一套全面的单元测试。
Python 的单元测试框架受 Java 的
JUnit启发,具备与其他语言主流单元测试框架类似的特性。Python 的单元测试框架提供多种 功能,包括:现在让我们通过一个示例来理解为什么需要为代码编写单元测试。
开始使用 Python unittest
下面概述了使用 Python
unittest框架所需的步骤创建一个立方体函数
我们来编写一段 Python 代码,用于计算正方体的体积
结果分析
上面的输出能帮助您直观理解为何需要为代码编写单元测试。上述代码中有三处明显不正确:
TypeError,因为无法将字符串(非 int)进行乘法。第三个问题还好触发了错误,而前两个虽然不合逻辑(体积不可能为负或复数),却仍然“成功”了。
单元测试通常作为独立代码写在不同文件中,命名也有多种约定。您可以使用
代码/单元名 + test(下划线分隔),或test + 代码/单元名(下划线分隔)。例如,如果上述代码文件名为
cuboid_volume.py,那么您的单元测试文件名可以是cuboid_volume_test.py事不宜迟,我们来为上面的代码编写单元测试。
使用 assertAlmostEqual
首先,新建名为
volume_cuboid.py的 Python 文件,编写计算体积的代码;再新建名为test_volume_cuboid.py的文件,编写单元测试代码。导入 unittest
类
TestCuboid继承自unittest模块中的基类,在该类中定义若干方法,用于核对您的函数cuboid_volume的行为。首先定义函数
test_volume,用于检查cuboid_volume的输出是否符合预期。为此将使用assertAlmostEqual方法。测试 1
我们来运行上述脚本。您将通过指定
-m参数,将unittest模块作为脚本来运行。输出:
太好了!您的第一个单元测试代码已成功运行。
测试成功并返回
OK,这意味着 cuboid_volume 函数按预期工作。测试 2
我们看看当某个
assertAlmostEqual断言失败时会发生什么。请注意,最后一条断言语句已被修改。
输出:
从以上输出可以看出,最后一条断言语句触发了
AssertionError,因此单元测试失败。Python 的单元测试模块会显示失败原因及失败用例数量。使用 assertRaises
接下来我们看看另一个断言方法
assertRaises,它能帮助您判断函数cuboid_volume是否正确处理输入值。比如,您想测试函数
cuboid_volume是否会检查输入的类型:当传入字符串时,它是否会通过抛出异常或 if 条件来处理(因为正方体的边长不可能是字符串)。输出:
很好!从上述输出可以看出,您的
volume_cuboid.py并未正确处理传入的输入。修改立方体函数
让我们在
volume_cuboid.py中增加条件判断,检查传入的正方体边长是否为布尔或字符串,并抛出错误。输出:
在 JupyterLab 中运行 Python 测试脚本
现在在 JupyterLab 中运行测试脚本。首先在 JupyterLab 中定义测试脚本,如下所示。
然后使用
unittest.main()方法运行测试脚本。该方法可接收多个参数,其中之一是verbosity level(详细程度)。我们来试验不同的详细程度,看看输出说明如何变化。
详细程度 0
详细程度 1
详细程度 2
可视化 AssertionError
最后,我们修改
test_volume方法中的最后一个assertAlmostEqual,并观察详细程度为 2 时如何展示方法失败。输出:
小结
根据上述输出,可以得出以下结论:
OK或FAIL。可用的 unittest 断言方法
Python 的单元测试模块中有许多断言方法,可用于您的测试场景。
常用断言方法
来源: unittest 文档
异常/警告/日志检查
使用以下方法,您可以检查异常、警告和日志消息的产生。
来源: unittest 文档
任务特定方法
来源: unittest 文档
如需详细了解各类
assert方法,请参阅 Python 的 官方文档。您也可以借助
Pydoc模块来学习,类似于 Python 的help函数。下面演示了如何使用 Pydoc 查看assertCountEqual方法的文档。输出:
结语
本教程简要介绍了 Python 中的单元测试及其对开发者的重要性。一个不错的练习是:为您以往的任一项目编写一个单元测试模块。这将帮助您亲手实践单元测试的编写。另外,也请尝试探索其余的断言方法。想进一步了解单元测试,请阅读我们的 Pytest 教程。
如果您刚开始学习 Python,想进一步入门,请参加我们的 Python 数据科学入门课程。