Skip to main content

26.8. test — Python的回归测试包

注解

test 包仅供Python内部使用。它被记录为Python的核心开发人员的利益。不建议在Python标准库之外使用这个包,因为这里提到的代码可能会在Python发行之间更改或删除,恕不另行通知。


test 软件包包含对Python以及模块 test.supporttest.regrtest 的所有回归测试。 test.support 用于增强您的测试,而 test.regrtest 驱动测试套件。

test 包中名称以 test_ 开头的每个模块是特定模块或功能的测试套件。所有新测试应使用 unittestdoctest 模块编写。一些较老的测试使用“传统”测试风格编写,将打印的输出与 sys.stdout 进行比较;这种风格的测试被视为已弃用。

参见

模块 unittest

写PyUnit回归测试。

模块 doctest

文档字符串中嵌入的测试。

26.8.1. 写入单元测试 test

优选地,使用 unittest 模块的测试遵循一些准则。一个是通过用 test_ 启动它来命名测试模块,并以被测试的模块的名称结束它。测试模块中的测试方法应该从 test_ 开始,并结束该方法测试的描述。这是需要的,使得方法被测试驱动程序识别为测试方法。此外,不应包括方法的文档字符串。应该使用注释(例如 # Tests function returns only True or False)来提供测试方法的文档。这是因为文档字符串打印出来,如果它们存在,因此没有说明正在运行的测试。

通常使用一个基本的样板:

import unittest
from test import support

class MyTestCase1(unittest.TestCase):

    # Only use setUp() and tearDown() if necessary

    def setUp(self):
        ... code to execute in preparation for tests ...

    def tearDown(self):
        ... code to execute to clean up after tests ...

    def test_feature_one(self):
        # Test feature one.
        ... testing code ...

    def test_feature_two(self):
        # Test feature two.
        ... testing code ...

    ... more test methods ...

class MyTestCase2(unittest.TestCase):
    ... same structure as MyTestCase1 ...

... more test classes ...

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

此代码模式允许测试套件由 test.regrtest 独立运行,作为支持 unittest CLI的脚本或通过 python -m unittest CLI运行。

回归测试的目的是试图破解代码。这导致了一些指导方针:

  • 测试套件应该运行所有类,函数和常量。这不仅包括要呈现给外部世界的外部API,还包括“私有”代码。

  • 白盒测试(在测试被编写时检查被测试的代码)是优选的。黑盒测试(仅测试已发布的用户界面)不够完整,无法确保所有边界和边缘情况都已测试。

  • 确保测试所有可能的值,包括无效的值。这确保不仅所有有效值都是可接受的,而且不正确的值被正确处理。

  • 排尽尽可能多的代码路径。测试分支发生的地方,从而定制输入,以确保通过代码获取多个不同的路径。

  • 为发现的测试代码的任何错误添加显式测试。这将确保错误不会再次裁剪,如果代码在将来更改。

  • 确保在测试后清理(例如关闭和删除所有临时文件)。

  • 如果测试依赖于操作系统的特定条件,则在尝试测试之前验证条件是否已存在。

  • 尽可能导入尽可能少的模块,并尽快完成。这最小化了测试的外部依赖性,并且最小化了导入模块的副作用的可能的异常行为。

  • 尝试最大化代码重用。有时,测试将随着使用什么类型的输入而变化。通过将一个基本测试类与指定输入的类子类化来最小化代码重复:

    class TestFuncAcceptsSequencesMixin:
    
        func = mySuperWhammyFunction
    
        def test_func(self):
            self.func(self.arg)
    
    class AcceptLists(TestFuncAcceptsSequencesMixin, unittest.TestCase):
        arg = [1, 2, 3]
    
    class AcceptStrings(TestFuncAcceptsSequencesMixin, unittest.TestCase):
        arg = 'abc'
    
    class AcceptTuples(TestFuncAcceptsSequencesMixin, unittest.TestCase):
        arg = (1, 2, 3)
    

    当使用此模式时,请记住所有从 unittest.TestCase 继承的类都作为测试运行。上例中的 Mixin 类没有任何数据,因此不能由自身运行,因此它不继承 unittest.TestCase

参见

测试驱动开发

一本由Kent Beck编写的测试代码之前的书。

26.8.2. 使用命令行界面运行测试

test 包可以作为脚本运行,以驱动Python的回归测试套件,这要归功于 -m 选项:python -m test。在引擎盖下,它使用 test.regrtest;在以前的Python版本中使用的调用 python -m test.regrtest 仍然工作。运行脚本本身自动开始在 test 包中运行所有回归测试。它通过查找程序包中名称以 test_ 开头的所有模块,导入它们,并执行函数 test_main() (如果存在)或通过unittest.TestLoader.loadTestsFromModule(如果 test_main 不存在)加载测试。要执行的测试的名称也可以传递给脚本。指定单个回归测试(python -m test test_spam)将最小化输出,并且仅打印测试是通过还是失败。

运行 test 直接允许有哪些资源可供测试使用设置。您可以使用 -u 命令行选项。将 all 指定为 -u 选项的值将启用所有可能的资源:python -m test -uall。如果仅需要一个资源(更常见的情况),则可以在 all 之后列出不期望的逗号分隔的资源列表。命令 python -m test -uall,-audio,-largefile 将对除了 audiolargefile 资源之外的所有资源运行 test。有关所有资源和更多命令行选项的列表,请运行 python -m test -h

执行回归测试的一些其他方法取决于正在执行测试的平台。在Unix上,您可以在Python构建的顶级目录中运行 make test。在Windows上,从 PCBuild 目录执行 rt.bat 将运行所有回归测试。

26.9. test.support — Python测试套件的实用程序

test.support 模块支持Python的回归测试套件。

注解

test.support 不是公共模块。它在这里被记录以帮助Python开发人员编写测试。此模块的API可能会发生更改,但不会出现版本之间的向后兼容性问题。

此模块定义了以下例外:

exception test.support.TestFailed

测试失败时引发异常。这是不赞成使用基于 unittest 的测试和 unittest.TestCase 的断言方法。

exception test.support.ResourceDenied

unittest.SkipTest 的子类。在资源(例如网络连接)不可用时触发。由 requires() 函数引发。

test.support 模块定义以下常量:

test.support.verbose

True,当启用详细输出时。当需要有关运行测试的更详细信息时,应检查。 verbosetest.regrtest 设置。

test.support.is_jython

True 如果运行的解释器是Jython。

test.support.TESTFN

设置为可安全用作临时文件名称的名称。创建的任何临时文件应该关闭并取消链接(删除)。

test.support 模块定义以下功能:

test.support.forget(module_name)

sys.modules 中删除名为 module_name 的模块,并删除模块的任何字节编译文件。

test.support.is_resource_enabled(resource)

如果 resource 已启用并可用,请返回 True。仅当 test.regrtest 正在执行测试时,才设置可用资源的列表。

test.support.requires(resource, msg=None)

如果 resource 不可用,请提高 ResourceDeniedmsgResourceDenied 的参数,如果它被提出。如果由 __name__'__main__' 的函数调用,则始终返回 True。当由 test.regrtest 执行测试时使用。

test.support.findfile(filename, subdir=None)

返回名为 filename 的文件的路径。如果找不到匹配,则返回 filename。这不等于故障,因为它可以是文件的路径。

设置 subdir 表示用于查找文件的相对路径,而不是直接在路径目录中查找。

test.support.run_unittest(*classes)

执行传递给函数的 unittest.TestCase 子类。该函数扫描以前缀 test_ 开头的方法的类,并单独执行测试。

将字符串作为参数传递也是合法的;这些应该是 sys.modules 中的密钥。每个相关模块将由 unittest.TestLoader.loadTestsFromModule() 扫描。这通常在以下 test_main() 函数中可见:

def test_main():
    support.run_unittest(__name__)

这将运行在命名模块中定义的所有测试。

test.support.run_doctest(module, verbosity=None)

在给定的 module 上运行 doctest.testmod()。返回 (failure_count, test_count)

如果 verbosityNone,则以设置为 verbose 的详细程度运行 doctest.testmod()。否则,它的详细程度设置为 None

test.support.check_warnings(*filters, quiet=True)

warnings.catch_warnings() 的一个方便包装,使得更容易测试警告是否正确提出。它大致相当于调用 warnings.catch_warnings(record=True),将 warnings.simplefilter() 设置为 always,并选择自动验证记录的结果。

check_warnings 接受 ("message regexp", WarningCategory) 形式的2元组作为位置参数。如果提供了一个或多个 filters,或者如果可选关键字参数 quietFalse,它将检查以确保警告符合预期:每个指定的过滤器必须匹配至少一个由附带代码引发的警告或测试失败,并且如果出现任何与任何指定的过滤器不匹配的警告,则测试失败。要禁用第一个检查,请将 quiet 设置为 True

如果未指定任何参数,则默认为:

check_warnings(("", Warning), quiet=True)

在这种情况下,所有警告都被捕获,并且不会产生错误。

在进入上下文管理器时,返回 WarningRecorder 实例。通过记录器对象的 warnings 属性可以获得 catch_warnings() 的基本警告列表。为方便起见,还可以通过记录器对象直接访问表示最近警告的对象的属性(参见下面的示例)。如果没有提出警告,那么在表示警告的对象上预期的任何属性将返回 None

记录器对象还有一个 reset() 方法,它清除警告列表。

上下文管理器被设计为像这样使用:

with check_warnings(("assertion is always true", SyntaxWarning),
                    ("", UserWarning)):
    exec('assert(False, "Hey!")')
    warnings.warn(UserWarning("Hide me!"))

在这种情况下,如果没有提出警告,或者提出了其他警告,check_warnings() 会引发错误。

当测试需要更深入地查看警告时,而不仅仅是检查它们是否发生,可以使用这样的代码:

with check_warnings(quiet=True) as w:
    warnings.warn("foo")
    assert str(w.args[0]) == "foo"
    warnings.warn("bar")
    assert str(w.args[0]) == "bar"
    assert str(w.warnings[0].args[0]) == "foo"
    assert str(w.warnings[1].args[0]) == "bar"
    w.reset()
    assert len(w.warnings) == 0

这里所有的警告都将被捕获,测试代码直接测试捕获的警告。

在 3.2 版更改: 新的可选参数 filtersquiet

test.support.captured_stdin()
test.support.captured_stdout()
test.support.captured_stderr()

一个上下文管理器,它用 io.StringIO 对象临时替换命名流。

使用输出流的示例:

with captured_stdout() as stdout, captured_stderr() as stderr:
    print("hello")
    print("error", file=sys.stderr)
assert stdout.getvalue() == "hello\n"
assert stderr.getvalue() == "error\n"

示例使用输入流:

with captured_stdin() as stdin:
    stdin.write('hello\n')
    stdin.seek(0)
    # call test code that consumes from sys.stdin
    captured = input()
self.assertEqual(captured, "hello")
test.support.temp_dir(path=None, quiet=False)

path 上创建一个临时目录并生成目录的上下文管理器。

如果 pathNone,则使用 tempfile.mkdtemp() 创建临时目录。如果 quietFalse,则上下文管理器在错误时引发异常。否则,如果指定了 path 且无法创建,则只会发出警告。

test.support.change_cwd(path, quiet=False)

临时将当前工作目录更改为 path 并生成目录的上下文管理器。

如果 quietFalse,则上下文管理器在错误时引发异常。否则,它只发出警告,并保持当前工作目录相同。

test.support.temp_cwd(name='tempcwd', quiet=False)

临时创建新目录并更改当前工作目录(CWD)的上下文管理器。

上下文管理器在临时更改当前工作目录之前,在名称为 name 的当前目录中创建一个临时目录。如果 nameNone,则使用 tempfile.mkdtemp() 创建临时目录。

如果 quietFalse,并且不可能创建或更改CWD,则会出现错误。否则,仅产生警告并使用原始CWD。

test.support.temp_umask(umask)

临时设置进程umask的上下文管理器。

如果操作系统支持符号链接,返回 True,否则返回 False

用于运行需要支持符号链接的测试的装饰器。

@test.support.anticipate_failure(condition)

有条件地用 unittest.expectedFailure() 标记测试的装饰器。任何使用这个装饰器应该有一个相关的意见识别相关的跟踪器问题。

@test.support.run_with_locale(catstr, *locales)

一个装饰器,用于在不同的语言环境中运行函数,在完成后正确地重置它。 catstr 是作为字符串的区域设置类别(例如 "LC_ALL")。将按顺序尝试传递的 locales,并使用第一个有效的区域设置。

test.support.make_bad_fd()

通过打开和关闭临时文件并返回其描述符来创建无效的文件描述符。

test.support.import_module(name, deprecated=False)

此函数导入并返回指定的模块。与正常导入不同,如果无法导入模块,此函数会引发 unittest.SkipTest

如果 deprecatedTrue,则在导入期间将禁止模块和软件包弃用消息。

3.1 新版功能.

test.support.import_fresh_module(name, fresh=(), blocked=(), deprecated=False)

此函数通过在执行导入之前从 sys.modules 中删除命名的模块,导入并返回命名的Python模块的新副本。请注意,与 reload() 不同,原始模块不受此操作的影响。

fresh 是一个可迭代的附加模块名称,在执行导入之前,这些名称也会从 sys.modules 缓存中删除。

blocked 是模块名称的迭代,在导入期间在模块高速缓存中用 None 替换,以确保导入它们的尝试提高 ImportError

命名模块和 freshblocked 参数中命名的任何模块在开始导入之前保存,然后在新导入完成时重新插入到 sys.modules 中。

如果 deprecatedTrue,则在导入期间将禁止模块和软件包弃用消息。

如果无法导入指定的模块,此函数将引发 ImportError

使用示例:

# Get copies of the warnings module for testing without affecting the
# version being used by the rest of the test suite. One copy uses the
# C implementation, the other is forced to use the pure Python fallback
# implementation
py_warnings = import_fresh_module('warnings', blocked=['_warnings'])
c_warnings = import_fresh_module('warnings', fresh=['_warnings'])

3.1 新版功能.

test.support.bind_port(sock, host=HOST)

将套接字绑定到空闲端口并返回端口号。依靠临时端口,以确保我们使用未绑定的端口。这很重要,因为许多测试可能同时运行,特别是在buildbot环境中。如果 sock.familyAF_INET 并且 sock.typeSOCK_STREAM,并且套接字上设置了 SO_REUSEADDRSO_REUSEPORT,则此方法引发异常。测试不应该为TCP/IP套接字设置这些套接字选项。设置这些选项的唯一情况是测试通过多个UDP套接字的多播。

此外,如果 SO_EXCLUSIVEADDRUSE 套接字选项可用(即在Windows上),它将在套接字上设置。这将防止任何人在测试期间绑定到我们的主机/端口。

test.support.find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM)

