编写Python测试的最新方法是什么?使用什么模块/框架?
还有一个问题:doctest
测试仍有任何价值吗?或者是否应该在更现代的测试框架中编写所有测试?
谢谢,Boda Cydo。
答案 0 :(得分:12)
通常的方法是使用内置unittest模块来创建单元测试并将它们捆绑在一起,以测试可以独立运行的套件。 unittest
与jUnit非常相似(并受其启发),因此非常易于使用。
如果您对最新的变化感兴趣,请查看Michael Foord的新PyCon演讲:
答案 1 :(得分:9)
使用内置的unittest
模块与以往一样简单易用。其他单元测试选项py.test
,nose
和twisted.trial
大多与unittest
兼容。
Doctests与他们一直有着相同的价值 - 他们非常适合测试您的文档,而不是您的代码。如果您要在文档字符串中放置代码示例,doctest可以确保您保持正确和最新。没有什么比尝试重现一个例子和失败更糟糕了,只是为了后来才意识到它实际上是文档的错误。
答案 2 :(得分:5)
我对doctests知之甚少,但在我的大学,我们会教授和鼓励鼻子测试。
可以按照以下步骤安装鼻子(我假设您使用的是PC - Windows操作系统):
如果您使用的是其他操作系统,请检查this site
修改强>:
我写这篇文章已经两年了。现在,我已经了解了这个名为Designing by Contract的编程原理。这允许程序员为其代码中的所有函数定义前置条件,后置条件和不变量(称为契约)。结果是,如果违反任何这些合同,就会引发错误。
我建议用于python的DbC框架称为PyContract我已成功在evolutionary programming framework
中使用它答案 3 :(得分:2)
在我目前的项目中,我正在使用unittest,minimock,nose。在过去,我已经大量使用了doctests,但在大型项目中,某些测试可能会变得有点笨拙,所以我倾向于保留使用doctests来实现更简单的功能。
如果您正在使用setuptools或distribute(您应该切换到分发),您可以将nose设置为默认测试收集器,以便您可以使用“python setup.py”运行测试测试“
setup(name='foo',
...
test_suite='nose.collector',
...
现在运行“python setup.py test”会调用nose,它会抓取你的项目看起来像测试并运行它们,累积结果。如果您的项目中也有doctests,则可以使用--with-doctest选项运行nosetests以启用doctest插件。
鼻子也与coverage
整合nosetests --with-coverage.
您还可以使用 - cover-html --cover-html-dir 选项为每个模块生成HTML覆盖率报告,其中每行代码均未突出显示。我不会过于沉迷于报告所有模块的100%测试覆盖率。有些代码最好留给集成测试,我将在最后介绍。
我已成为minimock的忠实粉丝,因为它使得具有大量外部依赖性的测试代码非常容易。虽然与doctest配对时它非常,但它可以与任何使用 unittest.TraceTracker 类的测试框架一起使用。我鼓励您尽量避免使用它来测试代码的所有,因为您仍应尝试编写代码,以便可以单独测试每个翻译单元而无需进行模拟。有时候这是不可能的。
以下是使用minimock和unittest进行此类测试的(未经测试)示例:
# tests/test_foo.py
import minimock
import unittest
import foo
class FooTest(unittest2.TestCase):
def setUp(self):
# Track all calls into our mock objects. If we don't use a TraceTracker
# then all output will go to stdout, but we want to capture it.
self.tracker = minimock.TraceTracker()
def tearDown(self):
# Restore all objects in global module state that minimock had
# replaced.
minimock.restore()
def test_bar(self):
# foo.bar invokes urllib2.urlopen, and then calls read() on the
# resultin file object, so we'll use minimock to create a mocked
# urllib2.
urlopen_result = minimock.Mock('urlobject', tracker=self.tracker)
urlopen_result.read = minimock.Mock(
'urlobj.read', tracker=self.tracker, returns='OMG')
foo.urllib2.urlopen = minimock.Mock(
'urllib2.urlopen', tracker=self.tracker, returns=urlopen_result)
# Now when we call foo.bar(URL) and it invokes
# *urllib2.urlopen(URL).read()*, it will not actually send a request
# to URL, but will instead give us back the dummy response body 'OMG',
# which it then returns.
self.assertEquals(foo.bar('http://example.com/foo'), 'OMG')
# Now we can get trace info from minimock to verify that our mocked
# urllib2 was used as intended. self.tracker has traced our calls to
# *urllib2.urlopen()*
minimock.assert_same_trace(self.tracker, """\
Called urllib2.urlopen('http://example.com/foo)
Called urlobj.read()
Called urlobj.close()""")
单元测试不应该是您编写的唯一类型的测试。如果您计划在任何延长的时间内维护此代码,它们肯定是有用的,IMO非常重要。它们使重构更容易,并有助于捕获回归,但它们并不真正测试各种组件之间的交互以及它们如何交互(如果你做对了)。
当我开始达到我想要发布的具有良好测试覆盖率的大部分成品时,我喜欢编写至少一个在隔离环境中运行完整程序的集成测试。
我在目前的项目中取得了很大的成功。我有大约80%的单元测试覆盖率,其余代码是参数解析,命令调度和顶级应用程序状态,这在单元测试中难以涵盖。这个程序有很多外部依赖关系,可以访问大约12种不同的Web服务,并与生产中的大约6,000台机器进行交互,因此单独运行这种机器有点困难。
我最终编写了一个集成测试,它生成了一个用eventlet和webob编写的WSGI服务器,它模拟了我的程序在生产中与之交互的所有服务。然后集成测试猴子修补我们的web service client library以拦截所有HTTP请求并将它们发送到WSGI应用程序。执行此操作后,它会加载一个状态文件,其中包含集群状态的序列化快照,并通过调用它的main()函数来调用该应用程序。现在我的程序与之交互的所有外部服务都被模拟,这样我就可以运行我的程序,因为它将以可重复的方式在生产中运行。
答案 4 :(得分:0)
要记住关于doctests的重要事项是测试基于字符串比较,数字呈现为字符串的方式在不同平台上甚至在不同的python解释器中都会有所不同。
我的大多数工作都涉及计算,所以我只使用doctests来测试我的示例和我的版本字符串。我在__init__.py中添加了一些,因为它将显示为我的epydoc生成的API文档的首页。
我使用nose进行测试,虽然我对检查py.test的最新更改非常感兴趣。