unittest — 单元测试框架

源代码: Lib/unittest/__init__.py


(如果你已经熟悉测试的基本概念,可以跳过 断言方法列表。)

unittest 单元测试框架最初受到 JUnit 的启发,并且与其他语言中的主要单元测试框架具有相似的风格。它支持测试自动化、共享测试的设置和关闭代码、将测试聚合到集合中以及测试与报告框架无关。

为了实现这一点,unittest 以面向对象的方式支持一些重要概念

测试固定装置

测试固定装置 表示执行一个或多个测试所需的准备以及任何相关的清理操作。例如,这可能涉及创建临时或代理数据库、目录或启动服务器进程。

测试用例

测试用例 是测试的单个单元。它检查对特定输入集的特定响应。unittest 提供了一个基类 TestCase,可用于创建新的测试用例。

测试套件

测试套件 是测试用例、测试套件或两者的集合。它用于聚合应该一起执行的测试。

测试运行器

一个测试运行器是一个协调测试执行并将结果提供给用户的组件。运行器可以使用图形界面、文本界面或返回一个特殊值来指示执行测试的结果。

另请参见

模块 doctest

另一个风味截然不同的测试支持模块。

简单的 Smalltalk 测试:使用模式

Kent Beck 关于使用 unittest 共享的模式的测试框架的原始论文。

pytest

第三方单元测试框架,具有更轻量级的语法来编写测试。例如,assert func(10) == 42

Python 测试工具分类

一个广泛的 Python 测试工具列表,包括功能测试框架和模拟对象库。

Python 邮件列表中的测试

一个专门讨论 Python 中的测试和测试工具的兴趣小组。

Python 源代码分发中的脚本 Tools/unittestgui/unittestgui.py 是一个用于测试发现和执行的 GUI 工具。这在很大程度上是为了方便那些刚接触单元测试的人使用。对于生产环境,建议由持续集成系统(如 BuildbotJenkinsGitHub ActionsAppVeyor)来驱动测试。

基本示例

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()

通过对 unittest.TestCase 进行子类化来创建测试用例。三个单独的测试使用以字母 test 开头的名称的方法来定义。此命名约定告知测试运行器哪些方法表示测试。

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

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

最后一个块显示了运行测试的简单方法。 unittest.main() 为测试脚本提供了一个命令行界面。从命令行运行时,上述脚本会生成类似以下内容的输出

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

OK

-v 选项传递给测试脚本将指示 unittest.main() 启用更高级别的详细程度,并生成以下输出

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

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

OK

以上示例展示了最常用的 unittest 特性,足以满足许多日常测试需求。本文档的其余部分将从基本原理探讨全部特性集。

3.11 版中已更改: 从测试方法返回一个值(除了默认 None 值)的行为现已弃用。

命令行界面

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

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

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

还可以通过文件路径指定测试模块

python -m unittest tests/test_something.py

这允许您使用 shell 文件名补全来指定测试模块。指定的仍必须可以作为模块导入。通过移除“‘.py’”并将路径分隔符转换为“‘.’”,将路径转换为模块名称。如果您想执行无法作为模块导入的测试文件,则应该直接执行该文件。

您可以通过传入 -v 标志来更详细地运行测试(更高级别的详细程度)

python -m unittest -v test_module

在没有参数的情况下执行时,将启动 测试发现

python -m unittest

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

python -m unittest -h

3.2 版中已更改: 在早期版本中,只能运行单个测试方法,而不能运行模块或类。

命令行选项

unittest 支持以下命令行选项

-b, --buffer

在测试运行期间,标准输出和标准错误流将被缓冲。通过测试期间的输出将被丢弃。在测试失败或出错时,输出将正常回显,并添加到失败消息中。

-c, --catch

Control-C 在测试运行期间等待当前测试结束,然后报告到目前为止的所有结果。第二次 Control-C 会引发正常的 KeyboardInterrupt 异常。

有关提供此功能的函数,请参见 信号处理

-f, --failfast

在第一个错误或失败时停止测试运行。

-k

仅运行与模式或子字符串匹配的测试方法和类。此选项可多次使用,在这种情况下,将包含与任何给定模式匹配的所有测试用例。

包含通配符字符 (*) 的模式使用 fnmatch.fnmatchcase() 与测试名称进行匹配;否则,使用简单的区分大小写的子字符串匹配。

模式与测试加载器导入的完全限定的测试方法名称进行匹配。

例如,-k foo 匹配 foo_tests.SomeTest.test_somethingbar_tests.SomeTest.test_foo,但不匹配 bar_tests.FooTest.test_something

--locals

在回溯中显示局部变量。

--durations N

显示 N 个最慢的测试用例(N=0 表示全部)。

在 3.2 版中添加: 添加了命令行选项 -b-c-f

在版本 3.5 中添加: 命令行选项 --locals

在版本 3.7 中添加: 命令行选项 -k

在版本 3.12 中添加: 命令行选项 --durations

命令行还可以用于测试发现,用于运行项目中的所有测试或仅运行其中一部分。

测试发现

在版本 3.2 中添加。

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_directory -p "*_test.py"
python -m unittest discover project_directory "*_test.py"

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

注意

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

如果您已全局安装了一个包,并尝试对该包的不同副本进行测试发现,则导入可能会从错误的位置发生。如果发生这种情况,测试发现会向您发出警告并退出。

如果您以包名称而不是目录路径提供起始目录,则发现会假定它导入的任何位置都是您想要的位置,因此您不会收到警告。

测试模块和包可以通过 load_tests 协议来自定义测试加载和发现。

版本 3.4 中已更改: 测试发现支持起始目录的 命名空间包。请注意,您还需要指定顶级目录(例如 python -m unittest discover -s root/namespace -t root)。

版本 3.11 中已更改: unittest 在 Python 3.11 中取消了对 命名空间包 的支持。它自 Python 3.7 以来一直处于损坏状态。包含测试的起始目录和子目录必须是具有 __init__.py 文件的常规包。

包含起始目录的目录仍然可以是命名空间包。在这种情况下,您需要将起始目录指定为带点的包名称,并显式指定目标目录。例如

# proj/  <-- current directory
#   namespace/
#     mypkg/
#       __init__.py
#       test_mypkg.py

python -m unittest discover -s namespace.mypkg -t .

组织测试代码

单元测试的基本构建模块是测试用例——必须设置并检查其正确性的单个场景。在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))

请注意,为了测试某些内容,我们使用assert* 方法之一,该方法由TestCase基类提供。如果测试失败,将引发一个带有解释性消息的异常,并且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 提供了一种机制:测试套件,由 unittestTestSuite 类表示。在大多数情况下,调用 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 编写的模块的测试必须在单独的模块中进行,那么为什么不保持一致呢?

  • 如果测试策略发生变化,则无需更改源代码。

