25.3. unittest-单元测试框架

2.1 版中的新Function。

(如果您已经熟悉测试的基本概念,则可能要跳到assert 方法列表。)

Python 单元测试框架(有时也称为“ PyUnit”)是 Kent Beck 和 Erich Gamma 编写的 JUnit 的 Python 语言版本。反过来,JUnit 是 Kent 的 Smalltalk 测试框架的 Java 版本。每个都是针对其相应语言的事实上的标准单元测试框架。

unittest支持测试自动化,共享测试的设置和关闭代码,将测试聚合到集合中以及测试与报告框架的独立性。 unittest模块提供的类可轻松支持一组测试的这些质量。

为此,unittest支持一些重要概念:

passTestCaseFunctionTestCase类支持测试用例和测试夹具概念。在创建新测试时应使用前者,而在将现有测试代码与_3 驱动框架集成时,可以使用后者。使用TestCase构建测试夹具时,可以覆盖setUp()tearDown()方法以提供夹具的初始化和清理。使用FunctionTestCase,可以将现有函数传递给构造函数以实现这些目的。运行测试时,首先运行夹具初始化;然后运行。如果成功,则无论测试结果如何,都将在执行测试后运行 cleanup 方法。 TestCase的每个实例将仅用于运行单个测试方法,因此将为每个测试创建一个新的夹具。

测试套件由TestSuite类实现。此类允许单个测试和测试套件的聚合;执行套件时,将运行直接添加到套件和“子”测试套件中的所有测试。

测试运行程序是提供单个方法run()的对象,该方法接受TestCaseTestSuite对象作为参数,并返回结果对象。提供了TestResult类作为结果对象。 unittest提供了TextTestRunner作为示例测试运行程序,该运行程序默认情况下在标准错误流上报告测试结果。可以在其他环境(例如图形环境)中实现替代运行程序,而无需从特定类派生。

See also

25.3.1. 基本例子

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'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

if __name__ == '__main__':
    unittest.main()

pass子类unittest.TestCase创建一个测试用例。这三个单独的测试使用名称以字母test开头的方法定义。该命名约定将告知测试运行者哪些方法表示测试。

每个测试的症结在于调用assertEqual()以检查预期结果; assertTrue()assertFalse()验证条件;或assertRaises()以验证是否引发了特定异常。使用这些方法代替assert语句,以便测试运行器可以累积所有测试结果并生成报告。

setUp()tearDown()方法允许您定义将在每种测试方法之前和之后执行的指令。它们在组织测试代码部分中有更详细的介绍。

最后一块显示了一种运行测试的简单方法。 unittest.main()提供测试脚本的命令行界面。从命令行运行时,上面的脚本产生的输出看起来像这样:

...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

除了unittest.main()以外,还有其他方法可以以更精细的控制级别,更简洁的输出以及无需从命令行运行的方式运行测试。例如,最后两行可以替换为:

suite = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods)
unittest.TextTestRunner(verbosity=2).run(suite)

从解释器或其他脚本运行修订的脚本会产生以下输出:

test_isupper (__main__.TestStringMethods) ... ok
test_split (__main__.TestStringMethods) ... ok
test_upper (__main__.TestStringMethods) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

上面的示例显示了最常用的unittestFunction,这些Function足以满足许多日常测试需求。本文档的其余部分从第一个原理探讨了完整的Function集。

25.3.2. 命令行界面

可以从命令行使用 unittest 模块从模块,类甚至单个测试方法运行测试:

python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method

您可以传入一个列表,其中包含模块名称以及完全限定的类或方法名称的任意组合。

您可以pass传递-v 标志来运行更详细(更详细)的测试:

python -m unittest -v test_module

有关所有命令行选项的列表:

python -m unittest -h

在 2.7 版中进行了更改:在早期版本中,只能运行单独的测试方法,而不能运行模块或类。

25.3.2.1. 命令行选项

unittest 支持以下命令行选项:

有关提供此Function的Function,请参见Signal Handling

2.7 版中的新增Function:添加了命令行选项-b-c-f

命令行还可以用于测试发现,运行项目中的所有测试或仅运行子集。

25.3.3. 测试发现

2.7 版的新Function。