返回应该适合绑定的未使用端口。这是通过创建与 sock 参数(默认为 AF_INETSOCK_STREAM)相同的家族和类型的临时套接字,并将其绑定到指定的主机地址(默认为 0.0.0.0),端口设置为0,实现,引出一个未使用的临时端口。然后关闭并删除临时套接字,并返回临时端口。

对于在测试期间需要将服务器套接字绑定到特定端口的任何测试,都应使用此方法或 bind_port()。使用哪一个取决于调用代码是创建一个python套接字,或者如果一个未使用的端口需要在构造函数中提供或传递给外部程序(即openssl的s_server模式的 -accept 参数)。在可能的情况下,bind_port() 始终优先于 find_unused_port()。不建议使用硬编码端口,因为它可以使多个测试实例无法同时运行,这是buildbots的一个问题。

test.support.load_package_tests(pkg_dir, loader, standard_tests, pattern)

通用实现 unittest load_tests 协议用于测试包。 pkg_dir 是包的根目录; loaderstandard_testspatternload_tests 期望的参数。在简单的情况下,测试包的 __init__.py 可以如下:

import os
from test.support import load_package_tests

def load_tests(*args):
    return load_package_tests(os.path.dirname(__file__), *args)
detect_api_mismatch(ref_api, other_api, *, ignore=()):