重新使用旧的测试代码

一些用户会发现他们有现有的测试代码,他们希望从 unittest 运行这些代码,而无需将每个旧测试函数转换为 TestCase 子类。

出于此原因,unittest 提供了一个 FunctionTestCase 类。此 TestCase 子类可用于包装现有的测试函数。还可以提供设置和拆除函数。

给定以下测试函数

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

可以按照以下方式创建一个等效的测试用例实例,并带有可选的设置和拆除方法

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

注意

虽然 FunctionTestCase 可用于将现有的测试基础快速转换为基于 unittest 的系统,但此方法并不推荐。花时间设置适当的 TestCase 子类将使将来的测试重构变得无限容易。

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

跳过测试和预期失败

在版本 3.1 中添加。

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

跳过测试只需使用 skip() 装饰器 或其条件变体之一,在 setUp() 或测试方法中调用 TestCase.skipTest(),或直接引发 SkipTest

基本跳过如下所示

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

    def test_maybe_skipped(self):
        if not external_resource_available():
            self.skipTest("external resource not available")
        # test code that depends on the external resource
        pass

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

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

----------------------------------------------------------------------
Ran 4 tests in 0.005s

OK (skipped=4)

类可以像方法一样跳过

@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")

通过制作一个在需要跳过测试时对其调用 skip() 的装饰器,可以轻松地实现自己的跳过装饰器。除非传递的对象具有某个属性,否则此装饰器会跳过测试

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)

无条件跳过装饰的测试。reason 应描述跳过测试的原因。

@unittest.skipIf(condition, reason)

如果 condition 为真,则跳过装饰的测试。

@unittest.skipUnless(condition, reason)

除非 condition 为真,否则跳过装饰的测试。

@unittest.expectedFailure

将测试标记为预期失败或错误。如果测试在测试函数本身(而不是在 测试固定装置 方法之一中)失败或出错,则它将被视为成功。如果测试通过,则将被视为失败。

异常 unittest.SkipTest(原因)

此异常用于跳过测试。

通常,你可以使用 TestCase.skipTest() 或其中一个跳过装饰器,而不是直接引发此异常。

跳过的测试不会有 setUp()tearDown() 在其周围运行。跳过的类不会有 setUpClass()tearDownClass() 运行。跳过的模块不会有 setUpModule()tearDownModule() 运行。

使用子测试区分测试迭代

在 3.4 版本中添加。

当你的测试之间存在非常小的差异时,例如一些参数,unittest 允许你使用 subTest() 上下文管理器在测试方法的主体中区分它们。

例如,以下测试

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)

将产生以下输出

======================================================================
FAIL: test_even (__main__.NumbersTest.test_even) (i=1)
Test that numbers between 0 and 5 are all even.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subtests.py", line 11, in test_even
    self.assertEqual(i % 2, 0)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1 != 0

======================================================================
FAIL: test_even (__main__.NumbersTest.test_even) (i=3)
Test that numbers between 0 and 5 are all even.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subtests.py", line 11, in test_even
    self.assertEqual(i % 2, 0)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1 != 0

======================================================================
FAIL: test_even (__main__.NumbersTest.test_even) (i=5)
Test that numbers between 0 and 5 are all even.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subtests.py", line 11, in test_even
    self.assertEqual(i % 2, 0)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1 != 0

如果不使用子测试,则执行将在第一次失败后停止,并且错误将更难诊断,因为 i 的值将不会显示

======================================================================
FAIL: test_even (__main__.NumbersTest.test_even)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "subtests.py", line 32, in test_even
    self.assertEqual(i % 2, 0)
AssertionError: 1 != 0

类和函数

本节详细描述了 unittest 的 API。

测试用例

unittest.TestCase(methodName='runTest')

TestCase 类的实例表示 unittest 世界中的逻辑测试单元。此类旨在用作基类,具体测试由具体子类实现。此类实现了测试运行器需要的接口,以便它可以驱动测试,以及测试代码可用于检查和报告各种类型的失败的方法。

每个 TestCase 实例将运行一个基本方法:名为 methodName 的方法。在大多数 TestCase 用法中,您既不会更改 methodName,也不会重新实现默认 runTest() 方法。

版本 3.2 中已更改: TestCase 可以在不提供 methodName 的情况下成功实例化。这使得从交互式解释器中试验 TestCase 变得更加容易。

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

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

setUp()

用于准备测试夹具的方法。这在调用测试方法之前立即调用;除了 AssertionErrorSkipTest,此方法引发的任何异常都将被视为错误,而不是测试失败。默认实现不执行任何操作。

tearDown()

在调用测试方法并记录结果后立即调用的方法。即使测试方法引发了异常,也会调用此方法,因此子类中的实现可能需要特别小心地检查内部状态。除了 AssertionErrorSkipTest,此方法引发的任何异常都将被视为附加错误,而不是测试失败(从而增加报告的错误总数)。无论测试方法的结果如何,只有在 setUp() 成功时才会调用此方法。默认实现不执行任何操作。

setUpClass()

在运行单个类中的测试之前调用的类方法。 setUpClass 以类作为唯一参数调用,并且必须装饰为 classmethod()

@classmethod
def setUpClass(cls):
    ...

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

在版本 3.2 中添加。

tearDownClass()

在单个类中的测试运行后调用的类方法。 tearDownClass 仅以类为参数调用,并且必须装饰为 classmethod()

@classmethod
def tearDownClass(cls):
    ...

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

在版本 3.2 中添加。

run(result=None)

运行测试,将结果收集到作为 result 传递的 TestResult 对象中。如果省略 resultNone,则创建一个临时结果对象(通过调用 defaultTestResult() 方法),并使用该对象。结果对象返回给 run() 的调用方。

只需调用 TestCase 实例即可产生相同的效果。

在 3.3 版本中更改: run 的早期版本不会返回结果。调用实例也不会返回结果。

skipTest(reason)

在测试方法或 setUp() 期间调用此方法会跳过当前测试。有关更多信息,请参见 跳过测试和预期失败

在版本 3.1 中添加。

subTest(msg=None, **params)

返回一个上下文管理器,该管理器将随附的代码块作为子测试执行。msgparams 是可选的任意值,当子测试失败时会显示这些值,以便你可以清楚地识别它们。

一个测试用例可以包含任意数量的子测试声明,并且可以任意嵌套。

有关更多信息,请参见 使用子测试区分测试迭代

在 3.4 版本中添加。