Unittest 支持简单的测试发现。为了与测试发现兼容,所有测试文件必须可以从项目的顶级目录导入modulespackages(这意味着它们的文件名必须为identifiers)。

测试发现在TestLoader.discover()中实现,但也可以从命令行使用。基本的命令行用法是:

cd project_directory
python -m unittest discover

discover子命令具有以下选项:

-s-p-t选项可以按位置 Sequences 作为位置参数传递。以下两个命令行是等效的:

python -m unittest discover -s project_directory -p "*_test.py"
python -m unittest discover project_directory "*_test.py"

除了作为路径外,还可以将包名称(例如myproject.subpackage.test)作为起始目录。然后,将提供您提供的软件包名称,并将其在文件系统上的位置用作起始目录。

Caution

测试发现pass导入来加载测试。一旦测试发现从开始目录中找到所有测试文件,您就可以指定将路径转换为要导入的程序包名称。例如foo/bar/baz.py将被导入为foo.bar.baz

如果您已全局安装了一个软件包,并try在该软件包的另一个副本上进行测试发现,则导入可能从错误的位置进行。如果发生这种情况,测试发现将警告您并退出。

如果您以包名而不是目录路径的形式提供开始目录,那么 Discover 会假设它从哪个导入位置进入您想要的位置,因此不会收到警告。

测试模块和软件包可以passload_tests protocol自定义测试加载和发现。

25.3.4. 组织测试代码

单元测试的基本构建块是测试用例-必须设置单个场景并检查其正确性。在unittest中,测试用例由unittestTestCase类的实例表示。要制作自己的测试用例,必须编写TestCase的子类,或使用FunctionTestCase

TestCase派生类的实例是可以完全运行单个测试方法以及可选的设置和整理代码的对象。

TestCase实例的测试代码应完全独立,以便可以与任意数量的其他测试用例隔离运行或任意组合运行。

最简单的TestCase子类将简单地覆盖runTest()方法以执行特定的测试代码:

import unittest

class DefaultWidgetSizeTestCase(unittest.TestCase):
    def runTest(self):
        widget = Widget('The widget')
        self.assertEqual(widget.size(), (50, 50), 'incorrect default size')

请注意,为了测试某些内容,我们使用TestCaseBase Class 提供的assert*()方法之一。如果测试失败,则会引发异常,并且unittest将测试用例标识为* failure 。其他任何异常都将被视为 errors *。这可以帮助您确定问题出在哪里:失败是由不正确的结果引起的-5 是您期望的 6.错误是由不正确的代码引起的-例如,由错误的函数调用引起的TypeError

稍后将描述运行测试用例的方法。现在,请注意,要构造这种测试用例的实例,我们将其构造函数称为不带参数的:

testCase = DefaultWidgetSizeTestCase()

现在,这样的测试用例可能很多,并且它们的设置可能是重复的。在上述情况下,在 100 个 Widget 测试用例子类中的每个子类中构造Widget将意味着难看的重复。

幸运的是,我们可以pass实现一个名为setUp()的方法来排除此类设置代码,测试框架将在运行测试时自动调用我们:

import unittest

class SimpleWidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

class DefaultWidgetSizeTestCase(SimpleWidgetTestCase):
    def runTest(self):
        self.assertEqual(self.widget.size(), (50,50),
                         'incorrect default size')

class WidgetResizeTestCase(SimpleWidgetTestCase):
    def runTest(self):
        self.widget.resize(100,150)
        self.assertEqual(self.widget.size(), (100,150),
                         'wrong size after resize')

如果在运行测试时setUp()方法引发异常,则框架将认为测试已出错,并且将不执行runTest()方法。

同样,我们可以提供一种tearDown()方法,该方法在运行runTest()方法后会整理:

import unittest

class SimpleWidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

    def tearDown(self):
        self.widget.dispose()
        self.widget = None

如果setUp()成功,则无论runTest()是否成功,都将运行tearDown()方法。

这样的测试代码工作环境称为* fixture *。

通常,许多小型测试用例将使用相同的夹具。在这种情况下,我们finally会将SimpleWidgetTestCase子类化为许多小的一方法类,例如DefaultWidgetSizeTestCase。这既耗时又令人沮丧,因此与 JUnit 一样,unittest提供了一种更简单的机制:

