unittest 组织测试代码
单元测试的基本构建块是测试用例 — 必须设置和检查其正确性的单个方案。在 unittest
中,测试用例由 unittest.TestCase 实例表示。要创建自己的测试用例,您必须编写 TestCase 的子类或使用 FunctionTestCase。
TestCase 实例的测试代码应该完全独立,这样它既可以单独运行,也可以与任意数量的其他测试用例任意组合运行。
最简单的 TestCase 子类将简单地实现一个测试方法(即名称以test
) 开头的方法,以便执行特定的测试代码:
import unittest
class DefaultWidgetSizeTestCase(unittest.TestCase):
def test_default_widget_size(self):
widget = Widget('The widget')
self.assertEqual(widget.size(), (50, 50))
请注意,为了测试某些内容,我们使用 TestCase 基类提供的 assert*()
方法之一。如果测试失败,将引发异常并带有解释性消息,并且 unittest
会将测试用例标识为失败。任何其他异常都将被视为错误。
测试可能很多,并且它们的设置可能是重复的。幸运的是,我们可以通过实现一个名为 setUp() 的方法来分解设置代码,测试框架将为我们运行的每个测试自动调用该方法:
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def test_default_widget_size(self):
self.assertEqual(self.widget.size(), (50,50),
'incorrect default size')
def test_widget_resize(self):
self.widget.resize(100,150)
self.assertEqual(self.widget.size(), (100,150),
'wrong size after resize')
注意
各种测试的运行顺序是通过相对于字符串的内置排序对测试方法名称进行排序来确定的。
如果 setUp() 方法在测试运行时引发异常,框架将认为测试已遭受错误,并且不会执行测试方法。
类似地,我们可以提供一个 tearDown() 方法,该方法在测试方法运行后进行整理:
import unittest
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def tearDown(self):
self.widget.dispose()
如果 setUp() 成功,则无论测试方法是否成功,都将运行 tearDown()。
这种用于测试代码的工作环境称为测试夹具。创建一个新的 TestCase 实例作为用于执行每个单独的测试方法的唯一测试夹具。因此每次测试都会调用一次 setUp()、tearDown() 和 __init__()。
建议您使用 TestCase 实现,根据测试的功能将测试组合在一起。unittest为此提供了一种机制:测试套件,由unittest的TestSuite类表示。在大多数情况下,调用unittest.main()将做正确的事情,并为您收集所有模块的测试用例并执行它们。
但是,如果要自定义测试套件的构建,可以自己操作:
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_widget_size'))
suite.addTest(WidgetTestCase('test_widget_resize'))
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
您可以将测试用例和测试套件的定义与它们要测试的代码(例如 widget.py
)放在相同的模块中,但是将测试代码放在单独的模块中有几个优点,例如 test_widget.py
:
测试模块可以从命令行独立运行。
测试代码可以更容易地与附带的代码分开。
- 在没有充分理由的情况下更改测试代码以适应它测试的代码的诱惑较小。
- 与它测试的代码相比,测试代码的修改频率应该低得多。
- 测试的代码可以更容易地重构。
- 无论如何,用C编写的模块的测试必须在单独的模块中,那么为什么不保持一致呢?
- 如果测试策略发生更改,则无需更改源代码。
更多建议: