unittest — 单元测试框架

源代码: Lib/unittest/__init__.py


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

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

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

测试夹具

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

测试用例

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

测试套件

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

测试运行器

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

参见

模块 doctest

另一个风格迥异的测试支持模块。

Simple Smalltalk Testing: With Patterns

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

pytest

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

The Python Testing Tools Taxonomy

一份包含功能测试框架和模拟对象库的 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 版本发生变化: 在早期版本中,只能运行单个测试方法,而不能运行模块或类。

3.14 版本新增: 输出默认是彩色的,并且可以使用环境变量控制

命令行选项

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 版本发生变化: 测试发现支持命名空间包

3.11 版本发生变化: 测试发现取消了命名空间包支持。自 Python 3.7 以来,它一直存在问题。包含测试的起始目录及其子目录必须是包含 __init__.py 文件的常规包。

如果起始目录是包的点分名称,则祖先包可以是命名空间包。

3.14 版本发生变化: 测试发现再次支持将命名空间包作为起始目录。为了避免扫描与 Python 无关的目录,不会在不包含 __init__.py 的子目录中搜索测试。

组织测试代码

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

TestCase 实例的测试代码应完全自包含,以便它可以在隔离状态下运行,或与任意数量的其他测试用例任意组合运行。

最简单的 TestCase 子类将仅实现一个测试方法(即名称以 test 开头的方法)以执行特定的测试代码

import unittest

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

请注意,为了测试某些东西,我们使用 TestCase 基类提供的 assert* 方法 之一。如果测试失败,将引发一个带有解释消息的异常,并且 unittest 会将该测试用例标识为 失败。任何其他异常都将被视为 错误

测试可能很多,而且它们的设置可能重复。幸运的是,我们可以通过实现一个名为 setUp() 的方法来提取设置代码,测试框架将自动为我们运行的每一个测试调用它。

import unittest

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

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

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

备注

各种测试的运行顺序由根据字符串的内置排序对测试方法名称进行排序来决定。

如果在测试运行期间 setUp() 方法引发异常,则框架将认为测试发生了错误,并且测试方法将不会执行。

同样,我们可以提供一个 tearDown() 方法,在测试方法运行后进行清理工作

import unittest

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

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

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

这样的测试代码工作环境被称为 测试夹具。将创建一个新的 TestCase 实例作为独特的测试夹具,用于执行每个单独的测试方法。因此,setUp()tearDown()__init__() 将为每个测试调用一次。

建议您使用 TestCase 实现根据其测试的功能将测试分组在一起。unittest 为此提供了一个机制:测试套件,由 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

将测试标记为预期失败或错误。如果测试在测试函数本身(而不是在某个 测试夹具 方法中)失败或报错,则将其视为成功。如果测试通过,则将其视为失败。

exception unittest.SkipTest(reason)

此异常用于跳过测试。

通常,您可以使用 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。

测试用例

class 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 对象中。如果 *result* 省略或为 None,则会创建一个临时结果对象(通过调用 defaultTestResult() 方法)并使用。结果对象将返回给 run() 的调用者。

只需调用 TestCase 实例即可达到同样的效果。

3.3 版本发生变化: 早期版本的 run 不返回结果。调用实例也不返回结果。

skipTest(reason)

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

在 3.1 版本加入。

subTest(msg=None, **params)

返回一个上下文管理器,它将执行包含的代码块作为子测试。 *msg* 和 *params* 是可选的任意值,当子测试失败时会显示出来,让您可以清晰地识别它们。

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

有关详细信息,请参阅 使用子测试区分测试迭代

在 3.4 版本加入。

debug()

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

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

方法

检查

新增于

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

assertIsSubclass(a, b)

issubclass(a, b)

3.14

assertNotIsSubclass(a, b)

not issubclass(a, b)

3.14

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

assertEqual(first, second, msg=None)

测试 *first* 和 *second* 是否相等。如果值不相等,测试将失败。

此外,如果 *first* 和 *second* 是完全相同的类型,并且是列表、元组、字典、集合、不可变集合或字符串之一,或者任何子类注册到 addTypeEqualityFunc() 的类型,将调用类型特定的相等函数以生成更有用的默认错误消息(另请参阅 类型特定方法列表)。

3.1 版本发生变化: 添加了自动调用类型特定相等函数的功能。

3.2 版本发生变化: assertMultiLineEqual() 作为比较字符串的默认类型相等函数添加。

assertNotEqual(first, second, msg=None)

测试 *first* 和 *second* 是否不相等。如果值相等,测试将失败。

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)

测试 *first* 和 *second* 是否是(或不是)同一个对象。

在 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* 的实例(*cls* 可以是一个类或一个类元组,如 isinstance() 所支持)。要检查精确类型,请使用 assertIs(type(obj), cls)

在 3.2 版本加入。

assertIsSubclass(cls, superclass, msg=None)
assertNotIsSubclass(cls, superclass, msg=None)

测试 *cls* 是否是(或不是) *superclass* 的子类(*superclass* 可以是一个类或一个类元组,如 issubclass() 所支持)。要检查精确类型,请使用 assertIs(cls, superclass)

在 3.14 版本加入。

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

方法

检查

新增于

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*

3.10

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

测试当调用 *callable* 时是否引发异常,同时将任何位置参数或关键字参数传递给 assertRaises()。如果引发 *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* 时是否触发警告,同时将任何位置参数或关键字参数传递给 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 块内发出的至少一条消息符合 *logger* 和 *level* 条件,则测试通过,否则失败。

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

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

assertStartsWith(a, b)

a.startswith(b)

3.14

assertNotStartsWith(a, b)

not a.startswith(b)

3.14

assertEndsWith(a, b)

a.endswith(b)

3.14

assertNotEndsWith(a, b)

not a.endswith(b)

3.14

assertHasAttr(a, b)

hastattr(a, b)

3.14

assertNotHasAttr(a, b)

not hastattr(a, b)

3.14

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