import unittest

class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

    def tearDown(self):
        self.widget.dispose()
        self.widget = None

    def test_default_size(self):
        self.assertEqual(self.widget.size(), (50,50),
                         'incorrect default size')

    def test_resize(self):
        self.widget.resize(100,150)
        self.assertEqual(self.widget.size(), (100,150),
                         'wrong size after resize')

这里我们没有提供runTest()方法,而是提供了两种不同的测试方法。现在,每个类实例都将运行test_*()方法之一,并为每个实例分别创建和销毁self.widget。创建实例时,必须指定要运行的测试方法。为此,我们在构造函数中传递方法名称:

defaultSizeTestCase = WidgetTestCase('test_default_size')
resizeTestCase = WidgetTestCase('test_resize')

测试用例实例根据它们测试的Function分组在一起。 unittest为此提供了一种机制:* test suite *,由unittestTestSuite类表示:

widgetTestSuite = unittest.TestSuite()
widgetTestSuite.addTest(WidgetTestCase('test_default_size'))
widgetTestSuite.addTest(WidgetTestCase('test_resize'))

为了使测试易于运行,我们将在后面看到,在每个测试模块中提供一个可调用的对象,该对象可返回预构建的测试套件,这是一个好主意:

def suite():
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase('test_default_size'))
    suite.addTest(WidgetTestCase('test_resize'))
    return suite

or even:

def suite():
    tests = ['test_default_size', 'test_resize']

    return unittest.TestSuite(map(WidgetTestCase, tests))

由于创建带有许多类似命名的测试函数的TestCase子类是一种常见的模式,因此unittest提供了TestLoader的类,该类可用于自动化创建测试套件并将其填充到各个测试中的过程。例如,

suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)

将创建一个将运行WidgetTestCase.test_default_size()WidgetTestCase.test_resize的测试套件。 TestLoader使用'test'方法名称前缀来自动识别测试方法。

注意,各种测试用例的运行 Sequences 是pass根据字符串的内置 Sequences 对测试函数名称进行排序来确定的。

通常希望将测试用例套件组合在一起,以便一次为整个系统运行测试。这很容易,因为可以将TestSuite个实例添加到TestSuite就像将TestCase实例可以添加到TestSuite一样:

suite1 = module1.TheTestSuite()
suite2 = module2.TheTestSuite()
alltests = unittest.TestSuite([suite1, suite2])

您可以将测试用例和测试套件的定义与它们要测试的代码放在相同的模块中(例如widget.py),但是将测试代码放在单独的模块中有很多优点,例如test_widget.py

25.3.5. 重用旧的测试代码

一些用户会发现他们已有想要从unittest运行的测试代码,而没有将每个旧的测试函数都转换为TestCase子类。

因此,unittest提供了FunctionTestCase类。 TestCase的此子类可用于包装现有的测试Function。还可以提供设置和拆卸Function。

给定以下测试Function:

def testSomething():
    something = makeSomething()
    assert something.name is not None
    # ...

可以创建一个等效的测试用例实例,如下所示:

testcase = unittest.FunctionTestCase(testSomething)

如果在测试用例的操作中需要调用其他设置和拆卸方法,则也可以这样提供它们:

testcase = unittest.FunctionTestCase(testSomething,
                                     setUp=makeSomethingDB,
                                     tearDown=deleteSomethingDB)

为了简化现有测试套件的迁移,unittest支持提高AssertionError的测试以指示测试失败。但是,建议您改用显式的TestCase.fail*()TestCase.assert*()方法,因为unittest的将来版本可能会区别AssertionError

Note

即使可以使用FunctionTestCase将现有的测试库快速转换为基于unittest的系统,也不建议使用此方法。花时间设置适当的TestCase子类将使将来的测试重构变得更加轻松。

在某些情况下,现有测试可能是使用doctest模块编写的。如果是这样,doctest提供了一个DocTestSuite类,该类可以根据现有基于doctest的测试自动构建unittest.TestSuite实例。

25.3.6. 跳过测试和预期的失败

2.7 版的新Function。

Unittest 支持跳过单个测试方法甚至整个测试类别。此外,它支持将测试标记为“预期失败”,即已损坏且将失败的测试,但不应将其视为TestResult上的失败。

