如何编写现代Python测试?

时间:2010-02-28 20:22:23

标签: python testing

编写Python测试的最新方法是什么?使用什么模块/框架?

还有一个问题:doctest测试仍有任何价值吗?或者是否应该在更现代的测试框架中编写所有测试?

谢谢,Boda Cydo。

5 个答案:

答案 0 :(得分:12)

通常的方法是使用内置unittest模块来创建单元测试并将它们捆绑在一起,以测试可以独立运行的套件。 unittest与jUnit非常相似(并受其启发),因此非常易于使用。

如果您对最新的变化感兴趣,请查看Michael Foord的新PyCon演讲:

PyCon 2010: New and Improved: Coming changes to unittest

答案 1 :(得分:9)

使用内置的unittest模块与以往一样简单易用。其他单元测试选项py.testnosetwisted.trial大多与unittest兼容。

Doctests与他们一直有着相同的价值 - 他们非常适合测试您的文档,而不是您的代码。如果您要在文档字符串中放置代码示例,doctest可以确保您保持正确和最新。没有什么比尝试重现一个例子和失败更糟糕了,只是为了后来才意识到它实际上是文档的错误。

答案 2 :(得分:5)

我对doctests知之甚少,但在我的大学,我们会教授和鼓励鼻子测试。

可以按照以下步骤安装鼻子(我假设您使用的是PC - Windows操作系统):

  1. 安装setuptools
  2. 运行DOS命令提示符(开始 - >所有程序 - >附件 - >命令提示符)
  3. 要使此步骤生效,您必须连接到互联网。在DOS中,键入:C:\ Python25 \ Scripts \ easy_install nose
  4. 如果您使用的是其他操作系统,请检查this site

    修改

    我写这篇文章已经两年了。现在,我已经了解了这个名为Designing by Contract的编程原理。这允许程序员为其代码中的所有函数定义前置条件,后置条件和不变量(称为契约)。结果是,如果违反任何这些合同,就会引发错误。

    我建议用于python的DbC框架称为PyContract我已成功在evolutionary programming framework

    中使用它

答案 3 :(得分:2)

在我目前的项目中,我正在使用unittestminimocknose。在过去,我已经大量使用了doctests,但在大型项目中,某些测试可能会变得有点笨拙,所以我倾向于保留使用doctests来实现更简单的功能。

如果您正在使用setuptoolsdistribute(您应该切换到分发),您可以将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台机器进行交互,因此单独运行这种机器有点困难。

我最终编写了一个集成测试,它生成了一个用eventletwebob编写的WSGI服务器,它模拟了我的程序在生产中与之交互的所有服务。然后集成测试猴子修补我们的web service client library以拦截所有HTTP请求并将它们发送到WSGI应用程序。执行此操作后,它会加载一个状态文件,其中包含集群状态的序列化快照,并通过调用它的main()函数来调用该应用程序。现在我的程序与之交互的所有外部服务都被模拟,这样我就可以运行我的程序,因为它将以可重复的方式在生产中运行。

答案 4 :(得分:0)

要记住关于doctests的重要事项是测试基于字符串比较,数字呈现为字符串的方式在不同平台上甚至在不同的python解释器中都会有所不同。

我的大多数工作都涉及计算,所以我只使用doctests来测试我的示例和我的版本字符串。我在__init__.py中添加了一些,因为它将显示为我的epydoc生成的API文档的首页。

我使用nose进行测试,虽然我对检查py.test的最新更改非常感兴趣。