通过计算差异,将结果四舍五入到给定的小数位数 places(默认 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 版本加入。

assertStartsWith(s, prefix, msg=None)
assertNotStartsWith(s, prefix, msg=None)

测试 Unicode 或字节字符串 s 是否以 prefix 开头(或不以 prefix 开头)。prefix 也可以是要尝试的字符串元组。

在 3.14 版本加入。

assertEndsWith(s, suffix, msg=None)
assertNotEndsWith(s, suffix, msg=None)

测试 Unicode 或字节字符串 s 是否以 suffix 结尾(或不以 suffix 结尾)。suffix 也可以是要尝试的字符串元组。

在 3.14 版本加入。

assertHasAttr(obj, name, msg=None)
assertNotHasAttr(obj, name, msg=None)

测试对象 obj 是否具有(或不具有)属性 name

在 3.14 版本加入。

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 版中,此功能进行了更改,即使存在文档字符串,也会将测试名称添加到简短描述中。这导致了与 unittest 扩展的兼容性问题,因此在 Python 3.2 中将测试名称添加到 TextTestResult 中。

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

添加一个函数,该函数将在 tearDown() 之后调用,以清理测试期间使用的资源。函数将以与添加顺序相反的顺序调用(后进先出)。它们将使用在添加时传递给 addCleanup() 的所有参数和关键字参数调用。

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

在 3.1 版本加入。

enterContext(cm)

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

在 3.11 版本中新增。

doCleanups()

此方法在 tearDown() 之后无条件调用,或者在 setUp() 抛出异常后调用。

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

doCleanups() 逐个从清理函数堆栈中弹出方法,因此可以随时调用。

在 3.1 版本加入。

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

添加一个函数,该函数将在 tearDownClass() 之后调用,以清理测试类期间使用的资源。函数将以与添加顺序相反的顺序调用(后进先出)。它们将使用在添加时传递给 addClassCleanup() 的所有参数和关键字参数调用。

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

在 3.8 版本加入。

classmethod enterClassContext(cm)

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

在 3.11 版本中新增。

classmethod doClassCleanups()

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

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

doClassCleanups() 逐个从清理函数堆栈中弹出方法,因此可以随时调用。

在 3.8 版本加入。

class unittest.IsolatedAsyncioTestCase(methodName='runTest')

此类的 API 类似于 TestCase,也接受协程作为测试函数。

在 3.8 版本加入。

loop_factory

传递给 asyncio.Runnerloop_factory。在子类中用 asyncio.EventLoop 覆盖,以避免使用 asyncio 策略系统。

在 3.13 版本加入。

async asyncSetUp()

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

async asyncTearDown()

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

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

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

async enterAsyncContext(cm)

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

在 3.11 版本中新增。

run(result=None)

设置一个新的事件循环来运行测试,将结果收集到作为 result 传递的 TestResult 对象中。如果省略 result 或为 None,则创建一个临时结果对象(通过调用 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"]

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

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

分组测试

class 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 派生类的层次结构可以方便地共享 fixture 和辅助函数,但在不打算直接实例化基类上定义测试方法与此方法不太兼容。然而,当 fixture 不同且在子类中定义时,这样做可能很有用。

如果模块提供了 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 实例的可调用对象。这些检查按此处列出的顺序应用;也就是说,可能测试用例类中的方法将被识别为“测试用例类中的测试方法”,而不是“可调用对象”。

例如,如果您有一个模块 SampleTests 包含一个 TestCase 派生类 SampleTestCase,其中包含三个测试方法(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 的模块被记录为跳过,而不是错误。

start_dir 可以是一个 命名空间包

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

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

版本 3.11 中的变化: start_dir 不能是命名空间包。自 Python 3.7 以来,它已损坏,Python 3.11 正式移除了它。

版本 3.13 中的变化: top_level_dir 仅在 discover 调用期间存储。

版本 3.14 中的变化: start_dir 再次可以是一个命名空间包

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 对象以进行报告;TestRunner.run() 方法为此目的返回一个 TestResult 实例。

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

errors

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

failures

一个包含 TestCase 实例和格式化回溯字符串的 2 元组列表。每个元组表示使用 断言方法 显式发出失败信号的测试。

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 引发意外异常时调用。err 是一个元组,其形式由 sys.exc_info() 返回:(type, value, traceback)

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

addFailure(test, err)

当测试用例 test 发出失败信号时调用。err 是一个元组,其形式由 sys.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)

当测试用例 testexpectedFailure() 装饰器标记,但却成功时调用。

默认实现将测试附加到实例的 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,并运行测试并将结果打印到标准输出。

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

调用 main 会返回一个对象,其 result 属性包含作为 unittest.TestResult 运行的测试结果。

版本 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)

其中 pattern 直接从 loadTestsFromModule 传递。它默认为 None

它应该返回一个 TestSuite

loader 是执行加载的 TestLoader 实例。standard_tests 是默认从模块加载的测试。测试模块通常只希望从标准测试集中添加或删除测试。第三个参数在作为测试发现的一部分加载包时使用。

一个典型的 load_tests 函数,它从一组特定的 TestCase 类加载测试,可能看起来像

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() 之后调用,以清理测试类期间使用的资源。函数将以与添加它们的顺序相反的顺序调用(后进先出)。它们将使用在添加它们时传递给 addModuleCleanup() 的任何参数和关键字参数进行调用。

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

在 3.8 版本加入。

unittest.enterModuleContext(cm)

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

在 3.11 版本中新增。

unittest.doModuleCleanups()

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

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

doModuleCleanups() 一次从清理函数堆栈中弹出一个方法,因此可以随时调用它。

在 3.8 版本加入。

信号处理

在 3.2 版本加入。

unittest 的 -c/--catch 命令行选项,以及 unittest.main()catchbreak 参数,提供了在测试运行期间更友好的 Control-C 处理。启用中断捕获行为后,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)

注册一个用于 Control-C 处理的 TestResult 对象。注册结果会存储一个弱引用,因此它不会阻止结果被垃圾回收。

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

unittest.removeResult(result)

删除已注册的结果。一旦结果被删除,在响应 Control-C 时将不再对该结果对象调用 stop()

unittest.removeHandler(function=None)

在不带参数调用时,此函数将删除 Control-C 处理程序(如果已安装)。此函数还可以用作测试装饰器,以在测试执行期间临时删除处理程序

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