debug()

在不收集结果的情况下运行测试。这允许将测试引发的异常传播给调用方,并且可用于支持在调试器下运行测试。

TestCase 提供了多种断言方法来检查和报告失败。下表列出了最常用的方法(有关更多断言方法,请参见下表)

方法

检查

新版

assertEqual(a, b)

a == b

assertNotEqual(a, b)

a != b

assertTrue(x)

bool(x) True

assertFalse(x)

bool(x) False

assertIs(a, b)

a b

3.1

assertIsNot(a, b)

a 不为 b

3.1

assertIsNone(x)

x None

3.1

assertIsNotNone(x)

x 不为 None

3.1

assertIn(a, b)

a b

3.1

assertNotIn(a, b)

a 不在 b

3.1

assertIsInstance(a, b)

isinstance(a, b)

3.2

assertNotIsInstance(a, b)

not isinstance(a, b)

3.2

所有断言方法都接受一个 msg 参数,如果指定,则在失败时将其用作错误消息(另请参见 longMessage)。请注意,当将 msg 关键字参数传递给 assertRaises()assertRaisesRegex()assertWarns()assertWarnsRegex() 时,只能在将它们用作上下文管理器时使用。

assertEqual(first, second, msg=None)

测试 firstsecond 是否相等。如果值不相等,则测试将失败。

此外,如果firstsecond 是完全相同的类型,并且是列表、元组、字典、集合、冻结集合或字符串,或者任何子类使用 addTypeEqualityFunc() 注册的类型,则将调用特定于类型的相等函数,以生成更有用的默认错误消息(另请参阅 特定于类型的函数列表)。

3.1 版中更改: 添加了特定于类型的相等函数的自动调用。

3.2 版中更改: 添加 assertMultiLineEqual() 作为比较字符串的默认类型相等函数。

assertNotEqual(first, second, msg=None)

测试 firstsecond 是否不相等。如果值相等,测试将失败。

assertTrue(expr, msg=None)
assertFalse(expr, msg=None)

测试 expr 是否为真(或假)。

请注意,这等效于 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)

测试firstsecond是否(或不是)同一对象。

在版本 3.1 中添加。

assertIsNone(expr, msg=None)
assertIsNotNone(expr, msg=None)

测试expr是否(或不是)None

在版本 3.1 中添加。

assertIn(member, container, msg=None)
assertNotIn(member, container, msg=None)

测试member是否(或不是)在container中。

在版本 3.1 中添加。

assertIsInstance(obj, cls, msg=None)
assertNotIsInstance(obj, cls, msg=None)

测试obj是否(或不是)cls的一个实例(它可以是类或类的元组,如isinstance()所支持的)。要检查确切的类型,请使用assertIs(type(obj), cls)

在版本 3.2 中添加。

还可以使用以下方法检查异常、警告和日志消息的生成

方法

检查

新版

assertRaises(exc, fun, *args, **kwds)

fun(*args, **kwds)引发exc

assertRaisesRegex(exc, r, fun, *args, **kwds)

fun(*args, **kwds)引发exc,并且该消息与正则表达式r匹配

3.1

assertWarns(warn, fun, *args, **kwds)

fun(*args, **kwds)引发warn

3.2

assertWarnsRegex(warn, r, fun, *args, **kwds)

fun(*args, **kwds)引发warn,并且该消息与正则表达式r匹配

3.2

assertLogs(logger, level)

with块在logger上记录最低level

3.4

assertNoLogs(logger, level)

with块不会在logger上记录最低level

logger with minimum level

3.10

assertRaises(exception, callable, *args, **kwds)
assertRaises(exception, *, msg=None)

测试当使用传递给assertRaises()的任何位置或关键字参数调用callable时引发异常。如果引发exception,则测试通过;如果引发另一个异常,则为错误;如果没有引发异常,则失败。要捕获一组异常中的任何一个,可以将包含异常类的元组作为exception传递。

如果只给出exception和可能的msg参数,则返回上下文管理器,以便可以将被测代码内联编写,而不是作为函数编写

with self.assertRaises(SomeException):
    do_something()

当用作上下文管理器时,assertRaises() 接受附加关键字参数 msg

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

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

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

在版本 3.1 中更改: 添加了将 assertRaises() 用作上下文管理器的功能。

在版本 3.2 中更改: 添加了 exception 属性。

在版本 3.3 中更改: 在用作上下文管理器时添加了 msg 关键字参数。

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')

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

在版本 3.1 中添加: assertRaisesRegexp 的名称添加。

在版本 3.2 中更改: 重命名为 assertRaisesRegex()

在版本 3.3 中更改: 在用作上下文管理器时添加了 msg 关键字参数。

assertWarns(warning, callable, *args, **kwds)
assertWarns(warning, *, msg=None)

callable被调用时,如果callable带有任何位置参数或关键字参数,并且这些参数也传递给了assertWarns(),则测试会触发一个警告。如果触发了warning,则测试通过,否则测试失败。任何异常都是错误。要捕获一组警告中的任何警告,可以将包含警告类的元组作为warnings传递。

如果只给出了warning和可能的msg参数,则返回一个上下文管理器,以便可以内联编写被测代码,而不是作为函数编写

with self.assertWarns(SomeWarning):
    do_something()

当用作上下文管理器时,assertWarns()接受附加的关键字参数msg

上下文管理器会将捕获的警告对象存储在其warning属性中,并将触发警告的源代码行存储在filenamelineno属性中。如果打算对捕获的警告执行其他检查,这将很有用

with self.assertWarns(SomeWarning) as cm:
    do_something()

self.assertIn('myfile.py', cm.filename)
self.assertEqual(320, cm.lineno)

无论在调用此方法时设置了哪些警告筛选器,此方法都能正常工作。

在版本 3.2 中添加。

在版本 3.3 中更改: 在用作上下文管理器时添加了 msg 关键字参数。

assertWarnsRegex(warning, regex, callable, *args, **kwds)
assertWarnsRegex(warning, regex, *, msg=None)

assertWarns()类似,但还测试了regex是否与触发警告的消息匹配。regex可以是正则表达式对象,也可以是包含正则表达式的字符串,该正则表达式适用于re.search()。示例

self.assertWarnsRegex(DeprecationWarning,
                      r'legacy_function\(\) is deprecated',
                      legacy_function, 'XYZ')

with self.assertWarnsRegex(RuntimeWarning, 'unsafe frobnicating'):
    frobnicate('/etc/passwd')

在版本 3.2 中添加。

在版本 3.3 中更改: 在用作上下文管理器时添加了 msg 关键字参数。

assertLogs(logger=None, level=None)

