文章目录
- unittest --- Unit testing framework
unittest — Unit testing framework
源码地址:Lib/unittest/__init__.py
(如果你已经熟悉了测试的基本概念,可以直接跳转到断言方法表。)
unittest 单无测试框架是源自于JUnit并且具备其他语言中的主流测试框架的特点。它支持自动化测试、测试中共享设置和关闭代码、将测试集成到集合中,并将测试从报告框架中独立出来。
为了实现这些,unittest通过面向对象的方法来实现许多重要的概念:
- test fixture (测试夹具)
估且翻译成测试夹具,作用是测试的预设置。一个test fixture代表了一个或多个测试运行时需要的准备工作。例如,它可能包含创建临时的或代理的数据库、目录或启动一个服务器进程等。 - test case (测试用例)
每个test case都是一个独立的测试单元。它检测一个特定的输入所对应的输出。unittest提供了一个基类TestCase
,可以使用它来创建test case。 - test suite (测试集合)
test suite是test case、test suite或者两者都包含的集合体。它的作用是将需要一起执行的测试集成在一起。 - test runner (测试执行器)
test runner是协调执行测试并将输出提供给用户的组件。执行器可能是一个图形交互,一个文本交互或者返回一个特别的值来表明测试的执行结果。
1. Basic example
unittest模块提供了一套丰富的工具来构建和执行测试。本节示范了一套小型的工具组合,能够满足大多数用户的需求了。
以下是一个简单的脚本来测试三个字符串方法:
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
通过unittest.TestCase
的子类来创建用例。这3个独立的测试是由三个以test
字母开头的方法定义的。这种命名方式是为了告知测试执行器哪些方法代表测试。
这些测试的核心是调用assertEqual()
来检查是否是预期的结果;调用assertTrue()
或assertFalse()
来判断状态;调用assertRaise()
来判定是否指定的异常被抛出。这些方法都使用了assert
(断言)声明,这样测试执行器能统计所有的测试结果,并生成报告。
setup()
和tearDown()
方法可以让你在测试开始之前和结束之后定义一些要执行的命令。在章节Organizing test code中会详细介绍他们。
代码中的最后一部分是一个简单地运行测试的方法。unittest.main()
为测试脚本提供了一个命令行的交互方式。当在命令行中运行时,上述脚本的输入如下所示:
在脚本中传入参数-v
将使unittest.main()
(将脚本最后一行改为unittest.main(argv=['Untitled.py', '-v'])
)产生更高级别的反馈,并生成如下输出:
上述范例展示了unittest功能的最普通的使用,这些已经可以满足遇到的大多数日常测试需求了。剩下的文档从原理出发拓展了完整的功能。
2. Command-Line Interface(命令行交互)
unittest模块能够直接在命令行中使用来测试模块、类、甚至是单独的测试方法。
python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unitest test_module.TestClass.test_method
你可以在一条命令中同时包含任意的模块名和符合规则的类名与方法名。
测试模块也可以是指定的文件路径:
python -m unittest tests/test_something.py
你也可以使用完整的shell文件名来指定要测试的模块。但是指定的文件必须是可以被作为模块导入的。将路径通过移除.py
的方式转换成模块名,并且将路径用.
隔开。例如将上一条例子改为python -m unittest tests.test_something
。但如果你的测试文件是不可以作为模块导入的,你仍需要以目录的形式输入。
你可以通过传入-v
参数使测试运行时展示更详细的信息(更高级的反馈):
python -m unittest -v test_module
当执行的命令不含参数时,会开始进行批量测试:
python -m unittest
显示所有的命令行参数:
python -m unittest -h
1. Command-line options
unittest支持以下这些参数:
-
-b, --buffer
将测试运行过程中的标准输出与标准错误流缓存。测试通过时这些缓存信息会被丢弃。测试失败或错误时,这些信息会被正常打印到失败信息中。 -
-c, --catch
在测试运行中使用Control-c
,使测试停止在当前测试完成后并生成当前已测试部分的结果。再次Control-c
将会正常抛出KeyboardInterrupt异常。
可以查看Signal Handling中的提供此功能的相关函数。 -
-f, --failfast
测试中遇到第一个错误或失败时就停止。 -
–locals
这些命令行参数也可以用于批量测试中,用来测试整个项目或者一个子集。
3. Test Discovery(批量测试)
unittest 支持简单的批量测试。为了兼容批量测试,所有的测试文件必须是可以从项目的顶层目录导入的模块或包(这意味着他们的文件名必须是正确的标识符)。
批量测试是在TestLoader.discover()
方法中实现的,但是也可以在命令行中使用他。基本的使用方法是:
cd project_directory
python -m unittest discover
注意:作为简写,python -m unittest 是与python -m unittest discover等价的。但如果你想往命令中传入参数,那么discover子命令必须被明确地使用。
使用discover子命令后,可以传入以下这些参数:
- -v, --verbose
详细信息 - -s, --start-directory directory
批量测试的目录(默认为.
) - -p, --pattern pattern
需要测试的文件所符合的表达式(默认为test*.py
) - -t, --top-level-directory directory
项目的最高级目录(默认为启动目录)
其中-s
,-p
,-t
可以作为位置参数输入。例如下述两条命令是等价的:
python -m unittest discover -s project_directroy -p "*_test.py"
python -m unittest discover project_directory "*_test.py"
也可以传入一个包名来作为启动目录,例如myproject.subpackage.test
。你提供的包名将被导入并且他在文件系统中的位置将会被作为启动目录。
注意:批量测试是通过导入测试文件来加载他们。当批量测试从你指定的启动目录中找到了所有的测试文件,它会将路径转换成包名进行导入。例如,foo/bar/baz.py
将会被转化成foo.bar.baz
导入。
如果你有一个已经加载了的全局包,而批量测试这个包在另一个地方的副本,那么这个导入过程可能会发生在错误的地方。如果发生了这种情况,批量测试将会发出警告并退出。
如果你以包名的方式而不是目标路径的形式提供了启动目录,那么批量测试将会认为所有位置的包都是你的目标,那么将不会发出警告。
测试的模块和包可以通过load_tests protocol来自定义测试加载和发现。
4. Organizing test code(组织测试代码)
单元测试的基本组成结构是test case(测试用例) — 一个必须创建并检查其正确性的假设的情况。在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()
方法。
setUp()
与tearDown()
为测试代码提供工作环境,被称为fixture。
测试用例根据他们各自的测试特点可以进行相应的分组。unittest为此提供了一个功能:test suite(测试集合),对应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语言编写的代码必须要分离开来,那为什么不保持一致呢?
- 如果只是测试策略的变更,那没有必要去修改源码。
5. Re-using old test code(复用旧的测试代码)
一些用户发现他们有一些测试代码想通过unittest来运行,但里面的旧测试函数没有转换成TestCase
子类方法。
由于这个原因,unittest
提供了一个FunctionTestCase
类。它是TestCase
子类,用来包装已经存在的测试函数。这里也是可以提供Set-up和tear-down功能的。
给出如下的测试函数:
def testSomething():
something = makeSomething()
assert something.name is not None
# ...
你可以创建一个等价的测试用例实例,包含了set-up和tesr-down方法的参数,如下:
testcase = unittest.FunctionTestCase(
testSomething,
setUp=makesomethingDB,
tearDown=deleteSomethingDb
)
注意:尽管FunctionTestCase
可以快速地将现有的测试方法转换成基于unittest的形式,但这种途径并不推荐。花点时间来创建合适的TestCase
子类将会使将来重构测试时非常地容易。
在有些情况中,现有的测试可能是使用doctest模块写的。如果是这样,doctest提供了一个DocTestSuite
类可以自动根据现有的doctest的测试来建立unittest.TestSuite
实例。
6. Skipping tests and expected failures
uittest支持跳过单独的甚至是整个类的测试。另外,它可以标记一个测试为"expected failure",一个测试中断而且失败了,但是不会作为一次失败计算在TestResult
中。
跳过一个测试是一件简单的事,通过使用skip()
装饰器,或它的条件判断变体之一。
基础的跳过使用如下:(笔者自已修改了代码,可以在IDEL中运行)
import unittest
import sys
class MyTestCase(unittest.TestCase):
@unittest.skip("demonstrating skipping")
def test_nothing(self):
self.fail("shouldn't happen")
@unittest.skipIf(int(sys.version[0]) >= 3, "not supported in this library version")
def test_format(self):
# Tests that work for olny a certain version of the library.
pass
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_windows_support(self):
# windows specific testing code
pass
下面展示了在verbose模式下的运行输出:
跳过类的方法与跳过函数是一样的:
@unittest.skip("showing class skipping")
class MyskippedTestCase(unittest.TestCase):
def test_not_run(self):
pass
TestCase.setUp()
也可以跳过测试。当需要被创建的资源不存在时,这是很有用的。
expected failure使用expectedFailure()
装饰器。
class ExpectedFailureTestCase(unittest.TestCase):
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "broken")
当想要跳过测试时,通过调用skip()
来创建自己的skip装饰器是很简单的。下面这个例子中的装饰器将会跳过测试,除非传入的obj参数确实包含了重要属性attr:
def skipUnlessHasattr(obj, attr):
if hasattr(obj, attr):
return lambda func:func
return unittest.skip("{!r} doesn't have {!r}".format(obj, attr))
以下这些装饰器实现了跳过测试和预期失败功能:
-
@unittest.skip(reason)
无条件地跳过装饰的测试。最好将为什么跳过的理由描述出来。 -
@unittest.skipIf(condition, reason)
跳过装饰的测试,如果condition的结果是true。 -
@unittest.skipUnless(condition, reason)
跳过装饰的测试,除非condition的结果是true。 -
@unittest.expectFailure
为测试标记一个预期失败。如果测试运行失败,它将不会作为失败为统计。 -
exception unittest.SkipTest(reason)
抛出此异常用来跳过测试。
通常你可以使用TestCase.skipTest()或者上述跳过装饰器之一来代替抛出异常的情况。
跳过的测试setUp()
或tearDown()
将不会运行。跳过类setUpClass()
或tearDownClass()
将不会运行。跳过模块setUpModule()
或tearDownModule()
将不会运行。
7.Distinguishing test iterations using subtests(使用subtests来区分每次迭代)
当你的一些测试之间只有些许细微的差异时,例如一些参数不同,通过在测试主体中使用subTest()
上下文管理函数,unittest可以让你区分他们。
例如下面这个测试:
class NumbersTest(unittest.TestCase):
def test_even(self):
"""
Test that numbers between 0 and 5 are all even.
"""
for i in range(0, 6):
with self.subTest(i=i):
self.assertEqual(i%2, 0)
生成如下的结果:
如果不使用subtest,程序会在第一个错误发生成就终止,而且由于i
的值不会被显示,也难于诊断出错误。
8. Classes and functions(类与方法)
此章节深度描述了unittest的API
1. Test cases
class unittest.TestCase(methodName=‘runTest’)
TestCase
类的实例代表了unittest体系里的逻辑测试单元。该类通常被作为基类作用,通过构造子类来实现指定的测试。这个类包含了测试执行器所需要的接口,包括控制测试、测试代码要去检查的被测方法和各种各样的失败报告。
每个TestCase
实例都会执行一个基本方法:名为methodName的方法。在使用TestCase
的大多数情况中,你既不会改变methodName也不会重写默认的runTest()
方法。
python3.2版本中的变动:TestCase
在不提供methodName时成功地实例化。这样你可以更轻松地在交互式解释器中对TestCase
进行试验。
TestCase
实例提供了三种类型的方法:第一种用来运行测试,第二种用在测试完成后检查条件和报告失败,最后是一些查询函数可以收集将测试本身的一些信息。
第一类方法(运行测试):
-
setUp()
调用此方法来准备测试fixture。它们在测试方法被调用之前被调用。不同于AssertionError
或者SkipTest
,此方法抛出的任务异常都会被当作错误,而不是测试失败。此方法默认情况下为pass。 -
tearDown()
当测试方法已经被调用且结果被记录后,此方法被调用。即使测试方法抛出异常,此方法仍然会被调用,所以在子类中实现时要特别注意检查它的内部状态。不同于AssertionError
或SkipTest
,此方法抛出的任意异常将被作为一个额外的错误而不是一次测试失败(这样报告的错误总数会增加)。此方法只有在setUp()
方法成功运行后被调用,与测试方法的结果无关。此方法默认情况下为pass。 -
setUpClass()
在一个类中的测试运行之前被调用的类方法。setUpClass
被调用时只有一个参数就是类本身并且必须由classmethod
方法装饰。@classmethod def setUpClass(cls): ...
在Class and Module Fixtures中查看更多详情。
-
tearDownClass()
在一个类中的测试运行之后被调用的类方法。tearDownClass
被调用时只有一个参数就是类本身并且必须由classmethod
方法装饰。@classmethod def tearDownClass(cls): ...
在Class and Module Fixtures中查看更多详情。
-
run(result=None)
运行测试,收集结果放入作为结果的TestResult
对象中。如果结果被省略或者是None,一个临时的结果对象会被创建(通过调用defaultTestResult()
方法)并使用。结果对象返回run()
的调用者。
简单地调用TestCase
实例可以有同样的效果。 -
skipTest(reason)
在测试方法或setUp()
方法中调用此方法将跳过当前测试。查看Skipping tests and expected failures 了解更多信息。 -
subTest(msg=None,**params)
将一个封闭的代码块作为子测试执行,并返回他的上下文管理器。msg和params是可选的、任意的值,将会在子测试失败时显示,可以让你更清楚的定位异常。
一个测试用例可以包括任意数量的subtest声明,而且他们可以随意嵌套。
查看Distinguishing test iterations using subtests来获取更多信息。 -
debug()
不收集结果的情况下运行测试。它可以将测试抛出的异常传递给调用者,并且可用于调试器中运行的测试。
TestCase类提供了多个断言方法来检查和报告失败。下表列出了大多数常用的方法(查看之后的其他表来了解更多assert方法):
Method | Checks that | New in |
---|---|---|
assertEqual(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 | 3.1 |
assertIsNot(a, b) | a is not b | 3.1 |
assertIsNone(x) | x is None | 3.1 |
assertIsNotNone(x) | x is not None | 3.1 |
assertIn(a, b) | a in b | 3.1 |
assertNotIn(a, b) | a not in b | 3.1 |
assertIsInstance(a, b) | isinstance(a, b) | 3.2 |
assertNotIsInstance(a, b) | not isinstance(a, b) | 3.2 |
所有的这些assert方法都可接受一个msg参数,如果被指定,失败时会作为错误信息(也可以查看longmessage)。注意当assertRaises(),assertRaisesRegex(),assertWarns(),assertWarnsReges()被作为上下文管理器使用时,msg关键字参数会被传递给他们。
- assertEqual(first, second, msg=None)
测试first和second是否相等。如果他们不相等,测试将会失败。
另外,如果first和second是完全相同的类型并且是list、tuple、dict、set、frozenset、str类型或任意由addTypeEqualityFunc()方法记录了的子类,该方法会被调用来生成更有用的默认错误信息(查看list of type-specific methods)。
3.1中更新:增加了自动调用特定类型比较函数。
3.2中更新:assertMultiLineEqual()被作为默认的类型比较函数用来比较字符串。
- assertNotEqual(first, second, msg=None)
测试first和second是否不相等。如果他们相等,测试失败。
- assertTrue(expr, msg=None)
assertFalse(expr, msg=NONE)
测试expr是true(或false)。
注意,它是等价于bool(expr) is True而不是expr is True(后者应使用*assertIs(expr, True) )。当有其他 的特定方法可用时该方法应尽量避免使用此方法(例如 使用assertEqual(a, b)
而不是assertTrue(a == b)
),因为当测试失败时他们能提供更好的错误信息。
- assertIs(first, second, msg=None)
assertIsNot(first, second, msg=None)
测试first和second是否是同一个对象。
- assertIsNone(expr,msg=None)
assertIsNotNone(expr, msg=None)
测试expr是否是None
。
- assertIn(first, second, msg=None)
assertNotIn(first, second, msg=None)
测试first是否在second中。
- assertIsInstance(obj, cls, msg=None)
assertNotIsInstance(obj, cls, msg=None)
测试obj是否是cls(此参数可以一个类或者几个类的元组,由isinstance()
支持)的一个实例。使用assertIs(type(obj), cls)
来查看准确的类型。
使用下列方法甚至可以检查生成的异常、警告和日志信息:
Method | Checks that | New in |
---|---|---|
assertRaises(exc, fun, *args, **kwds) | fun(*args,**kwds) raises exc | |
assertRaisesRegex(exc, r, fun, *args, **kwds) | fun(*args, **kwds) raises exc and the message matches regex r | 3.1 |
assertWarns(warn, fun, *args, **kwds) | fun(*args, **kwds) raises warn | 3.2 |
assertWarnsRegex(warn, r, fun, *args, **kwds) | fun(*args, **kwds) raises warn and the message matches regx r | 3.2 |
assertLogs(logger, lever) | The with block logs on logger with minimum level | 3.4 |
- assertRaises(exception, callable, *args, **kwds)
- assertRaises(exception,*, msg=None)
测试当callable被调用时所抛出的异常,callable函数的参数是传入assertRaises()中的*args与**kwds。如果抛出的异常是assertRaises()中传入的exception则测试通过,如果抛出了其他异常则判定为一个error,如果没有异常抛出则测试失败。exception可以是多个异常的集合体或是包含了多个exception类的元组。
如果只给定了必要参数exception和可选参数msg,那函数可以作为一个上下文管理器使用,如下:
当assertRaises()函数作为上下文管理器使用时,可以接受关键字参数msgwith self.assertRaises(SomeException): do_something()
上下文管理器会将蒱获的异常对象存放在它的exception
属性中。如果想对异常进行一些额外的检查,这样做是很有用的:with self.assertRaises(SomeException) as cm: do_something() the_exception = cm.exception self.assertEqual(the_exception.error_code, 3)
-
assertRaisesRegex(exception, regex, callable, *args, **kwds)
-
assertRaisesRegex(exception, regex, *, msg=None)
与assertRaises相似,但在此基础上抛出的异常还必须要匹配regex参数给定的表达式。regex可能是一个正则表达式对象或一个可用于re.search()
函数的包含正则表达式的字符串。例如:self.assertRaisesRegex(ValueError, "invalid literal for.*XYZ'$", int, 'XYZ')
or
with assertRaisesRegex(ValveError, "invalid literal": int('XYZ')
-
assertWarns(*warning, callable, *args, *kwds)
-
assertWarns(warning, *, mag=None)
测试调用callable时触发了的警告,callable的参数是传入assertWarns()函数中的*args与*kwds。当指定的warning被触发时,测试通过。所有的异常都判定为error。warning可以是多个warning的集合体或是包含了多个warning类的元组。
如果只给定了warning参数或者可选参数mag*,返回一个上下文管理器:with self.assertWarns(SomeWarning): do_something()
上下文管理器会将捕获的warning对象存放在它的
warning
属性中,并且触发警告的代码行的信息将会存放在它的filename
和lineno
属性中。这样便于对捕获的warning进行一些额外的检查。with self.assertWarns(SomeWarning) as cm: do_something() self.assertIn('myfile.py', cm.filename) self.assertEqual(320, cm.lineno)
该方法被调用时,忽略warning过滤器。
-
assertWarnsRegex(exception, regex, callable, *args, **kwds)
-
assertWarnsRegex(exception, regex, *, msg=None)
与assertRaisesRegex功能一样,不过针对对象是warning -
assertLogs(logger=None, level=None)
这是一个上下文管理器,用来测试至少在level这个级别有至少有1条信息记录在logger或者他的子类。
如果给定了logger,它必须是一个logging.Logger对象或者是该对像的名称。默认的为系统级的logger,它会抓取所有的信息。
如果给定了level,它必须是一个表示logging级别的数值或等价的字符串名称(例如"ERROR"或logging.ERROR)。默认的是logging.INFO。
如果至少有一条符合logger和level的信息发送到了with
块中,测试通过。
上下文管理器返回对象是一个记录器,可以保存匹配的日志信息。它有两个属性:- records
匹配的日志信息的logging.LogRecord对象的列表。 - output
匹配的日志信息的格式化的字符串输出组成的列表。
例子:with self.assertLogs('foo', level='INFO') as cm: logging.getLogger('foo').info('first message') logging.getLogger('foo.bar').error('second message') self.assertEqual(cm.output, ['INFO:foo:first message', 'ERROR:foo.bar:second message'])
- records
以下是一些其他的断言函数:
Methon | Checks than | New in |
---|---|---|
assertAlmostEqual(a, b) | round(a-b, 7) == 0 | |
assertNotAlmostEqual(a, b) | round(a-b, 7) != 0 | |
assertGreater(a, b) | a > b | 3.1 |
assertGreaterEqual(a, b) | a >= b | 3.1 |
assertLess(a, b) | a < b | 3.1 |
assertLessEqual(a, b) | a <= b | 3.1 |
assertRegex(s, r) | r.search(s) | 3.1 |
assertNotRegex(s, r) | not r.search(s) | 3.2 |
assertCountEqual(a, b) | 3.2 |
-
assertAlmostEqual(first, second, places=7, msg=None, delta=None)
-
assertNotAlmostEqual(first, second, places=7, msg=None, delta=None)
测试first和second是否是近似相等的。判定方法是计算first和second的差值,并依据给定的place参数的值(默认为7)进行四舍五入,然后将这个结果与0进行比较。注意这里是四舍五入而不是取多少位有效数字。
如果给定了delta参数,将忽略默认的places参数,测试first与second之前的差的绝对值是否小于(或者大于等于)delta的值。
如果强行同时给定places与delta参数,将会抛出TypeError错误。 -
assertGreater(first, second, msg=None)
-
assertGreaterEqual(first, second, msg=None)
-
assertLess(first, second, msg=None)
-
assertLessEqual(first, second, msg=None)
测试first是否 >, >=, <, <= second。 -
assertRegex(text, regex, msg=None)
-
assertNotRegex(text, regex, msg=None)
测试一个regex是否匹配text。万一测试未通过,表达式和text都会包含在错误信息中。regex可以是一个与此同时表达式对象或是一个适用于re.search()
函数的包含正则表达式的字符串。 -
assertCountEqual(first, second, msg=None)
测试序列first和second在不考虑顺序的情况下拥有相同的元素。如果不相同,将会生成一个列出了两个序列不同项的错误信息。
对两个序列进行比较时不会忽略重复的无素。它会检验两个序列的元素个数是否相等。
assertEqual()
函数可以检查两个相同类型的对象是否相等,由它派生出了一系列只查检指定类型的函数。这些函数涵盖了大多数的内置类型,另外我们也可以通过addTypeEqualityFunc()
函数来创建这类函数。
- addTypeEqualityFunc(typeobj, function)
注册一个将会被assertEqual()调用的判定指定类型的方法来检查两个typeobj类型(非子类)的对象是相等的。function必须传入两个位置参数和一个msg=None
的关键字参数,就是assertEqual()
函数一样。它必须在两个参数不相等时抛出self.failureException(msg)
异常,用来说明详细信息。
下列表中是被assertEqual()函数自行调用的指定类型方法。注意,没必须去专门调用这些方法。
Method | Used to compare | New in |
---|---|---|
assertMultiLineEqual(a, b) | strings | 3.1 |
assertSequenceEqual(a, b) | sequences | 3.1 |
assertListEqual(a, b) | lists | 3.1 |
assertTupleEqual(a, b) | tuples | 3.1 |
assertSetEqual(a, b) | sets or frozensets | 3.1 |
assertDictEqual(a, b) | dicts | 3.1 |
-
assertMultiLineEqual(first, second, msg=None)
测试多行字符串first和second是相等的。不相等时,它们之间的差异部分会被高亮显示在错误信息中。使用assertEqual()
函数比较string时默认调用该方法。 -
assertSequenceEqual(first, second, msg=None, seq_type=None)
测试两个序列是相等的。如果指定了seq_type,first和second必须是seq_type所指定的类型,否则将会失败。如果两个序列不相等,不相等的部分会在错误信息中显示。
该方法并不直接被assertEqual()
方法引用,而是封装在assertListEqual()
与assertTupleEqual()
方法中 -
assertListEqual(first, second, msg=None)
-
assertTupleEqual(first, second, msg=None)
测试两个list或tuple是否相等。如果不相等,不相等的部分会在错误信息中显示。如果它们的类型不是列表或元组也是抛出错误。这两个方法在assertEqual()
函数比较list或tuple时被调用。 -
assertSetEqual(first, second, msg=None)
测试两个set是相等的。如果不相等,差异的部分会显示在错误信息中。该方法在assertEqual()
函数比较set时被调用。 -
assertDictEqual(first, second, msg=None)
测试两个dict是相等的。如果不相等,差异的部分会显示在错误信息中。该方法在assertEqual()
函数比较dict时被调用。
最后,TestCase还提供了以下方法及属性:
-
fail(msg=None)
无条件地将一个测试标注为失败的,msg 为错误信息。 -
failureException
该属性提供了测试方法抛出的异常。如果测试框架需要全用一个特殊的exception,可能是用来携带一些额外信息,那么必须去构那建该属性的子类。该属性的初始值是AssertionError
。 -
longMessage
该属性决定了当一个带有msg参数的assert方法失败时程序的反应。默认值是True
,此时msg的信息将会添加到标准失败信息的末尾。当将它设置为False
时,msg的信息会替换标准失败信息。
该属性可以单独的测试方法中重新设置,self.longMessage
,在使用assert方法之前设置。每个测试方法执行前会重置该属性。
如果直接在类下设置longMessage=False
,则会对所有方法一直有效。 -
*maxDiff
该属性决定了assert方法在失败信息中反馈差异时的最大长度。默认情况是80*8个字符。受该属性影响的方法有assertSequenceEqual()
(包括所有调用该方法的方法),assertDictEqual()
和assertMultiLineEqual()
。
将该属性设置为None意味着没有最大长度限制。
测试框架可以使用下列方法来收集测试信息:
-
countTestCases()
返回测试对象所测试的数量。对TestCase
来说,它始终是1。 -
defaultTestResult()
返回一个当前测试测试用例类的测试result类的实例(如果没有其他的result类提供结run()
方法)。
对TestCase
实例来说,它始终是TestResult
类的实例;TestCase
的子类在必要时应该重写该方法。 -
id()
返回一个可以标记指定测试用例的字符串。它通常是包含了模块名及类名的测试方法的全称。 -
shortDescription()
返回测试的描述,如果没有提供描述则返回None
。默认返回测试方法下的文件字符串的第一行,如果没有返回None
。 -
addCleanup(function, *args, **kwargs)
添加一个函数将会在tearDown()
执行后被调用,参数为addCleanup
函数中的位置参数与关键字参数,用来清除测试过程中使用的资函数将会按照与它们添加进去的顺序相反顺序执行(LIFO)。
如果setUp()
执行失败,意味着tearDown()
将不会执行,但所有添加的函数仍然会执行。 -
doCleanups()
在tearDown()
执行后或setUp()
抛出异常后,无条件地执行该方法。
该方法是用来调用被addCleanup()
函数添加了的清除函数。如果你想在tearDown()
之前调用该方法,你可以自己主动调用doCleanups()
。
每次调用doCleanups()
会消除cleanup函数栈中的一个函数,因此可以在何何时候调用它。 -
classmethod addClassCleanup(function, /, *args, **kwargs)
-
classmethod doClassCleanups()
-
class unittest.IsolatedAsyncioTestCase(methodName=‘runTest’)
以上三个项为3.8版本新增,文档中有不一致处,本人未安装3.8版本,未进行试验,在此不翻译讨论。
class unittest.FunctionTestCase(testFunc, setUp=None, tearDown=None, description=None)
该类封装了TestCase
的部分接口,以允许测试执行器来执行测试,但并没有提供相关查检和反馈错误的方法。它是用以前的测试代码来创建测试用例,使它能适用于当前的uniitest测试框架。
废除的别名
因为一些历史原因,一些TestCase
方法用一个或多个如今已经废弃的别名。下表列出了它们的当前名字和曾用名:
Method Name | Deprecated alias | Deprecated alias |
---|---|---|
assertEqual() | failUnlessEqual | assertEquals |
assertNotEqual() | failIfEqual | assertNotEquals |
assertTrue() | failUnless | assert_ |
assertFalse() | failIf | |
assertRaises() | failUnlessRaises | |
assertAlmostEqual() | failUnlessAlmostEqual | assertAlmostEquals |
assertNotAlmostEqual() | failIfAlmostEqual | assertNotAlmostEquals |
assertRegex() | assertRegexpMatches | |
assertNotRegex() | assertNotRegexpMatches | |
assertRaisesRegex() | assertRaisesRegexp |
2.Grouping tests
class unittest.TestSuite(tests=())
该类代表一个测试用例和测试套件的集合。该类提供了测试执行器所需要的接口,使它能够像测试用例一样执行。运行TestSuit
实例就像对套件进行迭代,然后运行每个单独的测试。
如果提供了tests,它必须是由一些单独的测试用例和测试套件组成的可迭代对象,可以用来构建测试套件。还有一些额外的方法可以用来添加用例及套件。
TestSuite
对象与TestCase
对象非常相似,除了它们并没有实现测试。相反,它们只是将需要一起执行的测试集合在一个组中。一些额外的方法可以向TestSuite
实例中添加测试。
- addTest(test)
向测试套件中添加一个TestCase
或TestSuite
- addTests(tests)
向测试套件中添加一个由TestCase
和TestSuite
实例所组成的可迭代对象中的所有测试。
它等同于调用addTest()
方法对tests进行迭代。
TestSuite
与TestCase
共享哪下方法:
-
run(result)
执行套件中的测试,收集测试结果传递至测试结果对象result中。注意不同于TestCase.run()
,TestSuite.run()
需要测试结果对象作为参数。 -
debug()
执行套件中的测试,但不收集结果。它可以使用抛出的异常直接反馈给调用者,并且可以在debug栻上执行测试。 -
countTestCases()
返回当前测试对象中所包含的测试数量,包括单独的测试用例及子套件。 -
__iter__()
由TestSuite
所集成的测试是可迭代的。子类可以重写该方法来提供懒惰的测试。注意访方法在一个套件中可能被调用多次(例如计算测试数量和进行相等比较时),因此在执行TestSuite.run()
之前,每次迭代必须是相同的。在TestSuite.run()
之后,调用者不能信赖该方法的返回值,除非子类中重写了TestSuite._removeTestAtIndex()
来保持测试的引用。
在一个TestSuite
对象的典型使用中,run()
方法是被TestRunner
引用的而不是终端用户的组件。
3. Loading and running tests
class unittest.TestLoader
TestLoader
类是用来从类和模块中创建测试套件的。通常情况下没必要创建该类的实例;unittest模块提供了一个可用的unittest.defaultTestLoader
。然而需要定制专用配置时需要使用到子类或实例。
TestLoader
对象用下列属性:
- errors
加载测试时的非致命错误的列表 。加载器在任何时候都不会重置它。致命错误会被相关方法作为异常抛给用户。
TestLoader
对象有以下方法:
-
loadTestsFromTestCase(testCaseClass)
返回一个由TestCase
派生出来的testCaseClass所包含的所有测试用例所组成的套件。
每一个方法所创建的测试用例由getTestCaseNames()
全名。默认情况下这些方法都是以字符串test开头(由该类下的testMethodPrefix属性约定)。如果getTestCaseNames()
没有返回任何方法,但是testCaseClass已经实现了*runTest()*方法,那么将为该方法创建一个单独的测试用例。 -
loadTestsFromModule(module, pattern=None)
返回给定模块中的所有测试用例所组成的套件。该方法查找module中所有由TestCase
派生出的类,并为类中定义的每一个测试方法创建一个测试用命。注意:module可以是包也可以是.py文件,但必须是已使用import导入或使用__import__内置方法所生成的对象注意:虽然通过对
TestCase
类进行层次性派生可以方便地共享fixtures和helper函数,但是在不打算实例化的基类中定义测试方法是不合适的。然而该情况又适用于当子类中需要定义不同的fixtures时。如果模块中提供了
load_tests
函数,那么将会使用该函数来加载测试。它可以使模块能自定义测试加载的方式。[点击这里](#load_tests protocol)是加载的规则。partern参数作为load_tests
方法的第三个参数传入。 -
loadTestsFromName(name, module=None)
根据给定的name返回包含所有测试用例的suite对象。
指定的name必须是一个点号类名称即可以是一个模块,一个测试用例类,测试用例类中的一个测试方法,一个TestSuite
实例,或一个返回TestCase
或TestSuite
实例的可调用对象。按上述列出的顺序来分析它是哪种情况,如果它是一个测试用例类中的方法,则不会将它判定为一个可调用对象。
例如,如果有一个SampleTests
模块,里面有一个TestCase派生类SampleTestCase
,这个类又有三个测试方法(test_one(),test_two(),test_thress()),那么指定name为SampleTests.SampleTestCase
将会返回包含这三个测试方法的suite对象。而指定SampleTests,SampleTestCase.test_two
将会返回一个只包含这一个方法的suite对象。这个规则适对没有导入的模块和包都适用;它们将会被顺便导入。
该方法也可以将name按给定的module进行分解。
-
loadTestsFromNames(names, module=None)
与loadTestsFromName()
一样,除了names是一个序列而不是一个单独的名称。返回值是一个包含了names所有指定的对象中所包含的测试。 -
getTestCaseNames(testCaseClass)
返回testCaseClass中所有测试方法的名称的序列。参数必须是TestCase
类。 -
discover(start_dir, pattern=‘test*.py’, top_level_dir=None)
从指定的start_dir目录开始递归查找所有的测试模块,返回一个包含它们的TestSuite
对象。只有符合pattern规则的文件才会被加载。(这里使用shell的匹配规则)只有可被import的模块才能被加载。所有的测试模块必须都可以从项目的顶层目录所导入。如果启动目录不是顶层目录,那么顶层目录必须单独给出。
如果一个模块导入失败,例如发生了语法错误,那么它将被记录为一次单独的错误,但批量检索仍然继续。如果导入失败是因为
SkipTest
,则将会记录为一次跳过。如果一个检索到一个包(目录下含有__init__.py文件),将会在该文件下查找
load_tests
函数。如果存在,则会调用package.load_tests(loader, tests, pattern)。测试的批量检索会确保一个包只检索一次,尽管load_test
函数中又一次调用了loader.discover
。如果包中存在
load_tests
那么批量检索将不会再在包中递归查找,而是由load_tests
函数来加载包中的测试。pattern参数有意没有存储为loader的属性,因此包可以继续自行批量检索。top_level_dir被存储为loader属性,因此load_tests不用将它传递给loader.discover()。
start_dir可以是一个点号形式的模块名称,也可是一个路径。
下列TestLoader
的属性可以配置子类或实例。
-
testMethodPrefix
测试方法的名称前缀,符合该规则的方法将被判定为测试方法。默认为'test'
。
它作用于getTestCaseNames()
以及所有的loadTestsFrom*()
方法。 -
sortTestMethodsUsing
在getTestCaseNames()
方法以及所有的loadTestsFrom*()
方法中进行名称排序时使用。 -
suiteClass
用来构建测试方法集的可调用对象。不需要对结果使用任何方法。默认值是TestSuite
类。
影响所有的loadTestsFrom*()
方法。 -
testNamePatterns
3.7中新增
class unittest.TestResult
该类用来编写反应测试成功及失败的信息。
一个TestResult
对象存放一个测试集合的所有结果。TestCase
和TestSuite
确保结果被准确地记录;测试的编写者不用担心测试结果的记录问题。
基于unittest的测试框架希望TestResult
对象可能希望访问运行测试集合后存储报告的TestResult
对象;由TestRunner.run()
方法返回的TestResult
实例可以实现这个目的。
TestResult
实例的以下这些属性对于查看测试结果是很有用的:
-
errors
一个以二元元组为元素所组成的列表,元组内的每一个元素为TestCase
实例,第二个元素为已转换为字string格式的该实例执行时的回溯信息。每个元组代表一个发生了非预期异常的测试。 -
failures
一个以二元元组为元素所组成的列表,元组内的每一个元素为TestCase
实例,第二个元素为已转换为字string格式的该实例执行时的回溯信息。每个元组代表一个发生由TestCase.assert*()
方法所导致的失败。 -
skipped
一个以二元元组为元素所组成的列表,元组内的每一个元素为TestCase
实例,第二个元素为该测试跳过的原因。 -
expectedFailures
一个以二元元组为元素所组成的列表,元组内的每一个元素为TestCase
实例,第二个元素为已转换为字string格式的该实例执行时的回溯信息。每个元组代表一个发生了预期失败的用例。 -
unexpectedSuccess
一个列表,元素为一个标记为预期失败但执行成功了的用例。 -
shouldStop
当测试需要执行stop()
方法来终止时,将该属性设置为True
。 -
testsRun
目前为止已运行的测试用例的总合。 -
buffer
如果设置为True
,sys.stdout
和sys.stderr
将会在startTest()
执行后,stopTest()
执行前缓存。如果测试失败或错误,收集的输出会由实际sys.stdout
和sys.stderr
打印出来。(代码中此时是由_original_std*属性的write方法实现)。所有的输出也会符加上失败或错误信息。 -
failfast
如果设置为True
,当发生第一个错误或失败时会调用stop()
方法,停止测试。 -
tb_locals
如果设置为True
,局部变量将会在回溯中显示。 -
wasSuccessful()
如果当前所有的测试都通过了,返回True
,否则返回False
。
3.4版本中变更:如果标记了expectedFailure
的方法发生了unexpectedSuccess
,也返回False
。 -
stop()
调用该方法来设置shouldStop
属性为True
来表明测试应该中止。TestRunner
对象应该检查该标记,并不再执行任何其他测试。例如,当用户使用键盘来给出一个中断信号时,
TextTestRunner
类使用该特性来停止测试框架。提供了TextRunner
组件的交互工具可以使用一个类似的方法来实现这个功能。
下列TestResult
类中的方法用来保证内部的数据结构完善性,可以在子类中扩展来支持额外的测试报告需求。在创建在测试执行中支持交互性反馈的工具时是特别有用的。
-
startTest(test)
当一个测试用例准备执行时被调用。 -
stopTest(test)
不考虑测试结果,在一个测试用例执行完毕后调用。 -
startTestRun()
在整个测试开始之前,调用一次。 -
stopTestRun()
在整个测试结束后,调用一次。 -
addError(test, err)
当测试用例抛出一个非预期的异常时调用。err是由sys.exc_info()
返回的元组:(type, value, traceback)
。默认的功能是向实例的
errors
属性中添加一个元组(test, formatted_err)
,formatted_err是由err而来的格式化的回溯信息。 -
addFailure(test, err)
当测试用例发生失败时调用。err是由sys.exc_info()
返回的元组:(type, value, traceback)
。默认的功能是向实例的
failures
属性中添加一个元组(test, formatted_err)
,formatted_err是由err而来的格式化的回溯信息。 -
addSuccess(test)
测试用例执行成功时调用。
默认为pass。 -
addSkip(test, reason)
当测试用例被跳过时调用。reason作为调过的原因说明。默认功能是向实例的
skipped
属性中添加一个元组(test, reason)
。 -
addExpectedFailure(test, err)
当一个测试用例执行失败,但是被装饰了expectedFailure()
时调用。默认的功能是向实例的
expectedFailures
属性中添加一个元组(test, formatted_err)
,formatted_err是由err而来的格式化的回溯信息。 -
addUnexpectedSuccess(test)
当一个测试用例被装饰了expectedFailure()
但却执行成功时调用。默认功能是向实例的
unexpectedSuccesses
属性中添加该测试用例对象。 -
addSubTest(test, subtest, err)
当一个子测试完成时调用。test是当前由测试方法所构建的用例。subtest是一个描述子测试的TestCase
实例。(它是_SubTest
类的实例,该类是TestCase
的子类)。如果err是
None
,表示子测试执行通过。否则,子测试失败,err则是执行失败后收sys.exc_info()返回的元组:(type, value, traceback)
该方法默认在子测试通过时不做任何时,当子测试失败时将它记录为一个标准的失败。
class unittest.TextTestResult(stream, descriptions, verbosity)
被TestTestRunner
类所引用的改装后的TestResult
子类。
unittest.defaultTestLoader
被共享的TestLoader
类的实例。如果不需要自定义TestLoader
,可以使用该实例来避免反复创建新的实例。
class unittest.TextTestRunner(stream=None, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None, warnings=None, *, tb_locals=False)
一个基本的测试执行器工具,用来将结果输出到数据流。如果steam是None
,默认情况下sys.stderr
将会作为输出流。该类有很多配置参数,但其实都很简单。执行测试套件的图形界面应用应该提供灵活的工具。当需要向unittest框架中添加新特性时,这个工具应该接受**kwargs
参数来用于修建执行器。
执行器默认会显示DeprecationWarning
,PendingDeprecationWarning
,ResourceWarning
和ImportWarning
,尽管在默认情况下这些警告会被忽略。DeprecationWarning
是由unittest的已废弃的方法所引起的,当warnings是default
或always
时,它们在每个模块执行时只显示一次,这是为了避免出现太多的警告信息。通过在使用时使用Python的-Wd
或-Wa
参数和设置warning为None
,这个功能可以被覆盖_。
-
_makeResult()
该方法是在TextTestRunner
类的run()
方法用调用,用来创建一个TestResult
实例。它不可以被直接调用,但是可以重写该方法用来提供自定义的TestResult
类。_makeResult()
实例化了TextTestRunner
类的resultclass
参数。默认情况下,如果没有提供resultclass
参数,result类由以下参数来实例化
stream, descriptions, verbosity
-
run(test)
该方法是TextTestRunner
核心公共接口。该方法需要一个TestSuite
或TestCase
实例作为参数。通过调用_makeResult()
方法来创建TestResut
实例,然后测试执行并将结果打印到标准输出中(stdout)中。
unittest.main(module=‘main’, defaultTest=None, argv=None, testRunner=None, testLoader=unittest.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None)
这是一个从module中加载测试并执行的命令行程序;它主要是用来让测试模块可以方便地运行。最简单的使用方法是在脚本中添加下列代码:
if __name__ == '__main__':
unittest.main()
你可以通过设置verbosity参数让测试执行后反馈更多的信息:
if __name__ == '__main__':
unittest.main(verbosity=2)
defaultTest参数可以是单个的测试名称或是一些测试名称的可迭代对象,前提是argv参数中没有给出测试名称。如果该参数值为None
或不指定,并且argv中也没有给定测试名,那么在将执行在module找到的所有测试。如果值为None
或不指定,那它的值是sys.argv
。
argv参数是传递给程序的参数列表,它的第一个元素应该是项目名。如果值为None
或不指定,那它的值是sys.argv
。
testRunner参数可是一个测试执行器的类,也可以是它的实例。默认情况下,main
会调用sys.exit()
,并传入状态码来判定测试执行成功还是失败。
testloader参数必须是一个TestLoader
实例,并且默认为defaultTestLoader
main
支持在交互式编译器使用它时传入exit=False
参数。它可以在显示结果时不调用sys.exit()
:
from unittest import main
main(module='test_module', exit=False
failfast, * catchbreak* 和buffer参数与命令行参数-f,-c,-b是同样的作用。
warning参数指定了测试运行时的警告过滤器。如果没有指定则为None
,如果在python命令行中给定了参数-w则将被设置为default
。
调用main
实际上是返回一个TestProgam
类的实例。它将测试执行的结果存储在`result·属性。
load_tests protocol
通过实现一个名为load_tests
的函数,模块或包可以在测试运行时自定义加载测试的方式。
如果一个模块中定义了load_tests
函数,那么那他将被TestLoader.loadTestFromModule()
按以下格式调用:
load_tests(loader, standard_tests, pattern)
pattern是由loadTestsFromModule
中传入。默认值为None。
load_tests
必须返回一个TestSuite
实例。
loader是TestLoader
的实例对象。standard_tests
是在默认情况下将会从模块中加载的对象。通常情况下,只会从模块的标准测试对象集中添加或删除测试。第三个用来批量检索时的筛选。
一个典型的load_tests
函数,用来从一些指定的TestCase
类中来加载测试可能是像下面这样:
test_case = (TestCase1, TestCase2, TestCase3)
def load_tests(loader, tests, pattern):
suite = TestSuiite()
for test_class in test_cases:
tests = loader.loadTestsFromTestCase(test_class)
suite.addTests(tests)
return suite
如果在一个含有包的目录内开始批量检索,不论是使用命令行的方式还是调用TestLoader.discover()
,都会在包的__init__.py
中查找load_tests
函数。如果没有找到该函数,批量检索会将包作为另一个目录来递归地检索包内的测试。否则,检索包中的测试将会依据load_tests
:
load_tests(loader, standard_tests, pattern)
该函数必须返回包含了包中测试对象的TestSuite
实例。(standard_tests只会包含__init__.py
文件中的测试)
由于pattern传入了load_tests
函数中,因此包可以持续地检索测试。一个不做任何事的load_tests
函数像下面这样:
def load_tests(loader, standard_tests, pattern):
this_dir = os.path.dirname(__file__)
package_tests = loader.discover(start_dir=this_dir, pattern=pattern)
standard_tests.addTests(package_tests)
return standard_tests
个人小结:
- load_tests函数可以让我们自定义加载测试的方式, load_tests必须返回一个TestSuite实例
- 针对模块的load_tests定义在单个模块文件中,
standard_tests
参数默认为模块中检索所有测试对像的集合,其结构为:suite=[suite1,suite2…],suite都是TestSuite实例,内层的suite1等对象是由loader.loadFromTestCase(TestCase)返回的实例。 - 针对包的load_tests定义在包的__init__.py文件中,
standard_tests
默认为检索__init__.py模块所返回的suite。当前层次的load_tests函数将作用于下层所有包中。当前工作目录下的__init__.py中的load_tests并不会执行。
4. 类和模块的fixtures
类和模块级别的fixtures封装在TestSuite
中。当测试套件遇到一个从新的类中解析出来的测试时,那么上一个类的tearDownClass()
将会被调用,接下来调用新的类的setUpClass()
。
相同的,如果一个测试来自一个新的模块,那么将运行上一个模块的tearDownModule
,然后运行新模块的setUpModule
。
在所有的测试运行完毕后,将会运行tearDownClass
和tearDownModule
。
注意共享的fixtures并不适合一些潜在的特性,像并行测试和打断测试,它们应该被小心地使用。
默认的测试顺序是由unittest测试加载器创建的测试按照相同的模块和类分组。这可以使setUpClass
/setUpModule
在每个类和模块中只调用一次。如果将顺序打乱,那么相邻的测试可能来自不同的模块和类,那么在一个单独的测试用例中它们的共享fixture函数可能被调用多次。
共享fixture并不适用于非标准顺序的套件。如果不想使用共享fixture,框架中仍有BaseTestSuite
可以使用。
如果共享fixture运行时抛出异常,则测试结果将被作为error
。因为没有相应的测试实例化一个_ErrorHolder
(和TestCase
有相同的接口)对象来代表这个错误。如果你只是使用标准的unittest测试执行器,那么这些都无关紧要,但如果是你一个框架的作者,这可能是相关的。
1. setUpClass和tearDownClass
它们必须封装成类方法的形式:
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._connection = createExpensiveConnectionObject()
@classmethod
def tearDown(cls):
cls._connection.destroy()
如果希望setUpClass
和tearDownClass
在基类中被调用,那么你必须自己构建它们。在TestCase
中它们是没有内容的。
如果setUpClass
运行期间抛出了一个异常,那么该类中的所有测试将不再运行,tearDownClass
也不会运行。跳过的类不会运行setUpClass
和tearDownClass
。如果该异常是SkipTest
类型,那么测试类将会反馈一个skipped而不是一个error。
2. setUpModule 和 tearDownModule
它们应该作为封闭为函数:
def setUpModule():
createConnection()
def tearDownModule():
createConnection()
如果setUpModule
运行期间抛出了一个异常,那么该模块中的所有测试将不再运行,tearDownModule
也不会运行。如果异常是SkipTest
类型,那么测试类将会反馈一个skipped而不是一个error。
使用addModuleCleanup
来添加清理的代码,即使用例抛出的异常,它也一定会运行。
unittest.addModuleCleanup(function,/, *argv, **kwargs)
添加一个在tearDownModule
运行之后被调用的函数来清理测试期间使用的资源。函数的调用顺序与它们添加的顺序相反(LIFO)。它们被调用时所使用的参数由addModuleCleanup()
在添加它们时传入。
如果 setUpMoudle()
失败,意味着不会调用tearDownModule()
,但是清理函数仍然会调用。
python3.8版本新增
unittest.doModuleCleanups()
该方法在tearDownModule()
之后被无条件地调用,即使setUpModule
抛出一个异常。
它是用来调用所有被addCleanupModule()
所添加的函数。如果希望在tearDownModule()
之前调用它,你需要自己来调用。
doModuleCleanups()
每调用一次就会从栈中推出一个清理函数并执行它,所以可以在任何时候调用它。
python3.8版本新增
9. 信号控制
unittest的命令行参数-c/–catch,和unittest.main()的catchbreak
参数,为测试的运行提供了一个更友好控制control-C方式。拥有catchbreak特性的control-C允许正在运行的测试用例运行完毕后再停止测试,然后报告目前为止的所有测试结果。如果再次使用control-c将会抛出一个普通的KeyboardInterrupt
异常。
control-c信号控制器希望仍然兼容那些安装了自己的signal.SIGINT控制器的代码和测试。如果调用unittest的控制器而不是已配置的signal.SIGINT控制器,即被测试系统替换并授权,然后调用其默认的控制器。替换一个已安装的控制器并授权通常只是代码的一个额外动作。对单个需要无效化control-c控制的可以使用removeHandler()
装饰器。
有一些对框架作者来说有用的函数,可以让测试框架有效地进行control-c控制。
unittest.installHandler()
安装control-c控制器。当收到一个signal.SIGINT(通常由用户使用control-c时产生)。所有标记的result对象都将调用stop()
方法。
unittest.registerResult(result)
为control-c控制标记一个TestResult
对象。当标记一个result时,会储存它的一个弱引用,因此它并不能防止result收集无用的数据。
当control-c控制未启用时,标记一个TestResult
对象是无副作用的,因此测试框架可以无条件地标记所有各自独立的result对象,不管它们是否启用了控制器。
unittest.removeResult(result)
移除一个result对象的标记。当result对象的标记被移除时,control-c将不会再调用该result对象的stop()
方法。
unittest.removeHandler(function=None)
当没有参数传入时,该方法会移除已安装的control-c控制器。该函数也可以作为装饰器,在测试执行时临时地移除控制器:
@unittest.removeHandler
def test_signal_handling(self):
...