25.5. test —用于 Python 的回归测试包

Note

test软件包仅供 Python 内部使用。它被记录为 Python 核心开发人员的利益。不建议在 Python 标准库之外使用此软件包,因为此处提到的代码可以更改,也可以在不考虑 Python 版本之间删除的情况下删除。

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

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

See also

  • Module unittest

  • 编写 PyUnit 回归测试。

  • Module doctest

  • 测试嵌入在文档字符串中。

25.5.1. 为测试包编写单元测试

使用unittest模块的测试最好遵循一些准则。一种是pass以test_开头并以被测试模块的名称结尾来命名测试模块。测试模块中的测试方法应以test_开头,并以对该方法进行测试的描述结尾。这是必需的,以便测试驱动程序将这些方法识别为测试方法。另外,不应包括该方法的文档字符串。Comments(例如# 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 ...

def test_main():
    support.run_unittest(MyTestCase1,
                         MyTestCase2,
                         ... list other tests ...
                         )

if __name__ == '__main__':
    test_main()

此样板代码允许测试套件由test.regrtest运行,也可以作为脚本独立运行。

回归测试的目标是try破坏代码。这导致需要遵循一些准则:

  • 测试套件应使用所有类,函数和常量。这不仅包括要向外界展示的外部 API,还包括“私有”代码。

  • 首选白盒测试(在编写测试时检查被测试的代码)。黑盒测试(仅测试已发布的用户界面)还不足以确保测试所有边界和边缘情况。

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

  • 耗尽尽可能多的代码路径。测试分支发生的位置,并因此调整 Importing,以确保采用代码中的许多不同路径。

  • 添加一个显式测试,以发现针对测试代码发现的所有错误。如果将来更改代码,这将确保错误不会再次出现。

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

  • 如果测试取决于 os 的特定条件,则在try测试之前,请先验证条件是否已经存在。

  • 导入尽可能少的模块,并尽快进行。这样可以最大程度地减少测试的外部依赖性,还可以最大程度地减少由于导入模块的副作用而可能引起的异常行为。

  • try使代码重用最大化。有时,测试的差异可能与所使用的 Importing 类型一样小。pass使用指定 Importing 的类将基本测试类子类化,从而最大程度地减少代码重复:

class TestFuncAcceptsSequences(unittest.TestCase):

    func = mySuperWhammyFunction

    def test_func(self):
        self.func(self.arg)

class AcceptLists(TestFuncAcceptsSequences):
    arg = [1, 2, 3]

class AcceptStrings(TestFuncAcceptsSequences):
    arg = 'abc'

class AcceptTuples(TestFuncAcceptsSequences):
    arg = (1, 2, 3)

See also

  • 测试驱动开发

  • 肯特·贝克(Kent Beck)写的一本关于在代码之前编写测试的书。

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

借助-m选项,可以将test.regrtest模块作为脚本运行,以驱动 Python 的回归测试套件:python -m test.regrtest .单独运行脚本会自动开始运行test软件包中的所有回归测试.pass查找包中所有名称以test_开头的模块,导入它们并执行test_main()(如果存在)来完成此操作.要执行的测试的名称也可以传递给脚本.指定单个回归测试( python -m test.regrtest test_spam **)将最小化输出,并且仅打印测试pass还是失败,从而将输出最小化。

直接运行test.regrtest可以设置可用于测试的资源。您可以使用-u命令行选项来执行此操作。将all指定为-u选项的值将启用所有可能的资源: python -m test.regrtest -uall 。如果需要除一种资源以外的所有资源(更常见的情况),则可以在all之后列出用逗号分隔的不需要的资源列表。命令 python -m test.regrtest -uall,-audio,-largefile 将使用test.regrtestlargefile资源以外的所有资源运行test.regrtest。有关所有资源和更多命令行选项的列表,请运行 python -m test.regrtest -h

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

在版本 2.7.14 中更改:test软件包可以作为脚本运行: python -m test 。这与运行test.regrtest模块相同。

25.6. test.support —用于测试的 Util Function

Note

test.test_support模块已在 Python 3.x 和 2.7.14 中重命名为test.support。名称test.test_support在 2.7 中已保留为别名。

test.support模块提供对 Python 回归测试的支持。

该模块定义以下异常:

  • exception test.support. TestFailed

  • exception test.support. ResourceDenied

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

  • test.support. verbose

    • 启用详细输出时为True。当需要有关运行测试的更多详细信息时,应进行检查。 * verbose *由test.regrtest设置。
  • test.support. have_unicode

    • True当 Unicode 支持可用时。
  • test.support. is_jython

    • True(如果正在运行的 Interpreter 是 Jython)。
  • test.support. TESTFN

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

    • 定义用于网络测试的专用 HTTP 服务器的 URL。

test.support模块定义以下Function:

  • test.support. forget(* module_name *)

    • sys.modules中删除名为* module_name *的模块,并删除该模块的所有字节编译文件。
  • test.support. is_resource_enabled(资源)

    • 如果* resource *已启用且可用,则返回True。仅当test.regrtest执行测试时才设置可用资源列表。
  • test.support. requires(* resource * [,* msg *])

    • 如果* resource *不可用,请提高ResourceDenied。 * msg *是ResourceDenied的参数(如果引发)。如果由__name__'__main__'的函数调用,则始终返回True。在test.regrtest执行测试时使用。
  • test.support. findfile(* filename *)

    • 将路径返回到名为* filename 的文件。如果找不到匹配项,则返回 filename *。这并不等于失败,因为它可能是文件的路径。
  • test.support. run_unittest(*)

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

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

def test_main():
    support.run_unittest(__name__)

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

  • test.support. check_warnings(** filters quiet = True *)
    • warnings.catch_warnings()的便捷包装器,可以更轻松地测试警告是否正确发出。大约等效于将warnings.simplefilter()设置为always并具有自动验证记录结果的选项来调用warnings.catch_warnings(record=True)的情况。

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

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

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

在这种情况下,将捕获所有警告,并且不会引发任何错误。

进入上下文 Management 器后,将返回一个WarningRecorder实例。 catch_warnings()的基础警告列表可pass Logger 对象的warnings属性获得。为方便起见,代表最新警告的对象的属性也可以直接pass Logger 对象访问(请参见下面的示例)。如果未提出警告,则表示警告的对象上原应具有的任何属性都将返回None

Logger 对象还具有reset()方法,该方法清除警告列表。

上下文 Management 器的设计用途如下:

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

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

2.6 版的新Function。

在 2.7 版中进行了更改:新的可选参数* filters quiet *。

  • test.support. check_py3k_warnings(** filters quiet = False *)
    • check_warnings()类似,但适用于 Python 3 兼容性警告。如果为sys.py3kwarning == 1,则检查警告是否有效发出。如果sys.py3kwarning == 0,则检查是否未发出警告。它接受格式为("message regexp", WarningCategory)的 2Tuples 作为位置参数。当可选关键字参数* quiet *为True时,如果过滤器什么也没捕获,它不会失败。如果没有参数,则默认为:
check_py3k_warnings(("", DeprecationWarning), quiet=False)

2.7 版的新Function。

  • test.support. captured_stdout ( )
    • 这是一个上下文 Management 器,它使用StringIO.StringIO对象作为 sys.stdout 运行with语句主体。可以使用with语句的as子句检索该对象。

Example use:

with captured_stdout() as s:
    print "hello"
assert s.getvalue() == "hello\n"

2.6 版的新Function。

  • test.support. import_module(* name deprecated = False *)
    • 该函数导入并返回命名模块。与普通导入不同,如果无法导入模块,此函数将引发unittest.SkipTest

如果* deprecated *为True,则会在导入过程中禁止使用模块和程序包弃用消息。

2.7 版的新Function。

  • test.support. import_fresh_module(* name fresh =() blocked =() deprecated = False *)
    • 此函数pass在导入之前从sys.modules删除命名模块来导入并返回命名 Python 模块的新副本。请注意,与reload()不同,原始模块不受此操作的影响。
  • fresh *是可迭代的其他模块名称,这些名称在执行导入之前也已从sys.modules缓存中删除。

  • blocked *是可重复使用的模块名称,在导入过程中模块高速缓存中的模块名称被0替换,以确保导入它们的try引发ImportError

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

如果* deprecated *为True,则会在导入过程中禁止使用模块和程序包弃用消息。

如果无法导入命名模块,则此函数将引发unittest.SkipTest

Example use:

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

2.7 版的新Function。

test.support模块定义以下类别:

    • class * test.support. TransientResource(* exc * [,*** kwargs *])
    • 实例是上下文 Management 器,如果引发了指定的异常类型,则引发ResourceDenied。任何关键字参数都被视为属性/值对,以便与with语句中引发的任何异常进行比较。仅当所有对与异常的属性正确匹配时,才会引发ResourceDenied

2.6 版的新Function。

  • 类别 test.support. EnvironmentVarGuard
    • 用于临时设置或取消设置环境变量的类。实例可以用作上下文 Management 器,并具有用于查询/修改基础os.environ的完整词典接口。从上下文 Management 器退出后,pass该实例完成的所有对环境变量的更改将被回滚。

2.6 版的新Function。

在 2.7 版中进行了更改:添加了词典界面。

  • EnvironmentVarGuard. set(* envvar value *)

    • 暂时将环境变量envvar设置为value的值。
  • EnvironmentVarGuard. unset(* envvar *)

    • 暂时取消设置环境变量envvar
  • 类别 test.support. WarningsRecorder

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

2.6 版的新Function。