一个上下文管理器,用于测试是否至少在logger或其子项之一上记录了一条消息,并且至少具有给定的level

如果给出了logger,则它应该是logging.Logger对象或str,给出日志记录器的名称。默认值是根日志记录器,它将捕获所有未被非传播后代日志记录器阻止的消息。

如果给定,level 应为一个数字日志级别或其字符串等效项(例如 "ERROR"logging.ERROR)。默认值为 logging.INFO

如果 with 块内发出的至少一条消息与 loggerlevel 条件匹配,则测试通过;否则,测试失败。

上下文管理器返回的对象是一个记录帮助器,它跟踪匹配的日志消息。它有两个属性

records

匹配的日志消息的 logging.LogRecord 对象列表。

output

匹配消息的格式化输出的 str 对象列表。

示例

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'])

在 3.4 版本中添加。

assertNoLogs(logger=None, level=None)

一个上下文管理器,用于测试是否未在 logger 或其子项上记录任何消息,且至少具有给定的 level

如果给定,logger 应为 logging.Logger 对象或 str,给出日志记录器的名称。默认值是根日志记录器,它将捕获所有消息。

如果给定,level 应为一个数字日志级别或其字符串等效项(例如 "ERROR"logging.ERROR)。默认值为 logging.INFO

assertLogs() 不同,上下文管理器不会返回任何内容。

在版本 3.10 中添加。

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

方法

检查

新版

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)

ab 中的元素相同,数量相同,顺序无关。

3.2

assertAlmostEqual(first, second, places=7, msg=None, delta=None)
assertNotAlmostEqual(first, second, places=7, msg=None, delta=None)

通过计算差值、舍入到给定的十进制小数位(默认值为 7)并与零进行比较,测试firstsecond 是否近似(或不近似)相等。请注意,这些方法将值舍入到给定的小数位(即类似于 round() 函数),而不是有效数字

如果提供了 delta 而不是 places,则 firstsecond 之间的差值必须小于或等于(或大于)delta

同时提供 deltaplaces 会引发 TypeError

3.2 版中更改:assertAlmostEqual() 会自动考虑比较相等的近似相等对象。assertNotAlmostEqual() 如果对象比较相等,则会自动失败。添加了 delta 关键字自变量。

assertGreater(first, second, msg=None)
assertGreaterEqual(first, second, msg=None)
assertLess(first, second, msg=None)
assertLessEqual(first, second, msg=None)

测试 first 分别 >、>=、< 或 <= second,具体取决于方法名称。如果不是,则测试将失败

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

在版本 3.1 中添加。

assertRegex(text, regex, msg=None)
assertNotRegex(text, regex, msg=None)

测试 regex 搜索是否匹配(或不匹配)text。如果失败,错误消息将包括模式和 text(或模式和意外匹配的 text 部分)。regex 可以是正则表达式对象,也可以是包含正则表达式的字符串,适合 re.search() 使用。

3.1 版中新增: assertRegexpMatches 名称添加。

3.2 版中更改: 方法 assertRegexpMatches() 已重命名为 assertRegex()

3.2 版中新增: assertNotRegex()

assertCountEqual(first, second, msg=None)

测试序列first包含与second相同的元素,而不管它们的顺序如何。如果不相同,将生成一条列出序列之间差异的错误消息。

比较firstsecond时,不会忽略重复元素。它验证每个元素在两个序列中是否具有相同的计数。相当于:assertEqual(Counter(list(first)), Counter(list(second))),但也可以用于不可哈希对象的序列。

在版本 3.2 中添加。

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

addTypeEqualityFunc(typeobj, function)

注册一个特定类型的检查方法,该方法由assertEqual()调用,以检查两个完全相同的typeobj(不是子类)的对象是否相等。function必须采用两个位置参数和第三个msg=None关键字参数,就像assertEqual()一样。当检测到前两个参数之间不相等时,它必须引发self.failureException(msg)——可能在错误消息中提供有用的信息并详细解释不相等的情况。

在版本 3.1 中添加。

assertEqual()自动使用的特定类型检查方法列表总结在以下表格中。请注意,通常不需要直接调用这些方法。

方法

用于比较

新版

assertMultiLineEqual(a, b)

字符串

3.1

assertSequenceEqual(a, b)

序列

3.1

assertListEqual(a, b)

列表

3.1

assertTupleEqual(a, b)

元组

3.1

assertSetEqual(a, b)

集合或冻结集合

3.1

assertDictEqual(a, b)

字典

3.1

assertMultiLineEqual(first, second, msg=None)

测试多行字符串first是否等于字符串second。如果不相等,则错误消息中将包含突出显示差异的两个字符串的差异。在使用assertEqual()比较字符串时,默认使用此方法。

在版本 3.1 中添加。

assertSequenceEqual(first, second, msg=None, seq_type=None)

测试两个序列是否相等。如果提供了seq_type,则firstsecond都必须是seq_type的实例,否则将引发错误。如果序列不同,则会构造一条错误消息,显示两者之间的差异。

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

在版本 3.1 中添加。

assertListEqual(first, second, msg=None)
assertTupleEqual(first, second, msg=None)

测试两个列表或元组是否相等。如果不相等,则会构造一条错误消息,其中仅显示两者之间的差异。如果任一参数类型错误,也会引发错误。在使用assertEqual()比较列表或元组时,默认使用这些方法。

在版本 3.1 中添加。

assertSetEqual(first, second, msg=None)

测试两组是否相等。如果不相等,则构造一条错误消息,其中列出两组之间的差异。在使用 assertEqual() 比较集合或冻结集合时,默认使用此方法。

如果 firstsecond 中任何一个没有 set.difference() 方法,则失败。

在版本 3.1 中添加。

assertDictEqual(first, second, msg=None)

测试两个字典是否相等。如果不相等,则构造一条错误消息,其中显示字典中的差异。在调用 assertEqual() 时,默认使用此方法来比较字典。

在版本 3.1 中添加。

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

fail(msg=None)

无条件地发出测试失败信号,其中 msgNone 表示错误消息。

failureException

此类属性给出测试方法引发的异常。如果测试框架需要使用专门的异常(可能携带其他信息),则必须对此异常进行子类化,以便与框架“公平竞争”。此属性的初始值为 AssertionError

longMessage

此类属性确定当将自定义失败消息作为 msg 参数传递给失败的 assertXYY 调用时会发生什么情况。 True 是默认值。在这种情况下,自定义消息将附加到标准失败消息的末尾。当设置为 False 时,自定义消息将替换标准消息。

可以通过在调用 assert 方法之前将实例属性 self.longMessage 分配给 TrueFalse 来在各个测试方法中覆盖类设置。