返回在 other_api 上找不到的 ref_api 的属性,函数或方法集,除了在 ignore 中指定的此检查中要忽略的定义的项目列表之外。

默认情况下,这将跳过以“_”开头的私有属性,但包括所有魔术方法,即以“__”开头和结尾的方法。

3.5 新版功能.

test.support.check__all__(test_case, module, name_of_module=None, extra=(), blacklist=())

断言 module__all__ 变量包含所有公共名称。

模块的公共名称(其API)根据它们是否匹配公共名称约定并在 module 中定义而自动检测。

name_of_module 参数可以指定(作为其字符串或元组)什么模块可以定义API以便被检测为公共API。一种情况是,module 从其他模块(可能是C后端(如 csv 及其 _csv))导入其公共API的一部分。

extra 参数可以是一组不会被自动检测为“public”的名称,如没有正确 __module__ 属性的对象。如果提供,它将被添加到自动检测到的。

blacklist 参数可以是一组不能被视为公共API的一部分的名称,即使它们的名称另有说明。

使用示例:

import bar
import foo
import unittest
from test import support

class MiscTestCase(unittest.TestCase):
    def test__all__(self):
        support.check__all__(self, foo)

class OtherTestCase(unittest.TestCase):
    def test__all__(self):
        extra = {'BAR_CONST', 'FOO_CONST'}
        blacklist = {'baz'}  # Undocumented name.
        # bar imports part of its API from _bar.
        support.check__all__(self, bar, ('bar', '_bar'),
                             extra=extra, blacklist=blacklist)

