Python 测试框架:选择和运行测试

发表于:2009-9-14 14:44

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:未知    来源:IBM

  最近出现了行业级的 Python 测试框架,这意味着 Python 测试可以编写得更简洁、更统一,能够产生更好的结果报告。本文讨论三种最流行的测试框架如何识别和收集测试,以及它们如何支持编写完整的测试层,共享共同的 setup 和 teardown 代码。

  这三篇系列文章 的 第一篇文章 讨论了标准测试框架(比如 zope.testing、py.test 和 nose)给 Python 测试领域带来的革命性影响。这些框架支持更简单的测试方法,让项目不再需要为运行测试编写和维护专门的代码。第二篇文章 讨论了这些自动化解决方案如何搜索 Python 包以识别可能包含测试的模块。

  本文讨论下一步,介绍框架在找到测试模块之后如何发现其中的待测试项。还讨论一些细节,比如这三种框架对共同的 setup 和 teardown 代码的支持情况。

  Zope 框架中的测试发现

  决定了感兴趣的模块列表之后,如何发现其中的实际测试呢?

  对于 zope.testing 框架,您会发现 Zope 社区有一些有意思的现象。Zope 社区并不为解决每个问题构建大型工具,而是构建小型的功能有限的工具,这些工具能够连接在一起。目前,zope.testing 模块本身实际上根本没有提供检测测试的机制!

  相反,zope.testing 让程序员自己寻找每个模块中需要运行的测试并把它们集中在一个列表中。它在每个测试模块中只寻找一个东西:test_suite() 函数,这个函数应该返回标准 unittest.TestSuite 类的实例,其中包含模块定义的测试。

  使用 zope.testing 的一些程序员在 test_suite() 函数中手工地创建和维护测试列表。其他程序员通过编写定制代码发现已经定义的可用测试。但是,最有意思的方法是使用另一个 Zope 包 z3c.testsetup,它能够像其他现代 Python 测试框架一样自动地发现包中的测试。

  这一现象再次说明 Zope 程序员倾向于编写小型代码块,然后使用它们构建框架,而不是编写大型的全面解决方案。z3c.testsetup 包不包含可以选择测试的命令行界面,也不包含可以显示测试结果的输出模块;它完全依靠 zope.testing 实现这些功能。

  实际上,z3c.testsetup 用户一般不使用 zope.testing 的测试模块发现功能。相反,他们绕开 zope.testing 的算法,按照它的默认行为只寻找名为 test.py 的模块,然后在整个源代码树中只提供一个采用此名称的模块。在最简单的情况下,他们的 test.py 像下面这样:

  import z3c.testsetup

  test_suite = z3c.testsetup.register_all_tests(my_package)

  这完全不通过 zope.testing 执行测试发现任务,而是依靠 z3c.testsetup 本身提供的更强大的发现机制。

  可以向 register_all_tests() 函数提供几个配置选项。详细信息请参见 z3c.testsetup 文档,这里只需要介绍它的基本行为。与本文讨论的其他框架不同,z3c.testsetup 在默认情况下不关心包中每个 Python 模块的名称,而是关注它的内容。它检查所有模块以及包中的所有 .txt 或 .rst 文件,选择文本中指定了 :Test-Layer: 的文件。然后,它组合模块中的所有 TestCase 和文本文件中的所有 doctest 部分,形成测试套件。

  使用:Test-Layer: 字符串标出包含测试的文件是一种有意思的机制。它的缺点是,在浏览包的文件时,为了找到测试的位置,新程序员必须打开每个文件,至少要用 grep 命令寻找 :Test-Layer: 字符串。(更不用提 z3c.testsetup 显然必须做同样的事;这使它比那些只操作文件名的框架要慢)。

  最后注意,Zope 测试框架只支持 UnitTest 实例或 doctest。正如本系列的第一篇文章中讨论的,更现代的 Python 测试框架还支持一般的 Python 函数作为有效测试。这需要不同的测试检测算法,在下面讨论的框架中就会看到。

  py.test 和 nose 中的测试发现

  正如前一篇文章中提到的,py.test 和 nose 框架使用相似但略有差异的规则集搜索 Python 包,寻找它们认为包含测试的模块。但是,之后它们都会遇到相同的情况:它们必须检查模块列表,寻找开发人员希望作为测试运行的函数和类。

  正如在前一篇文章中看到的,py.test 往往选择单一标准,期望使用它的所有项目都遵守这一标准;而 nose 允许更丰富的定制,但是这会牺牲行为的可预测性。对于测试发现,也是如此:py.test 按照固定、不可变且可预测的规则检测测试模块中的测试,而 nose 采用灵活的可定制的规则。如果项目使用 nose 执行测试,就先必须阅读项目的 setup.cfg 文件,了解 nose 是采用通常的测试检测规则,还是采用这个项目特有的规则。

  下面是 py.test 使用的过程:

  当 py.test 检查 Python 测试模块的内部时,它收集名称以 test_ 开头的每个函数和名称以 Test 开头的每个类。无论类是否继承自 unittest.TestCase,它都会收集它们。

  测试函数直接运行,但是对于测试类,还必须搜索方法。类实例化之后,作为测试运行名称以 test_ 开头的所有方法。

  如果测试类继承自标准的 Python unittest.TestCase 类,py.test 框架会表现出一种古怪的行为:如果类不包含 runTest() 方法,那么即使它包含几个 test_ 方法,py.test 也会抛出异常并失败。但是,如果存在 runTest() 方法,py.test 会忽略它;这个方法必须存在,py.test 才能接受这个类,但是不会运行这个方法,因为它的名称不是以 test_ 开头的。

  为了纠正这种行为,可以在项目的 conttest.py 文件中或使用 -p 命令行选项激活框架的 unittest 插件:

  $ py.test -p unittest

  这会让 py.test 对其行为做三个更改。首先,不再只检测名称以 Test 开头的类,还会检测继承自 unittest.TestCase 的其他类。第二,对于没有提供 runTest() 方法的 TestCase 子类,py.test 不再报告异常。第三,在类包含的测试之前和之后,以标准方式正确地运行 TestCase 子类中的所有 setUp() 和 tearDown() 方法。

  尽管 nose 提供更强的定制能力,但是比较简单的测试发现过程如下:

  当 nose 检查 Python 测试模块的内部时,它采用在选择测试模块时使用的正则表达式,收集与这个正则表达式匹配的函数和类。(在默认情况下,寻找包含单词 Test 或 test 的名称,但是可以通过命令行或配置文件提供不同的正则表达式)。

  当 nose 检查测试类的内部时,它运行与同一正则表达式匹配的方法。

  无须特别指定,nose 总会检测 unittest.TestCase 的子类并作为测试使用它们。但是,它会根据自己的正则表达式决定哪些方法是测试,而不使用标准的 unittest 模式 ^test。

31/3123>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号