类设置在每次测试调用之前都会重置。

在版本 3.1 中添加。

maxDiff

此属性控制在失败时报告差异的断言方法输出的差异的最大长度。它默认为 80*8 个字符。受此属性影响的断言方法为 assertSequenceEqual()(包括所有委托给它的序列比较方法)、assertDictEqual()assertMultiLineEqual()

maxDiff 设置为 None 意味着没有差异的最大长度。

在版本 3.2 中添加。

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

countTestCases()

返回此测试对象表示的测试数。对于 TestCase 实例,这始终为 1

defaultTestResult()

返回应用于此测试用例类的测试结果类的实例(如果未向 run() 方法提供其他结果实例)。

对于 TestCase 实例,这始终是 TestResult 的实例;TestCase 的子类应根据需要覆盖此类。

id()

返回标识特定测试用例的字符串。这通常是测试方法的完整名称,包括模块和类名称。

shortDescription()

返回测试的描述,或 None(如果未提供描述)。此方法的默认实现返回测试方法文档字符串的第一行(如果可用),或 None

3.1 版中已更改:在 3.1 版中,此项已更改为即使存在文档字符串,也会将测试名称添加到简短描述中。这导致与单元测试扩展的兼容性问题,并且在 Python 3.2 中将添加测试名称移至 TextTestResult

addCleanup(function, /, *args, **kwargs)

添加一个函数,在 tearDown() 之后调用,以清理在测试期间使用的资源。函数将按添加顺序的逆序调用(LIFO)。当添加时,它们将使用传递给 addCleanup() 的任何参数和关键字参数进行调用。

如果 setUp() 失败,这意味着未调用 tearDown(),那么仍将调用添加的任何清理函数。

在版本 3.1 中添加。

enterContext(cm)

输入提供的 上下文管理器。如果成功,还将添加其 __exit__() 方法作为清理函数,通过 addCleanup() 添加,并返回 __enter__() 方法的结果。

在 3.11 版中添加。

doCleanups()

tearDown() 之后或在 setUp() 引发异常后在 setUp() 之后无条件调用此方法。

它负责调用 addCleanup() 添加的所有清理函数。如果您需要在 tearDown() 之前 调用清理函数,那么您可以自己调用 doCleanups()

doCleanups() 一次从清理函数的栈中弹出方法,因此它可以在任何时候调用。

在版本 3.1 中添加。

classmethod addClassCleanup(function, /, *args, **kwargs)

添加一个函数,在 tearDownClass() 之后调用,以清理测试类期间使用的资源。函数将按添加它们的顺序的相反顺序调用(LIFO)。当它们被添加时,它们将使用传递给 addClassCleanup() 的任何参数和关键字参数进行调用。

如果 setUpClass() 失败,这意味着没有调用 tearDownClass(),那么仍将调用添加的任何清理函数。

在版本 3.8 中添加。

classmethod enterClassContext(cm)

进入提供的 上下文管理器。如果成功,还通过 addClassCleanup() 将其 __exit__() 方法添加为清理函数,并返回 __enter__() 方法的结果。

在 3.11 版中添加。

classmethod doClassCleanups()

此方法在 tearDownClass() 之后无条件调用,或者在 setUpClass() 引发异常后调用。

它负责调用 addClassCleanup() 添加的所有清理函数。如果您需要在 tearDownClass() 之前调用清理函数,那么您可以自己调用 doClassCleanups()

doClassCleanups() 一次从清理函数的堆栈中弹出方法,因此它可以在任何时候调用。

在版本 3.8 中添加。

class unittest.IsolatedAsyncioTestCase(methodName='runTest')

此类提供与 TestCase 类似的 API,还接受协程作为测试函数。

在版本 3.8 中添加。

coroutine asyncSetUp()

准备测试夹具时调用的方法。这在 setUp() 之后调用。这在调用测试方法之前立即调用;除了 AssertionErrorSkipTest,此方法引发的任何异常都将被视为错误,而不是测试失败。默认实现不执行任何操作。

coroutine asyncTearDown()

在调用测试方法并记录结果后立即调用的方法。这在 tearDown() 之前调用。即使测试方法引发异常,也会调用此方法,因此子类中的实现可能需要特别小心地检查内部状态。除了 AssertionErrorSkipTest,此方法引发的任何异常都将被视为附加错误,而不是测试失败(从而增加报告的错误总数)。无论测试方法的结果如何,只有在 asyncSetUp() 成功时才会调用此方法。默认实现不执行任何操作。

addAsyncCleanup(function, /, *args, **kwargs)

此方法接受一个协程,该协程可用作清理函数。

coroutine enterAsyncContext(cm)

输入提供的 异步上下文管理器。如果成功,还将它的 __aexit__() 方法作为清理函数添加通过 addAsyncCleanup(),并返回 __aenter__() 方法的结果。

在 3.11 版中添加。

run(result=None)

设置一个新的事件循环来运行测试,将结果收集到作为 result 传递的 TestResult 对象中。如果省略 resultNone,则创建一个临时结果对象(通过调用 defaultTestResult() 方法)并使用。结果对象返回给 run() 的调用者。在测试结束时,事件循环中的所有任务都将被取消。

一个说明顺序的示例

from unittest import IsolatedAsyncioTestCase

events = []


class Test(IsolatedAsyncioTestCase):


    def setUp(self):
        events.append("setUp")

    async def asyncSetUp(self):
        self._async_connection = await AsyncConnection()
        events.append("asyncSetUp")

    async def test_response(self):
        events.append("test_response")
        response = await self._async_connection.get("https://example.com")
        self.assertEqual(response.status_code, 200)
        self.addAsyncCleanup(self.on_cleanup)

    def tearDown(self):
        events.append("tearDown")

    async def asyncTearDown(self):
        await self._async_connection.close()
        events.append("asyncTearDown")

    async def on_cleanup(self):
        events.append("cleanup")

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

在运行测试后,events 将包含 ["setUp", "asyncSetUp", "test_response", "asyncTearDown", "tearDown", "cleanup"]

unittest.FunctionTestCase(testFunc, setUp=, tearDown=, description=)

此类实现 TestCase 接口的部分,该接口允许测试运行器驱动测试,但不提供测试代码可用于检查和报告错误的方法。这用于使用旧版测试代码创建测试用例,使其可以集成到基于 unittest 的测试框架中。

分组测试

unittest.TestSuite(tests=())

此类表示单个测试用例和测试套件的聚合。该类呈现测试运行器所需的接口,使其可以像任何其他测试用例一样运行。运行 TestSuite 实例与迭代套件相同,分别运行每个测试。

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

