用 Python 测试框架简化测试
最近出现了行业级的 Python 测试框架,这意味着 Python 测试可以编写得更简洁、更统一,能够产生更好的结果报告。本文介绍比较先进的测试框架并讨论它们的基本特性。
Python 测试领域的蛮荒时代结束了。仅仅在几年前,几乎每个用 Python 构建的项目在编写和运行测试方面都采用自己的习惯做法。但是现在,这种混乱局面终于要结束了。社区中已经出现了几种出色的测试解决方案,它们为数百个流行 的项目的测试套件提供约定和通用标准。
本文是 三篇系列文章 的第一篇,本系列要介绍新的测试框架。在本文中,将介绍三种流行的测试框架,讨论新一代工具鼓励的简单测试风格。第二篇文章发现和选择测试 讨论更大的问题:这些框架如何自动地发现和分类项目的测试。最后一篇文章用 Python 测试框架生成测试报告 讨论这些框架为查看测试结果提供的强大特性。
通过学习这三种框架的通用习惯做法,您不但可以更好地理解其他程序员编写的 Python 包,还可以为自己的应用程序构建优雅、强大的测试套件。
候选框架:三种 Python 测试框架
有三种 Python 测试框架是目前使用最为广泛的。下面按时间次序简要介绍它们:
zope.testing
通常,从事 Zope 项目的开发人员就像是早期的拓荒者。他们需要以一种统一的方式在大型代码基上发现和运行测试,为此开发了 zope.testing 包,这个包现在仍然得到广泛使用。
zope.testing 包只支持 unittest 和 doctest 等传统 Python 测试风格,而不支持更现代的框架支持的简化风格。但是,它提供一个强大的分层系统,在这种系统中包含测试的目录可以依赖于通用的设置代码,设置代码为层 (而不是每个测试)创建一个运行测试所需的环境。
py.test
2004 年,Holger Krekel 把他的 std 包改名为 ‘py’,因为原来的名称常常与 Python 附带的标准库混淆。尽管这个包包含另外几个子包,但是现在最著名的部分只有它的 py.test 框架。
py.test 框架建立了 Python 测试的新标准,目前许多开发人员都采用这种标准。它为编写测试提供了优雅的符合 Python 风格的习惯做法,让开发人员能够以非常紧凑的风格编写测试套件。
nose
nose 项目是于 2005 年发布的,也就是 py.test 改名后的一年。它是由 Jason Pellerin
编写的,支持与 py.test 相同的测试习惯做法,但是这个包更容易安装和维护。尽管 py.test 在某些方面有所进步,目前也很容易安装,但是
nose 仍然保持了易用性方面的声誉。
在 Python 大会上,常常会看到开发人员穿着黑色 T 恤衫,上面印着 nosetests 命令,后面是表示测试成功的点号。有意思的是,随着 nose 的发展,在项目邮件列表上常常看到开发人员向项目负责人询问他们的项目什么时候可以转换到 nose。
在这三种框架中,nose 看起来会成为标准,py.test 的用户群比较小,但是很忠诚,zope.testing 只在基于 Zope 框架的项目中受欢迎。但是,这三种框架都得到积极的维护,各有一些独特的特性。建议您了解它们的特性和差异,选择适合自己项目的框架。
测试演化
py.test 框架接受普通的 Python 函数作为测试,而不要求把测试放在更大更重型的测试类中,这开启了 Python 测试的新时代。因为 nose 框架支持相同的习惯做法,所以这些模式很可能越来越流行。
假设希望检查 Python 真假值 True 和 False 是否真的等于布尔数字 1 和 0。py.test 或 nose 接受并运行以下代码行,作为回答此问题的有效测试:
# test_new.py - simple tests functions def testTrue(self): assert True == 1 def testFalse(self): assert False == 0 |
为了体会以上示例的简单性,可以对比过去的 Python 测试文档中复杂的示例测试,比如:
# test_old.py - The old way of doing things import unittest class TruthTest(unittest.TestCase): def testTrue(self): assert True == 1 def testFalse(self): assert False == 0 if __name__ == '__main__': unittest.main() |
看到了吗?这么多代码只是为了支持两行测试代码!首先,代码需要一个 import
语句,这与要测试的代码完全无关,因为测试本身简单地忽略模块,只使用内置的 Python 值,比如 True 和
False。另外,要创建一个类,但是它不支持或增强测试,因为测试实际上没有使用它们的 self
参数做任何事情。最后,需要两行固定不变的代码,这样才能从命令行运行这个测试。
有使用 unittest 经验的用户可能认为,上面的示例应该使用 TruthTest 类从 TestCase 类继承的测试方法。例如,他们会建议使用 assertEqual(),而不是手工测试是否相等的 assert 语句,在这种情况下测试会使用 self 而不是忽略它:
# alternate version of the TestTrue method ... def testTrue(self): self.assertEqual(True, 1) ... |
对这个建议有三条反对意见。