25.5. test — Python 的回归测试包 — Python 文档

来自菜鸟教程
Python/docs/2.7/library/test
跳转至:导航、​搜索

25.5. 测试 — Python 的回归测试包

笔记

test 包仅供 Python 内部使用。 为了 Python 的核心开发人员的利益,它被记录下来。 不鼓励在 Python 标准库之外使用此包,因为此处提到的代码可能会在 Python 版本之间更改或删除,恕不另行通知。


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

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

也可以看看

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


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

使用 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 ...

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

if __name__ == '__main__':
    test_main()

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

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

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

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

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

  • 用尽尽可能多的代码路径。 测试分支发生的位置,从而调整输入以确保通过代码采用尽可能多的不同路径。

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

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

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

  • 导入尽可能少的模块并尽快完成。 这最大限度地减少了测试的外部依赖性,也最大限度地减少了导入模块的副作用可能导致的异常行为。

  • 尝试最大化代码重用。 有时,测试会因使用的输入类型而有所不同。 通过使用指定输入的类对基本测试类进行子类化来最小化代码重复:

    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)

也可以看看

测试驱动开发
Kent Beck 关于在编写代码之前编写测试的书。


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

test.regrtest 模块可以作为脚本运行来驱动 Python 的回归测试套件,这要归功于 -m 选项:python -m test.regrtest。 单独运行脚本会自动开始运行 test 包中的所有回归测试。 它通过查找包中名称以 test_ 开头的所有模块、导入它们并执行函数 test_main()(如果存在)来实现此目的。 要执行的测试的名称也可以传递给脚本。 指定单个回归测试 (python -m test.regrtest test_spam) 将最小化输出,只打印测试是通过还是失败,从而最小化输出。

直接运行 test.regrtest 可以设置哪些资源可用于测试使用。 您可以使用 -u 命令行选项执行此操作。 将 all 指定为 -u 选项的值会启用所有可能的资源:python -m test.regrtest -uall。 如果只需要一个资源(一种更常见的情况),则不需要的资源的逗号分隔列表可能会列在 all 之后。 命令 python -m test.regrtest -uall,-audio,-largefile 将使用除 audiolargefile 资源之外的所有资源运行 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.test_support 模块已在 Python 3.x 和 2.7.14 中重命名为 test.support。 名称 test.test_support 在 2.7 中作为别名保留。


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

该模块定义了以下异常:

exception test.support.TestFailed
测试失败时引发的异常。 这已被弃用,以支持基于 unittest 的测试和 unittest.TestCase 的断言方法。
exception test.support.ResourceDenied
unittest.SkipTest 的子类。 当资源(例如网络连接)不可用时引发。 由 requires() 函数引发。

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

test.support.verbose
True 启用详细输出时。 当需要有关运行测试的更多详细信息时,应进行检查。 verbosetest.regrtest 设置。
test.support.have_unicode
True 当 Unicode 支持可用时。
test.support.is_jython
True 如果正在运行的解释器是 Jython。
test.support.TESTFN
设置为可安全用作临时文件名称的名称。 创建的任何临时文件都应关闭并取消链接(删除)。
test.support.TEST_HTTP_URL
为网络测试定义专用 HTTP 服务器的 URL。

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])
如果 resource 不可用,则提高 ResourceDeniedmsgResourceDenied 如果它被引发的参数。 如果由 __name__'__main__' 的函数调用,则始终返回 True。 当测试由 test.regrtest 执行时使用。
test.support.findfile(filename)
返回名为 filename 的文件的路径。 如果未找到匹配项,则返回 文件名 。 这并不等于失败,因为它可能是文件的路径。
test.support.run_unittest(*classes)

执行传递给函数的 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.catch_warnings(record=True) 并将 warnings.simplefilter() 设置为 always 并带有自动验证记录结果的选项。

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

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

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

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

在进入上下文管理器时,会返回一个 WarningRecorder 实例。 来自 catch_warnings() 的底层警告列表可通过记录器对象的 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

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

2.6 版中的新功能。

2.7 版更改: 新的可选参数 filtersquiet

test.support.check_py3k_warnings(*filters, quiet=False)

类似于 check_warnings(),但针对 Python 3 兼容性警告。 如果是 sys.py3kwarning == 1,它会检查警告是否有效。 如果是 sys.py3kwarning == 0,它会检查是否没有发出警告。 它接受 ("message regexp", WarningCategory) 形式的 2 元组作为位置参数。 当可选关键字参数 quietTrue 时,如果过滤器什么也没捕获,它不会失败。 没有参数,它默认为:

check_py3k_warnings(("", DeprecationWarning), quiet=False)

2.7 版中的新功能。

test.support.captured_stdout()

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

使用示例:

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

2.6 版中的新功能。

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

此函数导入并返回命名模块。 与普通导入不同,如果模块无法导入,此函数会引发 unittest.SkipTest

如果 deprecatedTrue,则在此导入过程中会抑制模块和包弃用消息。

2.7 版中的新功能。

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

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

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

blocked 是一个可迭代的模块名称,在导入期间在模块缓存中替换为 0,以确保尝试导入它们会引发 ImportError

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

如果 deprecatedTrue,则在此导入过程中会抑制模块和包弃用消息。

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

使用示例:

# 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 版中的新功能。

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

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

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

2.6 版中的新功能。

class test.support.EnvironmentVarGuard

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

2.6 版中的新功能。

2.7 版本变化:增加字典接口。

EnvironmentVarGuard.set(envvar, value)
临时设置环境变量envvarvalue的值。
EnvironmentVarGuard.unset(envvar)
暂时取消设置环境变量 envvar
class test.support.WarningsRecorder

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

2.6 版中的新功能。