TestSuite 对象的行为与 TestCase 对象非常相似,除了它们实际上并未实现测试。相反,它们用于将测试聚合到应一起运行的测试组中。还提供了其他一些方法来向 TestSuite 实例添加测试

addTest(test)

向套件中添加 TestCaseTestSuite

addTests(tests)

TestCaseTestSuite 实例的全部测试添加到此测试套件。

这相当于迭代 tests,为每个元素调用 addTest()

TestSuiteTestCase 共享以下方法

run(result)

运行与此套件关联的测试,将结果收集到作为 result 传递的测试结果对象中。请注意,与 TestCase.run() 不同,TestSuite.run() 要求传入结果对象。

debug()

运行与此套件关联的测试,但不收集结果。这允许将测试引发的异常传播给调用方,并且可用于支持在调试器下运行测试。

countTestCases()

返回此测试对象表示的测试数量,包括所有单独的测试和子套件。

__iter__()

TestSuite 分组的测试始终通过迭代访问。子类可以通过重写 __iter__() 来延迟提供测试。请注意,在单个套件上可能多次调用此方法(例如在计算测试或比较是否相等时),因此在 TestSuite.run() 之前重复迭代返回的测试对于每次调用迭代都必须相同。在 TestSuite.run() 之后,调用方不应依赖此方法返回的测试,除非调用方使用重写 TestSuite._removeTestAtIndex() 以保留测试引用。

3.2 版中更改:在较早的版本中,TestSuite 直接访问测试,而不是通过迭代,因此覆盖 __iter__() 并不足以提供测试。

3.4 版中更改:在较早的版本中,TestSuiteTestSuite.run() 之后对每个 TestCase 持有引用。子类可以通过覆盖 TestSuite._removeTestAtIndex() 来恢复该行为。

TestSuite 对象的典型用法中,run() 方法由 TestRunner 调用,而不是由最终用户测试框架调用。

加载和运行测试

class unittest.TestLoader

TestLoader 类用于从类和模块创建测试套件。通常,无需创建此类的实例;unittest 模块提供了一个可以作为 unittest.defaultTestLoader 共享的实例。但是,使用子类或实例允许自定义一些可配置的属性。

TestLoader 对象具有以下属性

errors

在加载测试时遇到的非致命错误列表。加载器在任何时候都不会重置它。致命错误由相关方法向调用者引发异常来发出信号。非致命错误也由一个合成测试指示,该测试在运行时会引发原始错误。

3.5 版中新增。

TestLoader 对象具有以下方法

loadTestsFromTestCase(testCaseClass)

返回包含在 TestCase 派生的 testCaseClass 中的所有测试用例的套件。

对于由 getTestCaseNames() 命名的每个方法,都会创建一个测试用例实例。默认情况下,这些方法名称以 test 开头。如果 getTestCaseNames() 未返回任何方法,但已实现 runTest() 方法,则会为该方法创建一个单独的测试用例。

loadTestsFromModule(module, *, pattern=None)

返回给定模块中包含的所有测试用例的套件。此方法在 module 中搜索派生自 TestCase 的类,并为该类定义的每个测试方法创建一个类的实例。

注意

虽然使用 TestCase 派生类的层次结构在共享固定装置和帮助器函数时很方便,但在不打算直接实例化的基类上定义测试方法并不适用于此方法。但是,当固定装置不同且在子类中定义时,这样做可能很有用。

如果模块提供 load_tests 函数,则会调用该函数来加载测试。这允许模块自定义测试加载。这是 load_tests 协议pattern 参数作为第三个参数传递给 load_tests

在 3.2 版中更改:添加了对 load_tests 的支持。

在 3.5 版中更改:添加了对仅关键字参数 pattern 的支持。

在 3.12 版中更改:已删除未记录且非官方的 use_load_tests 参数。

loadTestsFromName(name, module=None)

返回给定字符串说明符的所有测试用例套件。

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

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

此方法可选择相对于给定的module 解析name

在 3.5 版中更改:如果在遍历name 时发生 ImportErrorAttributeError,则将返回一个在运行时引发该错误的合成测试。这些错误包含在 self.errors 累积的错误中。

loadTestsFromNames(names, module=None)

类似于 loadTestsFromName(),但采用名称序列而不是单个名称。返回值是一个测试套件,它支持为每个名称定义的所有测试。

getTestCaseNames(testCaseClass)

返回在testCaseClass 中找到的一个已排序的方法名称序列;这应该是 TestCase 的一个子类。

discover(start_dir, pattern='test*.py', top_level_dir=None)

通过从指定的起始目录递归进入子目录来查找所有测试模块,并返回一个包含它们的 TestSuite 对象。只有与pattern 匹配的测试文件才会被加载。(使用 shell 样式模式匹配。)只有可导入的模块名称(即有效的 Python 标识符)才会被加载。

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

如果导入模块失败,例如由于语法错误,则这将被记录为单个错误,并且发现将继续。如果导入失败是由于引发了 SkipTest,则它将被记录为跳过,而不是错误。

如果找到一个包(一个包含名为 __init__.py 的文件的目录),则将检查包是否有 load_tests 函数。如果存在,则将调用它 package.load_tests(loader, tests, pattern)。测试发现会小心确保在调用期间只检查一次包的测试,即使 load_tests 函数本身调用了 loader.discover

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

该模式故意不存储为加载器属性,以便包可以继续自己发现。

top_level_dir 在内部存储,并用作对 discover() 的任何嵌套调用的默认值。也就是说,如果包的 load_tests 调用了 loader.discover(),则它不需要传递此参数。

start_dir 可以是点模块名称,也可以是目录。

在版本 3.2 中添加。

版本 3.4 中更改:导入时引发 SkipTest 的模块记录为跳过,而不是错误。

版本 3.4 中更改:start_dir 可以是 命名空间包

版本 3.4 中更改:在导入之前对路径进行排序,以便即使底层文件系统的排序不依赖于文件名,执行顺序也相同。

版本 3.5 中更改:现在检查找到的包是否具有 load_tests,无论其路径是否与 pattern 匹配,因为包名称不可能与默认模式匹配。

版本 3.11 中更改:start_dir 不能是 命名空间包。它自 Python 3.7 起已损坏,Python 3.11 正式将其移除。

版本 3.12.4 中更改:top_level_dir 仅在 discover 调用期间存储。

TestLoader 的以下属性可以通过子类化或在实例上赋值进行配置

testMethodPrefix

给出将被解释为测试方法的方法名称前缀的字符串。默认值为 'test'

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

sortTestMethodsUsing