跳过测试仅是使用skip() decorator或其条件变量之一。

基本跳过如下所示:

class MyTestCase(unittest.TestCase):

    @unittest.skip("demonstrating skipping")
    def test_nothing(self):
        self.fail("shouldn't happen")

    @unittest.skipIf(mylib.__version__ < (1, 3),
                     "not supported in this library version")
    def test_format(self):
        # Tests that work for only 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

这是在详细模式下运行上述示例的输出:

test_format (__main__.MyTestCase) ... skipped 'not supported in this library version'
test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping'
test_windows_support (__main__.MyTestCase) ... skipped 'requires Windows'

----------------------------------------------------------------------
Ran 3 tests in 0.005s

OK (skipped=3)

可以像方法一样跳过类:

@unittest.skip("showing class skipping")
class MySkippedTestCase(unittest.TestCase):
    def test_not_run(self):
        pass

TestCase.setUp()也可以跳过测试。当需要设置的资源不可用时,这很有用。

预期的失败使用expectedFailure()装饰器。

class ExpectedFailureTestCase(unittest.TestCase):
    @unittest.expectedFailure
    def test_fail(self):
        self.assertEqual(1, 0, "broken")

pass制作一个在测试中希望跳过测试的调用skip()的装饰器,可以轻松滚动自己的跳过装饰器。除非传递的对象具有某个属性,否则此装饰器将跳过测试:

def skipUnlessHasattr(obj, attr):
    if hasattr(obj, attr):
        return lambda func: func
    return unittest.skip("{!r} doesn't have {!r}".format(obj, attr))

以下装饰器实现了测试跳过和预期的失败:

通常,您可以使用TestCase.skipTest()或跳过的装饰器之一,而不是直接提高它。

跳过的测试不会在它们周围运行setUp()tearDown()。跳过的类不会运行setUpClass()tearDownClass()

25.3.7. 类和Function

本节深入介绍unittest的 API。

25.3.7.1. 测试用例

TestCase的每个实例将运行一个测试方法:名为* methodName *的方法。如果您还记得的话,我们有一个较早的示例,其内容如下:

def suite():
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase('test_default_size'))
    suite.addTest(WidgetTestCase('test_resize'))
    return suite

在这里,我们创建WidgetTestCase的两个实例,每个实例都运行一个测试。

TestCase实例提供三组方法:一组用于运行测试,另一组由测试实现用于检查条件和报告故障,还有一些查询方法允许收集有关测试本身的信息。

第一组(运行测试)的方法是:

@classmethod
def setUpClass(cls):
    ...

有关更多详细信息,请参见类和模块夹具

2.7 版的新Function。

@classmethod
def tearDownClass(cls):
    ...

有关更多详细信息,请参见类和模块夹具

2.7 版的新Function。

只需调用TestCase实例,就可以达到相同的效果。

2.7 版的新Function。

