【问题标题】:Unit testing infrastructure for a python modulepython模块的单元测试基础设施
【发布时间】:2010-07-14 02:01:57
【问题描述】:

我正在编写一个 python 模块,我想对其进行单元测试。我是 python 新手,对可用的选项有些迷惑。

目前,我想将我的测试写为doctests,因为我喜欢声明式而不是命令式的风格(但是,如果有误,请随时取消我的这种偏好)。然而,这提出了几个问题:

  1. 我应该把测试放在哪里?在与他们正在测试的代码相同的文件中(或在 doctests 的文档字符串中)?还是认为将它们分开到自己的目录中更好?
  2. 如何从命令行一次性运行整个模块中的所有测试?
  3. 如何报告测试套件的代码覆盖率?
  4. 在 python 中进行单元测试时,我应该注意哪些其他最佳实践?

【问题讨论】:

    标签: python unit-testing code-coverage doctest


    【解决方案1】:

    请随意拒绝我 如果有误会优先考虑

    我相信我使用doctest 比任何其他开源开发人员更广泛(方式 延伸其预期用途边界),至少在单个项目中 - 全部我的gmpy 项目中的测试是文档测试。在gmpy 刚开始的时候它是全新的,这似乎是一个很棒的小技巧,如果某件事值得做,那就值得做多余的事情——对吧?-)

    错了。除了gmpy,将所有内容重做为适当的单元测试将需要太多的返工,我再也没有犯过这个错误:这些天,我使用单元测试作为单元测试,而文档测试只是为了检查我的文档,因为他们'一直是被使用的。 doctest 所做的(将预期结果与实际结果进行比较——仅此而已)并不是构建可靠测试套件的良好基础。从未有过其他打算。

    我建议您查看nose。新的 Python 2.7 中的 unittest 模块更加丰富和美观,如果您卡在 2.4、2.5 或 2.6 上,您仍然可以使用 unittest2 的新功能,您可以下载并安装; nose 很好地补充了 unittest

    如果您无法忍受 unittest(但是 -- 试一试,它会在您身上生长!-),也许可以尝试 py.test,这是一个具有完全不同理念的替代软件包。

    但是,,不要拉伸doctest 来测试文档中示例以外的内容!完全相等的比较经常会妨碍你,因为我不得不在gmpy...

    【讨论】:

      【解决方案2】:

      出于以下原因,我不喜欢 doctest:

      • 您无法运行测试的子集。当测试失败时,只运行一个测试很有用。 Doctest 无法做到这一点。
      • 如果在 doctest 中间发生故障,整个事情就会停止。我宁愿查看所有结果来决定如何解决破损问题。
      • 编码风格是风格化的,并且必须具有可打印的结果。
      • 您的代码是以一种特殊的方式执行的,因此更难推断其将如何执行,更难添加帮助程序,也更难围绕测试进行编程。

      此列表摘自我的博客文章 Things I don't like about doctest,其中还有更多内容,以及一长串 cmets 争论的要点。

      关于覆盖率:我不相信 Python 有一个覆盖率工具可以测量 doctest 中的覆盖率。但由于它们只是一长串没有分支或循环的语句,这有问题吗?

      【讨论】:

      • 对不起,我不是指 of doctests 的覆盖率,更多的是通过运行 doctests 执行的代码的覆盖率。但是在你的建议和 Alex 的建议之间,我完全确信不要使用它们,而是去进行单元测试!
      • 那么一定要接受@bstpiere 关于coverage.py 的建议! :)
      【解决方案3】:

      我怀疑 Alex 在程序员的曲线上可能比我领先一点,但如果你想要有一些 Python 经验的人(作为“用户”而不是专家或传道者)的观点,但不是在同一个联盟中,我对单元测试的发现几乎相同。

      一开始,Doctests 可能听起来很适合进行简单的测试,我在家里进行了一些个人项目,因为它在其他地方得到了推荐。 在工作中,我们使用鼻子(虽然我觉得我们在不久前一直在使用 pyUnit),但几个月前我也搬到了家里。

      最初的设置时间和管理开销,以及与实际代码的分离,一开始似乎没有必要,尤其是当您测试的代码库不是那么大的时候,但从长远来看,我已经发现 doctest 阻碍了我想做的每一次重构或重组,很难维护,几乎不可能扩展,并且很快就抵消了最初的节省。是的,我知道单元测试与集成测试不同,但文档测试往往过于严格地为您定义单元。 如果您确定它是有效的草图工具或开发模型,它们也不太适合基于单元的敏捷。

      您可能需要花一些时间来计划和完善您的单元测试,以 pyUnit 或鼻子引导您的方式,但即使在短期内,您也可能会发现它实际上在许多层面上都对您有所帮助。我知道这对我有用,而且我对这些天正在处理的代码库的复杂性和规模还比较陌生。头几周只需要咬紧牙关。

      【讨论】:

        【解决方案4】:

        有关报道,请查看优秀的coverage.py

        否则,Alex Martelli 所写的一切都非常中肯。

        【讨论】:

          【解决方案5】:

          doctests 非常适合快速、次要的单元测试,这些测试描述了所讨论对象的一些基本用法(因为它们出现在 docstrings 中,因此帮助(无论)等等)。

          我个人发现使用 unittest 模块进行更广泛和更彻底的测试会更有效,现在 2.7 模块(向后移植到 unittest2)具有更方便的断言。您可以使用单元测试框架设置测试套件和任意复杂的场景,并一次性覆盖所有不同的测试(命令行方式)

          coverage.py,由 Ned Batchelder 和 @bstpierre 提到的将与其中任何一个一起使用,我建议您使用它来查看您已经对代码进行了哪些测试,哪些没有。您可以将它添加到 CI 系统(即 Hudson 或您喜欢使用的任何东西)中,以跟上涵盖的内容和未涵盖的内容,并且 HTML 报告非常适合查看测试覆盖范围未命中的内容。 Coverage 支持 Junit xml 输出,许多 CI 系统都知道如何提供图表化的持续结果,让您看到构建随着时间的推移变得更好或更糟。

          【讨论】:

            【解决方案6】:

            我同意上述关于 doctest 不缩放的所有观点,我更喜欢坚持使用 unittest。

            我可以提供的一个提示是从代码处理 __name__ == "__main__ 调用单元测试,因此如果测试文件作为脚本运行,它将运行其测试。

            例如:

            #!/usr/bin/env python
            
            """
            Unit tests for the GetFiles.py utility
            """
            
            import unittest
            from FileUtilities import getTree
            
            class TestFileUtilities(unittest.TestCase):
            
               def testGetTree(self):
                  """
                  Tests that a known tree is found and incidentally confirms
                  that we have the tree we expected to use for our current
                  sample extraction.
                  """
                  found = getTree('./anzmeta-dtd', '.pen')
                  expected_path_tail = ['ISOdia.pen',
                                   'ISOgrk1.pen',
                                   'ISOtech.pen']
                  for i, full_path in enumerate(found):
                     assert full_path.endswith( expected_path_tail[i] ), expected_path_tail[i]
            
            # other tests elided         
            
            if __name__ == "__main__":
               # When this module is executed from the command-line, run all its tests
               unittest.main()
            

            【讨论】:

              猜你喜欢
              • 2020-08-23
              • 1970-01-01
              • 2010-11-01
              • 1970-01-01
              • 2016-04-26
              • 1970-01-01
              • 2023-03-20
              • 1970-01-01
              • 2019-03-10
              相关资源
              最近更新 更多