getTestCaseNames() 和所有 loadTestsFrom* 方法中对方法名称进行排序时要使用的函数。

suiteClass

从测试列表构建测试套件的可调用对象。不需要结果对象上的任何方法。默认值为 TestSuite 类。

这会影响所有 loadTestsFrom* 方法。

testNamePatterns

Unix shell 风格通配符测试名称模式列表,测试方法必须匹配这些模式才能包含在测试套件中(请参阅 -k 选项)。

如果此属性不是 None(默认值),则要包含在测试套件中的所有测试方法都必须匹配此列表中的某个模式。请注意,匹配始终使用 fnmatch.fnmatchcase() 执行,因此与传递给 -k 选项的模式不同,简单的子字符串模式必须使用 * 通配符进行转换。

这会影响所有 loadTestsFrom* 方法。

在 3.7 版本中添加。

class unittest.TestResult

此类用于编译有关哪些测试成功和哪些测试失败的信息。

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

建立在 unittest 之上的测试框架可能需要访问 TestResult 对象,该对象由运行一组测试生成以用于报告目的;TestResult 实例由 TestRunner.run() 方法为此目的返回。

TestResult 实例具有以下属性,在检查一组测试运行结果时会用到这些属性

errors

一个列表,其中包含 TestCase 实例和包含格式化回溯的字符串的 2 元组。每个元组都表示引发意外异常的测试。

failures

一个列表,其中包含 TestCase 实例和包含格式化回溯的字符串的 2 元组。每个元组都表示使用 assert* 方法 明确发出失败信号的测试。

skipped

TestCase 实例和包含跳过测试原因的字符串的 2 元组列表。

在版本 3.1 中添加。

expectedFailures

TestCase 实例和包含格式化回溯的字符串的 2 元组列表。每个元组表示测试用例的预期失败或错误。

unexpectedSuccesses

标记为预期失败但成功的 TestCase 实例列表。

collectedDurations

包含测试用例名称和表示每个已运行测试的经过时间的浮点数的 2 元组列表。

在 3.12 版本中添加。

shouldStop

当应通过 stop() 停止测试执行时,设置为 True

testsRun

到目前为止运行的测试总数。

buffer

如果设置为 true,sys.stdoutsys.stderr 将在 startTest()stopTest() 被调用之间进行缓冲。仅当测试失败或出错时,收集的输出才会回显到真正的 sys.stdoutsys.stderr。任何输出也会附加到失败/错误消息。

在版本 3.2 中添加。

failfast

如果设置为 true,stop() 将在第一次失败或错误时被调用,从而停止测试运行。

在版本 3.2 中添加。

tb_locals

如果设置为 true,则本地变量将显示在回溯中。

3.5 版中新增。

wasSuccessful()

如果到目前为止运行的所有测试都通过,则返回 True,否则返回 False

3.4 版中的更改:如果使用 expectedFailure() 装饰器标记的测试中存在任何 unexpectedSuccesses,则返回 False

stop()

可以通过调用此方法来发出信号,表明应该通过将 shouldStop 属性设置为 True 来中止正在运行的测试集。 TestRunner 对象应遵守此标志,并在不运行任何其他测试的情况下返回。

例如,TextTestRunner 类使用此功能在用户从键盘发出中断信号时停止测试框架。提供 TestRunner 实现的交互式工具可以以类似的方式使用此功能。

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

startTest(test)

当测试用例 test 即将运行时调用。

stopTest(test)

无论结果如何,在测试用例 test 执行后调用。

startTestRun()

在执行任何测试之前调用一次。

在版本 3.1 中添加。

stopTestRun()

在执行所有测试后调用一次。

在版本 3.1 中添加。

addError(test, err)

当测试用例test引发意外异常时调用。errsys.exc_info()返回的元组形式:(type, value, traceback)

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

addFailure(test, err)

当测试用例test发出失败信号时调用。errsys.exc_info()返回的元组形式:(type, value, traceback)

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

addSuccess(test)

当测试用例test成功时调用。

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

addSkip(test, reason)

当测试用例test被跳过时调用。reason是测试给出的跳过原因。

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

addExpectedFailure(test, err)

当测试用例test失败或出错,但已用expectedFailure()装饰器标记时调用。

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

addUnexpectedSuccess(test)

当测试用例 test 被标记为 expectedFailure() 装饰器,但成功时调用。

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

addSubTest(test, subtest, outcome)

当子测试完成时调用。test 是对应于测试方法的测试用例。subtest 是描述子测试的自定义 TestCase 实例。

如果 outcomeNone,则子测试成功。否则,它将失败,并引发异常,其中 outcome 是由 sys.exc_info() 返回的元组形式:(type, value, traceback)

当结果成功时,默认实现不执行任何操作,并将子测试失败记录为正常失败。

在 3.4 版本中添加。

addDuration(test, elapsed)

当测试用例完成时调用。elapsed 是以秒为单位表示的时间,它包括清理函数的执行。

在 3.12 版本中添加。

class unittest.TextTestResult(stream, descriptions, verbosity, *, durations=None)

TestResult 使用的 TextTestRunner 的具体实现。随着接口的变化,子类应接受 **kwargs 以确保兼容性。

在版本 3.2 中添加。

3.12 版中已更改: 添加了 durations 关键字参数。

unittest.defaultTestLoader

旨在共享的 TestLoader 类的实例。如果不需要自定义 TestLoader,则可以使用此实例,而不是重复创建新实例。

class unittest.TextTestRunner(stream=None, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None, warnings=None, *, tb_locals=False, durations=None)

一个基本的测试运行器实现,它将结果输出到流中。如果streamNone(默认值),则使用sys.stderr作为输出流。此类有一些可配置参数,但本质上非常简单。运行测试套件的图形应用程序应提供备用实现。此类实现应接受**kwargs作为接口,以便在向 unittest 添加功能时构造运行器发生更改。

默认情况下,此运行器会显示DeprecationWarningPendingDeprecationWarningResourceWarningImportWarning,即使它们默认情况下被忽略。可以使用 Python 的-Wd-Wa选项(请参阅警告控制)并保留warningsNone来覆盖此行为。

在版本 3.2 中更改:添加了warnings参数。

在版本 3.2 中更改:默认流在实例化时间而不是导入时间设置为sys.stderr

在版本 3.5 中更改:添加了tb_locals参数。

在版本 3.12 中更改:添加了durations参数。

_makeResult()

此方法返回run()使用的TestResult实例。它不打算直接调用,但可以在子类中覆盖以提供自定义TestResult

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

stream, descriptions, verbosity
run(test)