TestCase类提供了几种 assert 方法来检查和报告故障。下表列出了最常用的方法(有关更多 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 2.7
assertIsNot(a, b) a is not b 2.7
assertIsNone(x) x is None 2.7
assertIsNotNone(x) x is not None 2.7
assertIn(a, b) a in b 2.7
assertNotIn(a, b) a not in b 2.7
assertIsInstance(a, b) isinstance(a, b) 2.7
assertNotIsInstance(a, b) not isinstance(a, b) 2.7

所有 assert 方法(除了assertRaises()assertRaisesRegexp()除外)都接受一个* msg *参数,如果指定该参数,该参数将用作失败时的错误消息(另请参见longMessage)。

另外,如果* first second *是完全相同的类型,并且是 list,tuple,dict,set,frozenset 或 unicode 或子类向addTypeEqualityFunc()注册的任何类型之一,则将按 Sequences 调用特定于类型的相等函数生成更有用的默认错误消息(另请参见类型专用方法列表)。

在 2.7 版中进行了更改:添加了自动调用特定于类型的相等函数的Function。

请注意,这等效于bool(expr) is True而不是expr is True(后者使用assertIs(expr, True))。当有更多特定方法可用时(例如assertEqual(a, b)而不是assertTrue(a == b)),也应避免使用此方法,因为在发生故障的情况下,它们可以提供更好的错误消息。

2.7 版的新Function。

2.7 版的新Function。

2.7 版的新Function。

2.7 版的新Function。

也可以使用以下方法检查是否引发了异常和警告:

Method Checks that New in
assertRaises(exc,fun,* args,** kwds) fun(*args, **kwds)加注* exc *
assertRaisesRegexp(exc,r,fun,* args,** kwds) fun(*args, **kwds)引发* exc ,并且消息与正则表达式 r *匹配 2.7

如果仅给出* exception *参数,则返回一个上下文 Management 器,以便可以将内嵌代码而不是作为函数编写被测代码:

with self.assertRaises(SomeException):
    do_something()

上下文 Management 器会将捕获的异常对象存储在其exception属性中。如果要对引发的异常执行其他检查,这将很有用:

with self.assertRaises(SomeException) as cm:
    do_something()

the_exception = cm.exception
self.assertEqual(the_exception.error_code, 3)

在 2.7 版中进行了更改:添加了使用assertRaises()作为上下文 Management 器的Function。

self.assertRaisesRegexp(ValueError, "invalid literal for.*XYZ'$",
                        int, 'XYZ')

or:

with self.assertRaisesRegexp(ValueError, 'literal'):
   int('XYZ')

2.7 版的新Function。

还有其他用于执行更具体检查的方法,例如:

Method Checks that New in
assertAlmostEqual(a, b) round(a-b, 7) == 0
assertNotAlmostEqual(a, b) round(a-b, 7) != 0
assertGreater(a, b) a > b 2.7
assertGreaterEqual(a, b) a >= b 2.7
assertLess(a, b) a < b 2.7
assertLessEqual(a, b) a <= b 2.7
assertRegexpMatches(s, r) r.search(s) 2.7
assertNotRegexpMatches(s, r) not r.search(s) 2.7
assertItemsEqual(a, b) sorted(a)== sorted(b)并与不可哈希的 objs 一起使用 2.7
assertDictContainsSubset(a, b) * a 中的所有键/值对都存在于 b *中 2.7

如果提供了* delta 而不是 places ,那么 first second 之间的差值必须小于或等于(或大于) delta *。

同时提供* delta places *会引发TypeError

在 2.7 版中进行了更改:assertAlmostEqual()自动考虑比较相等的几乎相等的对象。如果对象比较相等,则assertNotAlmostEqual()自动失败。添加了* delta *关键字参数。

>>> self.assertGreaterEqual(3, 4)
AssertionError: "3" unexpectedly not greater than or equal to "4"

2.7 版的新Function。

2.7 版的新Function。

2.7 版的新Function。

比较* acting expected *时,*不可忽略重复的元素。它验证两个序列中每个元素的计数是否相同。它等效于assertEqual(sorted(expected), sorted(actual)),但也适用于不可哈希对象序列。

在 Python 3 中,此方法名为assertCountEqual

2.7 版的新Function。

2.7 版的新Function。

从 3.2 版开始不推荐使用。

assertEqual()方法将相同类型对象的相等性检查分派给不同类型特定的方法。这些方法已经为大多数内置类型实现,但是也可以使用addTypeEqualityFunc()注册新方法:

2.7 版的新Function。

下表总结了assertEqual()自动使用的特定于类型的方法的列表。请注意,通常不必直接调用这些方法。

Method 用于比较 New in
assertMultiLineEqual(a, b) strings 2.7
assertSequenceEqual(a, b) sequences 2.7
assertListEqual(a, b) lists 2.7
assertTupleEqual(a, b) tuples 2.7
assertSetEqual(a, b) 集或冻结集 2.7
assertDictEqual(a, b) dicts 2.7

2.7 版的新Function。

assertEqual()不会直接调用此方法,但是它用于实现assertListEqual()assertTupleEqual()

2.7 版的新Function。

2.7 版的新Function。

如果* set1 set2 *中的任何一个都不具有set.difference()方法,则失败。

2.7 版的新Function。

2.7 版的新Function。

最后,TestCase提供以下方法和属性:

此属性默认为False,这意味着传递到 assert 方法的自定义消息将使普通消息静音。

pass在调用 assert 方法之前将实例属性分配给TrueFalse,可以在各个测试中覆盖类设置。

2.7 版的新Function。

maxDiff设置为None表示没有最大的差异长度。

2.7 版的新Function。

测试框架可以使用以下方法来收集有关测试的信息:

对于TestCase实例,它将始终是TestResult的实例; TestCase的子类应在必要时覆盖此子类。

如果setUp()失败,即未调用tearDown(),则仍将调用添加的任何清除函数。

2.7 版的新Function。

它负责调用addCleanup()添加的所有清除Function。如果您需要在tearDown()之前将清理Function称为* prior *,则可以自己调用doCleanups()

doCleanups()一次将一种方法从清除函数堆栈中弹出,因此可以随时调用它。

2.7 版的新Function。

25.3.7.1.1. 弃用的别名

由于历史原因,某些TestCase方法具有一个或多个别名,现在不建议使用。下表列出了正确的名称及其不赞成使用的别名:

Note

Method Name Deprecated alias(es)
assertEqual() failUnlessEqual, assertEquals
assertNotEqual() failIfEqual
assertTrue() failUnless, assert_
assertFalse() failIf
assertRaises() failUnlessRaises
assertAlmostEqual() failUnlessAlmostEqual
assertNotAlmostEqual() failIfAlmostEqual

从 2.7 版开始不推荐使用:第二列中列出的别名

25.3.7.2. 分组测试

如果给出* tests *,则它必须是单个测试用例或其他最初用于构建套件的测试套件的可迭代项。稍后提供了其他方法来将测试用例和套件添加到集合中。

TestSuite对象的行为与TestCase对象非常相似,不同之处在于它们实际上并未实施测试。相反,它们用于将测试聚合到应一起运行的测试组中。可以使用一些其他方法将测试添加到TestSuite实例:

这等效于迭代* tests *,对每个元素调用addTest()

TestSuiteTestCase共享以下方法:

在 2.7 版中进行了更改:在早期版本中,TestSuite直接访问测试而不是pass迭代访问测试,因此,覆盖iter()不足以提供测试。

TestSuite对象的典型用法中,run()方法是由TestRunner而不是finally用户测试工具调用的。

25.3.7.3. 加载和运行测试

TestLoader对象具有以下方法:

Note

虽然使用TestCase派生的类的层次结构可以方便地共享固定装置和辅助函数,但是在不打算直接实例化的 Base Class 上定义测试方法在此方法中不能很好地发挥作用。但是,当固定装置不同且在子类中定义时,这样做可能会很有用。

如果模块提供load_tests函数,则将调用它以加载测试。这允许模块自定义测试加载。这是load_tests protocol

在 2.7 版中进行了更改:添加了对load_tests的支持。

说明符* name *是一个“点名”,可以解析为模块,测试用例类,测试用例类中的测试方法,TestSuite实例或返回TestCaseTestSuite实例的可调用对象。这些检查按照此处列出的 Sequences 进行;也就是说,可能的测试用例类上的方法将被选择为“测试用例类内的测试方法”,而不是“可调用对象”。

例如,如果您有一个包含TestCase派生类SampleTestCase的模块SampleTests以及三个测试方法(test_one()test_two()test_three()),则说明符'SampleTests.SampleTestCase'将导致此方法返回将运行所有三个测试方法的套件。使用说明符'SampleTests.SampleTestCase.test_two'将导致它返回仅运行test_two()测试方法的测试套件。该说明符可以引用尚未导入的模块和包。它们将作为副作用导入。

该方法可选地相对于给定的* module 解析 name *。

所有测试模块必须可从项目的顶层导入。如果起始目录不是顶级目录,则必须单独指定顶级目录。

如果导入模块失败,例如由于语法错误,则将其记录为单个错误,并且发现将 continue。

如果测试软件包名称(带有_的目录)与该模式匹配,则将检查该软件包的load_tests函数。如果存在,则将使用* loader tests pattern *进行调用。

如果存在 load_tests,则发现不会递归到软件包中,load_tests负责加载软件包中的所有测试。

故意不将模式存储为 loader 属性,以便程序包可以自己 continue 发现。 * top_level_dir *已存储,因此load_tests无需将此参数传递给loader.discover()

2.7 版的新Function。

TestLoader的以下属性可以pass子类化或在实例上分配来配置:

这会影响getTestCaseNames()和所有loadTestsFrom*()方法。

这会影响所有loadTestsFrom*()方法。

TestResult对象存储一组测试的结果。 TestCaseTestSuite类可确保正确记录结果;测试作者无需担心记录测试结果。

构建在unittest之上的测试框架可能希望访问pass运行一组测试生成的TestResult对象以进行报告。为此,TestRunner.run()方法返回一个TestResult实例。

TestResult实例具有以下属性,这些属性在检查运行一组测试的结果时会引起关注:

在版本 2.2 中进行了更改:包含格式化的回溯,而不是sys.exc_info()个结果。

在版本 2.2 中进行了更改:包含格式化的回溯,而不是sys.exc_info()个结果。

2.7 版的新Function。

2.7 版的新Function。

2.7 版的新Function。

例如,当用户从键盘发出break signal 时,TextTestRunner类将使用此Function来停止测试框架。提供TestRunner实现的交互式工具可以类似的方式使用它。

TestResult类的以下方法用于维护内部数据结构,并且可以在子类中扩展以支持其他报告要求。这对于在运行测试时支持交互式报告的构建工具特别有用。

2.7 版的新Function。

2.7 版的新Function。

默认实现将 Tuples(test, formatted_err)附加到实例的errors属性,其中* formatted_err 是从 err *派生的格式化回溯。

默认实现将 Tuples(test, formatted_err)附加到实例的failures属性,其中* formatted_err 是从 err *派生的格式化回溯。

默认实现不执行任何操作。

默认实现将 Tuples(test, reason)附加到实例的skipped属性。

默认实现将 Tuples(test, formatted_err)附加到实例的expectedFailures属性,其中* formatted_err 是从 err *派生的格式化回溯。

默认实现将测试附加到实例的unexpectedSuccesses属性。

2.7 版中的新Function:此类以前称为_TextTestResult。旧名称仍然作为别名存在,但已过时。

_makeResult()实例化TextTestRunner构造函数中作为resultclass参数传递的类或可调用对象。如果未提供resultclass,则默认为TextTestResult。结果类使用以下参数实例化:

stream, descriptions, verbosity
if __name__ == '__main__':
    unittest.main()

您可以pass传入 verbosity 参数来运行包含更详细信息的测试:

if __name__ == '__main__':
    unittest.main(verbosity=2)

如果没有pass* argv 指定测试名称,则 defaultTest 参数是要运行的测试的名称。如果未指定或None并且没有pass argv 提供测试名称,则将运行 module *中找到的所有测试。

main支持pass传入参数exit=False从交互式解释器中使用。这将在标准输出上显示结果,而无需调用sys.exit()

>>> from unittest import main
>>> main(module='test_module', exit=False)

调用main实际上会返回TestProgram类的实例。这会将测试结果存储为result属性。

在 2.7 版中进行了更改:添加了* exit verbosity failfast catchbreak buffer *参数。

25.3.7.3.1. load_tests 协议

2.7 版的新Function。

模块或软件包可以pass实现称为load_tests的Function来自定义在正常测试运行或测试发现期间如何从中加载测试。

如果测试模块定义了load_tests,它将由TestLoader.loadTestsFromModule()调用,并带有以下参数:

load_tests(loader, standard_tests, None)

它应该返回TestSuite

从一组特定的TestCase类加载测试的典型load_tests函数可能类似于:

test_cases = (TestCase1, TestCase2, TestCase3)

def load_tests(loader, tests, pattern):
    suite = TestSuite()
    for test_class in test_cases:
        tests = loader.loadTestsFromTestCase(test_class)
        suite.addTests(tests)
    return suite

如果从命令行或pass调用TestLoader.discover()(使用与程序包名称匹配的模式)启动发现,则将在程序包__init__.py中检查load_tests

Note

默认模式是'test*.py'。这会匹配以'test'开头但不会匹配任何测试目录的所有 Python 文件。

'test*'这样的模式将匹配测试包和模块。

如果程序包__init__.py定义了load_tests,则它将被调用,并且发现不会 continue 进入程序包。 load_tests使用以下参数调用:

load_tests(loader, standard_tests, pattern)

这应该返回一个TestSuite,代表该程序包中的所有测试。 (standard_tests仅包含从__init__.py收集的测试.)

由于该模式已传递到load_tests中,因此程序包可以自由 continue(并可能修改)测试发现。测试包的“不执行任何操作” load_tests函数如下所示:

def load_tests(loader, standard_tests, pattern):
    # top level directory cached on loader instance
    this_dir = os.path.dirname(__file__)
    package_tests = loader.discover(start_dir=this_dir, pattern=pattern)
    standard_tests.addTests(package_tests)
    return standard_tests

25.3.8. 类和模块夹具

类和模块级别的固定装置在TestSuite中实现。当测试套件遇到来自新类的测试时,则调用前一类的tearDownClass()(如果有),然后调用新类的setUpClass()

同样,如果测试来自与先前测试不同的模块,则运行来自先前模块的tearDownModule,然后运行来自新模块的setUpModule

完成所有测试后,将运行finally的tearDownClasstearDownModule

请注意,共享夹具不能与测试并行化等[潜在]Function配合使用,并且会破坏测试隔离。应该小心使用它们。

由 unittest 测试加载程序创建的测试的默认 Sequences 是将来自相同模块和类的所有测试组合在一起。这将导致每个类和模块一次调用setUpClass/setUpModule(等)。如果您将 Sequences 随机化,以使来自不同模块和类的测试彼此相邻,则可以在一次测试运行中多次调用这些共享的夹具Function。

共享夹具不适用于非标准 Order 的套件。 BaseTestSuite对于不希望支持共享装置的框架仍然存在。

如果在共享夹具Function之一期间引发任何异常,则将测试报告为错误。因为没有相应的测试实例,所以创建了_ErrorHolder对象(与TestCase具有相同的接口)来表示错误。如果您只是使用标准的单元测试测试运行程序,那么此细节并不重要,但是如果您是框架作者,则可能很重要。

25.3.8.1. setUpClass 和 tearDownClass

这些必须作为类方法实现:

import unittest

class Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls._connection = createExpensiveConnectionObject()

    @classmethod
    def tearDownClass(cls):
        cls._connection.destroy()

如果要在 Base Class 上调用setUpClasstearDownClass,则必须自己调用它们。 TestCase中的实现为空。

如果在setUpClass期间引发异常,则不会运行该类中的测试,也不会运行tearDownClass。跳过的类不会运行setUpClasstearDownClass。如果该异常是SkipTest异常,则该类将被报告为已跳过而不是错误。

25.3.8.2. setUpModule 和 tearDownModule

这些应实现为Function:

def setUpModule():
    createConnection()

def tearDownModule():
    closeConnection()

如果setUpModule中引发异常,则模块中的任何测试都不会运行,而tearDownModule也不会运行。如果异常是SkipTest异常,则模块将被报告为已跳过而不是错误。

25.3.9. signal 处理

单元测试的-c/--catch命令行选项,以及unittest.main()catchbreak参数,在测试运行期间提供了对 Control-C 的更友好处理。启用捕获break行为后,control-C 将允许当前运行的测试完成,然后测试运行将结束并报告到目前为止的所有结果。第二个 Control-c 将以通常方式提高KeyboardInterrupt

控件 c 处理 signal 处理程序try与安装自己的signal.SIGINT处理程序的代码或测试保持兼容。如果调用unittest处理程序但不是*已安装的signal.SIGINT处理程序,即它已被测试系统替换并委托给它,则它将调用默认处理程序。pass替换已安装的处理程序并委托给它的代码,这通常是预期的行为。对于需要禁用unittest control-c 处理的单个测试,可以使用removeHandler()装饰器。

有一些 Util Function可供框架作者使用,以在测试框架中启用 Control-C 处理Function。

2.7 版的新Function。

如果未启用 control-c 处理,则注册TestResult对象没有副作用,因此测试框架可以无条件地注册其创建的所有结果,而与是否启用处理无关。

2.7 版的新Function。

2.7 版的新Function。

@unittest.removeHandler
def test_signal_handling(self):
    ...

2.7 版的新Function。

首页