与Pythons Unittest一起工作
在软件工程中,单元测试是指用于自动检查软件各个部分/单元中的错误的程序。单元测试是软件开发生命周期的一个重要阶段。为了进行测试,开发者要指定一组测试用例及其预期结果,以便进行比较。
测试简介
大多数编程语言提供内置的单元测试框架。在这篇文章中,我们将重点介绍Python的unittest。回归测试是指对软件进行重新测试,以确保其在做出改变后(重构代码后)能很好地工作。在这篇文章中,我们将看到如何使用 unittest 模块来进行回归测试。
前提条件
要跟上这篇文章,你将需要
- 对Python函数和类的基本了解。
- 一个文本编辑器。我将使用[Visual Studio Code]。
- 安装了[Python]。
让我们开始创建两个文件:mathfns.py 和main.py ,使用下面的项目结构。
project-root
|
--- mathfns.py
--- main.py
让我们编写代码
mathfns.py 文件将存储我们要进行单元测试的单元。这些单元是基本的数学函数,用于将一个给定的数字x ,得到一个数字的平方x ,并将两个数字num (分子)和den (分母)相除。
# mathfns.py
def double(x):
return 2 * x
def square(x):
return x * x
# divide numerator by denominator
def divide(num, den):
return num / den
单元测试
# main.py
from mathfns import double, square, divide
import unittest
class TestMathFunctions(unittest.TestCase):
def test_double(self):
# used to check that double(8) returns 16
self.assertEqual(double(8), 16)
# used to check that double(8) returns a number not equal to 15
self.assertNotEqual(double(8), 15)
def test_square(self):
# used to check that square(7) returns 49
self.assertEqual(square(7), 49)
def test_divide(self):
# we want to make sure that the divide unit raises a ZeroDivisionError
# when one attempts to divide a number by zero.
with self.assertRaises(ZeroDivisionError):
divide(42, 0)
if __name__ == '__main__':
# unittest.main() serves as the main entry point to run the unit tests.
# unit tests are not performed without this function call
unittest.main()
main.py 文件将存储我们的单元测试。我们从上面定义的mathfns 脚本中导入单元double,square, 和divide 。然后我们从 Python 的标准库中导入unittest 模块。
TestMathFunctions 类从unittest.TestCase 类继承了单元测试功能。
它提供了三个方法。
test_double对 单元进行单元测试。doubletest_square对 单元进行单元测试。squaretest_divide对 单元进行单元测试。divide
为了使单元测试具有自我描述性,使用了以下命名惯例:测试名称应该采用test_UnitName 的格式。在这种情况下,UnitName 指的是被测试的单元。在每个单元测试中,我们使用断言方法来检查任何报告错误。
要运行单元测试,请按以下步骤进行。
>>> python3 -m unittest main.py
输出将显示所有三个测试都被正确执行。
>>> python3 -m unittest main.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
输出类型
运行测试后,unittest模块将有两种类型的输出。
OK:这表明所有测试都成功运行了,如上图所示。FAILED (failures=n):这显示n测试失败。unittest 然后打印出相关的失败信息。
失败的例子
我们已经看到了单元测试正确运行时的情况。现在,让我们来看看当其中一个单元测试失败时会发生什么。为此,我们改变了double 函数,将数字x ,而不是翻倍,如下图所示。
def double(x):
return 4 * x
当我们运行单元测试时,我们得到以下输出。
>>> python3 -m unittest main.py
.F.
======================================================================
FAIL: test_double (__main__.TestMathFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
File "main.py", line 8, in test_double
self.assertEqual(double(8), 16)
AssertionError: 32 != 16
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
在我们进行的3个测试中,self.assertEqual(double(8), 16) 产生了一个错误,因为32 != 16 。这说明了单元测试是如何用于回归测试的。
要看到更详细的信息,请在存放单元测试的文件前使用-v 标志。v 代表verbose 。
>>> python3 -m unittest -v main.py
test_divide (tests.TestMathFunctions) ... ok
test_double (tests.TestMathFunctions) ... FAIL
test_square (tests.TestMathFunctions) ... ok
======================================================================
FAIL: test_double (tests.TestMathFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
File "main.py", line 8, in test_double
self.assertEqual(double(8), 16)
AssertionError: 32 != 16
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
错误信息
错误信息对于未来的代码调试非常有用。它们也为质量保证测试人员和其他开发人员提供了文件。
例如,要在self.assertEqual(double(8), 16) ,添加一个错误信息,改变你的代码来匹配这个。
def test_double(self):
self.assertEqual(double(8), 16, "the function should return two times the number provided")
输出结果将是。
>>> python3 -m unittest main.py
.F.
======================================================================
FAIL: test_double (__main__.TestMathFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
File "main.py", line 8, in test_double
self.assertEqual(double(8), 16, "the function should return two times the number provided")
AssertionError: 32 != 16 : the function should return two times the number provided
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
如上所示,我们添加的错误信息更好地描述了AssertionError 。这使它更容易被理解。
断言方法
到目前为止,我们已经看到了assertEqual(a, b) 方法、assertNotEqual(a, b) 方法和assertRaises(x) 方法。除此以外,unittest模块还提供了这些额外的断言方法。
| 方法 | 结果 |
|---|---|
| assertTrue(x) | 检查一个布尔值x 是否等于True |
| assertFalse(x) | 检查一个布尔值x 是否等于False |
| assertIn(a,b) | 检查一个值a 是否存在于一个列表b 。例如,当a=2 和...时,这个检查应该通过。b=[1,2,3] |
| assertNottIn(a,b) | 检查一个值a 不存在于一个列表b 。例如,当a=2 和b=[1,2,3] 时,这个检查应该失败,因为2 存在于列表中b |
| assertIsNone(x) | 检查一个值a 是否等于None |
| assertIsNotNone(x) | 检查一个值a 不等于None |
| assertIs(a, b) | 检查a 和b 是否为同一对象 |
| assertIsNot(a, b) | 检查a 和b 是不同的对象。 |
| assertIsInstance(a,b) | 检查a 是b 类的一个实例。例如,如果a="Hello" ,和b=str ,检查应该通过,因为a 是一个字符串。 |
| assertNotIsInstance(a, b) | 检查a 是不是一个类的实例。b |
单元测试的好处
单元测试是软件开发生命周期的一个重要步骤,它提供的好处有:
- 发现错误的有效和自动化的方法。
- 确保一个单元在重构后能正常工作(回归测试)。
- 在开发的早期阶段发现bug,可以降低项目成本。
- 它可以帮助新的开发人员熟悉单元的工作情况。
- 它提供了代码质量的保证,因为它们显示了代码的正确运行。
unittest和其他Python测试框架
最流行的第三方单元测试框架依次包括pytest和nose。然而,unittest更适合初学者,因为它有一个浅显的学习曲线。
例如,它不使用像pytest这样的框架中使用的复杂注释。此外,它不需要安装,因为它已经内置于Python的标准库中。
与第三方框架相比,unittest确实有一些限制。
这些限制包括。
- 它需要更多的模板代码才能开始使用。
- 它不支持插件。
总结
在这篇文章中,我们了解了单元测试、回归测试,以及如何使用Python的unittest模块。