此方法是 TextTestRunner 的主要公共接口。此方法采用 TestSuiteTestCase 实例。通过调用 _makeResult() 创建 TestResult,运行测试并打印结果到 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 中找到的所有测试。

argv 参数可以是传递给程序的选项列表,第一个元素是程序名称。如果未指定或 None,则使用 sys.argv 的值。

testRunner 参数可以是测试运行器类或其已创建的实例。默认情况下,main 使用退出代码调用 sys.exit(),表示运行的测试成功 (0) 或失败 (1)。退出代码 5 表示未运行或跳过任何测试。

testLoader 参数必须是 TestLoader 实例,并且默认为 defaultTestLoader

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

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

failfastcatchbreakbuffer 参数与同名 命令行选项 具有相同的效果。

warnings 参数指定在运行测试时应使用的 警告过滤器。如果未指定,则如果将 -W 选项传递给 python(请参见 警告控制),它将保持为 None,否则将设置为 'default'

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

在版本 3.1 中更改:添加了 exit 参数。

在版本 3.2 中更改:添加了 verbosityfailfastcatchbreakbufferwarnings 参数。

在版本 3.4 中更改:defaultTest 参数已更改,还接受测试名称的可迭代对象。

load_tests 协议

在版本 3.2 中添加。

模块或包可以通过实现名为 load_tests 的函数,自定义在正常测试运行或测试发现期间如何从其中加载测试。

如果测试模块定义了 load_tests,则 TestLoader.loadTestsFromModule() 将使用以下参数调用它

load_tests(loader, standard_tests, pattern)

其中 patternloadTestsFromModule 直接传递。它默认为 None

它应该返回 TestSuite

loader 是执行加载操作的 TestLoader 实例。standard_tests 是默认情况下从模块加载的测试。测试模块通常只希望添加或移除标准测试集中的测试。第三个参数用于在测试发现过程中加载包。

从特定 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

如果从命令行或通过调用 TestLoader.discover() 在包含包的目录中启动发现,则将检查包 __init__.py 是否包含 load_tests。如果该函数不存在,则发现将递归进入包,就好像它只是另一个目录一样。否则,包的测试发现将留给 load_tests,后者将使用以下参数调用

load_tests(loader, standard_tests, pattern)

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

由于模式已传递给 load_tests,因此包可以继续(并可能修改)测试发现。测试包的“无操作” 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

3.5 版中已更改:由于包名称无法匹配默认模式,因此发现不再检查包名称是否与 pattern 匹配。

类和模块固定装置

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

类似地,如果测试来自与前一个测试不同的模块,则将运行前一个模块的 tearDownModule,然后运行新模块的 setUpModule

在所有测试运行完毕后,将运行最终的 tearDownClasstearDownModule

请注意,共享固定装置与 [潜在] 功能(如测试并行化)不太兼容,并且会破坏测试隔离。应谨慎使用它们。

由 unittest 测试加载器创建的测试的默认排序是将来自同一模块和类的所有测试分组在一起。这将导致 setUpClass / setUpModule(等)每个类和模块仅调用一次。如果你随机化顺序,以便来自不同模块和类的测试彼此相邻,那么这些共享固定函数可能会在单个测试运行中被调用多次。

共享固定函数不适用于具有非标准顺序的套件。BaseTestSuite 仍然存在于不想支持共享固定函数的框架中。

如果在其中一个共享固定函数期间引发任何异常,则该测试将报告为错误。因为没有相应的测试实例,所以会创建一个 _ErrorHolder 对象(具有与 TestCase 相同的接口)来表示该错误。如果你只是使用标准的 unittest 测试运行器,那么这个细节并不重要,但如果你是一个框架作者,它可能是相关的。

setUpClass 和 tearDownClass

这些必须作为类方法实现

import unittest

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

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

如果你希望在基类上调用 setUpClasstearDownClass,则必须自己调用它们。在 TestCase 中的实现是空的。

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

setUpModule 和 tearDownModule

这些应该作为函数实现

def setUpModule():
    createConnection()

def tearDownModule():
    closeConnection()

如果在 setUpModule 中引发异常,则模块中的所有测试都不会运行,并且不会运行 tearDownModule。如果异常是 SkipTest 异常,则该模块将报告为已跳过,而不是错误。

要添加即使在异常情况下也必须运行的清理代码,请使用 addModuleCleanup

unittest.addModuleCleanup(function, /, *args, **kwargs)

tearDownModule() 之后添加一个函数来调用,以清理测试类期间使用的资源。函数将按添加的顺序逆序调用(LIFO)。当添加这些函数时,它们将使用传递给 addModuleCleanup() 的任何参数和关键字参数进行调用。

如果 setUpModule() 失败,这意味着未调用 tearDownModule(),那么仍将调用添加的任何清理函数。

在版本 3.8 中添加。

classmethod unittest.enterModuleContext(cm)

进入提供的 上下文管理器。如果成功,还会通过 addModuleCleanup() 将其 __exit__() 方法添加为清理函数,并返回 __enter__() 方法的结果。

在 3.11 版中添加。

unittest.doModuleCleanups()

此函数在 tearDownModule() 之后或在 setUpModule() 引发异常后在 setUpModule() 之后无条件调用。

它负责调用 addModuleCleanup() 添加的所有清理函数。如果你需要在 tearDownModule() 之前调用清理函数,那么你可以自己调用 doModuleCleanups()

doModuleCleanups() 一次从清理函数的堆栈中弹出方法,因此它可以在任何时候调用。

在版本 3.8 中添加。

信号处理

在版本 3.2 中添加。

unittest 的 -c/--catch 命令行选项以及 unittest.main()catchbreak 参数在测试运行期间提供了对 Control-C 的更友好的处理。启用 catch break 行为后,Control-C 将允许当前正在运行的测试完成,然后测试运行将结束并报告到目前为止的所有结果。第二次 Control-C 将像往常一样引发 KeyboardInterrupt

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

框架作者可以使用一些实用函数在测试框架内启用 control-c 处理功能。

unittest.installHandler()

安装 control-c 处理程序。当收到 signal.SIGINT(通常是对用户按 control-c 的响应)时,所有已注册的结果都调用 stop()

unittest.registerResult(result)

注册 TestResult 对象以进行 control-c 处理。注册结果会存储对它的弱引用,因此不会阻止结果被垃圾回收。

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

unittest.removeResult(result)

移除已注册的结果。一旦移除结果,那么 stop() 将不再对该结果对象调用以响应 control-c。

unittest.removeHandler(function=None)

在不带参数调用时,此函数会在已安装的情况下移除控制-c 处理程序。此函数还可用作测试装饰器,在执行测试时暂时移除处理程序

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