3.6 新版功能.

test.support 模块定义以下类:

class test.support.TransientResource(exc, **kwargs)

实例是一个上下文管理器,如果引发指定的异常类型,则提升 ResourceDenied。任何关键字参数都被视为属性/值对,以便与 with 语句中引发的任何异常进行比较。只有当所有对匹配正确的属性上的异常是 ResourceDenied 引发。

class test.support.EnvironmentVarGuard

用于临时设置或取消设置环境变量的类。实例可以用作上下文管理器,并具有用于查询/修改底层 os.environ 的完整字典接口。从上下文管理器退出后,通过此实例完成的对环境变量的所有更改将被回滚。

在 3.1 版更改: 添加字典界面。

EnvironmentVarGuard.set(envvar, value)

将环境变量 envvar 临时设置为 value 的值。

EnvironmentVarGuard.unset(envvar)

临时取消设置环境变量 envvar

class test.support.SuppressCrashReport

一个上下文管理器,用于尝试防止崩溃对话框弹出在预期会导致子进程崩溃的测试。

在Windows上,它使用 SetErrorMode 禁用Windows错误报告对话框。

在UNIX上,resource.setrlimit() 用于将 resource.RLIMIT_CORE 的软限制设置为0,以防止创建coredump文件。

在这两个平台上,旧值由 __exit__() 恢复。

class test.support.WarningsRecorder

用于记录单元测试警告的类。有关更多详细信息,请参阅上面的 check_warnings() 文档。