26.8. test - Python的回归测试包

注意

test包仅供Python内部使用。它被记录为Python的核心开发人员的利益。任何使用这个包外面的Python的标准库是不鼓励的,因为这里提到的代码可以改变或删除没有通知Python版本之间。

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

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

也可以看看

模块unittest
编写PyUnit回归测试。
模块doctest
文档字符串中嵌入的测试。

26.8.1. Writing Unit Tests for the test package

优选地,使用unittest模块的测试遵循一些准则。一种是通过使用test_启动测试模块来命名测试模块,并以被测试模块的名称结束测试模块。测试模块中的测试方法应以test_开始,并以该方法正在测试的内容结束。这是需要的,使得方法被测试驱动程序识别为测试方法。此外,不应包括方法的文档字符串。评论(如 测试 功能 仅返回 True 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. Running tests using the command-line interface

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

运行test直接允许使用哪些资源来进行测试。您可以使用-u命令行选项。Specifying all as the value for the -u option enables all possible resources: python -m test -uall. 如果仅需要一个资源(更常见的情况),则可以在all之后列出不期望的逗号分隔的资源列表。The command python -m test -uall,-audio,-largefile will run test with all resources except the audio and largefile resources. 有关所有资源和更多命令行选项的列表,请运行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)

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

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

引发ResourceDenied如果资源不可用。msgResourceDenied的参数,如果它被引发。如果由__name__'__main__'的函数调用,则始终返回True当测试由test.regrtest执行时使用。

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

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

Setting subdir indicates a relative path to use to find the file rather than looking directly in the path directories.
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)

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

If verbosity is None, doctest.testmod() is run with verbosity set to verbose. 否则,它的详细程度设置为None

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

warnings.catch_warnings()的便利包装,可以更容易地测试警告是否正确引发。它大致相当于调用warnings.catch_warnings(record=True)warnings.simplefilter()设置为always验证记录的结果。

check_warnings接受以下形式的2元组:(“message regexp”, WarningCategory) t2 >作为位置参数。如果提供了一个或多个过滤器,或者如果可选的关键字参数quietFalse,它会检查以确保警告符合预期:每个指定的过滤器必须匹配附带代码引发的至少一个警告或测试失败,并且如果出现任何与任何指定过滤器不匹配的警告,测试将失败。要禁用这些检查中的第一个,请将安静设置为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中更改:新的可选参数过滤器安静

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)

路径创建临时目录并生成目录的上下文管理器。

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

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

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

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

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

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

上下文管理器在临时更改当前工作目录之前,在名为name的当前目录中创建一个临时目录。如果name为None,则使用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")。将按顺序尝试传递的语言环境,并使用第一个有效语言环境。

test.support.make_bad_fd()

通过打开和关闭临时文件,并返回其描述器,创建一个无效的文件描述器。

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

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

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

版本3.1中的新功能。

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

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

fresh是在执行导入之前,还从sys.modules高速缓存中删除的其他模块名称的迭代。

已阻止是在导入期间在模块高速缓存中替换为None的模块名称的可迭代,以确保尝试导入它们引发ImportError

在开始导入之前保存命名的模块以及在新鲜阻塞参数中命名的任何模块,然后重新插入sys.modules导入完成。

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

如果无法导入指定的模块,此函数将引发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_INETsock.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参数)。始终优先于find_unused_port() bind_port()不建议使用硬编码端口,因为它可以使多个测试实例无法同时运行,这是buildbots的一个问题。

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

通用实现用于测试包的unittest load_tests协议。pkg_dir是包的根目录; 装载器standard_tests模式load_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模块定义以下类:

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()文档。