Python单元测试去哪儿了?


490

如果您正在编写库或应用程序,则单元测试文件会放在哪里?

将测试文件与主应用程序代码分开是很好的选择,但是将它们放在应用程序根目录内的“ tests”子目录中是很尴尬的,因为这使得导入要测试的模块更加困难。

这里有最佳实践吗?


4
这个问题有很多可接受的答案,这使得它对于Q / A网站而言不是问题。人们需要了解,并不是所有关于SO的有用的大问题都可以提出来。前者为此选择,我们必须遵守他们的规则。
Wouter J

121
人们还需要了解,当SO页面最终成为Google搜索的热门话题时,最好顺其自然,而不是坚持SO官方原则。
Christopher Barber

6
@ChristopherBarber人们还需要理解,决策和积极的关闭才是最终赢得如此可怕声誉的网站,除非我真的没有更好的选择,否则我不愿提出问题。
Stefano Borini,

Answers:


200

对于文件module.py,通常应test_module.py遵循Pythonic命名约定来调用单元测试。

有几个公认的地方test_module.py

  1. 与相同的目录中module.py
  2. 进入../tests/test_module.py(与代码目录处于同一级别)。
  3. tests/test_module.py(代码目录下的一级)。

我更喜欢#1,因为它可以轻松找到测试并将其导入。无论您使用哪种构建系统,都可以轻松地将其配置为运行以开头的文件test_。实际上,用于测试发现默认unittest模式是test*.py


12
默认情况下,load_tests协议搜索名为test * .py的文件。另外,此Google排名靠前的结果该unittest文档均使用test_module.py格式。
dfarrell07

6
使用Tab_completion时,使用foo_test.py可以节省一两次击键,因为您没有一堆以'test_'开头的文件。
杜松

11
@juniper,按照您的想法,在编码时,module_test将显示在自动完成中。这可能令人困惑或烦恼。
Medeiros

11
部署代码时,我们不想将测试部署到我们的生产位置。因此,在进行部署时,很容易将它们放在一个目录“ ./tests/test_blah.py”中。此外,有些测试会提取示例数据文件,将这些文件保存在测试目录中至关重要,以免我们部署测试数据。
凯文·赖斯

1
@ KevinJ.Rice您是否应该测试该代码在您的生产位置上是否可用?
endlith

67

仅1个测试文件

如果只有1个测试文件,建议将其放在顶层目录中:

module/
    lib/
        __init__.py
        module.py
    test.py

在CLI中运行测试

python test.py

许多测试文件

如果有许多测试文件,请将其放在tests文件夹中:

module/
    lib/
        __init__.py
        module.py
    tests/
        test_module.py
        test_module_function.py
# test_module.py

import unittest
from lib import module

class TestModule(unittest.TestCase):
    def test_module(self):
        pass

if __name__ == '__main__':
    unittest.main()

在CLI中运行测试

# In top-level /module/ folder
python -m tests.test_module
python -m tests.test_module_function

采用 unittest discovery

unittest discovery 将在包文件夹中找到所有测试。

创建一个__init__.pyin tests/文件夹

module/
    lib/
        __init__.py
        module.py
    tests/
        __init__.py
        test_module.py
        test_module_function.py

在CLI中运行测试

# In top-level /module/ folder

# -s, --start-directory (default current directory)
# -p, --pattern (default test*.py)

python -m unittest discover

参考

单元测试框架


51

通常的做法是将tests目录放置在与模块/软件包相同的父目录中。因此,如果您的模块名为foo.py,则目录布局将如下所示:

parent_dir/
  foo.py
  tests/

当然,没有一种方法可以做到这一点。您也可以创建一个tests子目录,然后使用绝对导入导入模块。

无论您在哪里进行测试,我都建议您使用鼻子进行测试。鼻子会在您的目录中搜索测试。这样,您可以在组织上最有意义的地方进行测试。


16
我想这样做,但是我无法使它起作用。要运行测试,我在parent_dir中,键入:“ python tests \ foo_test.py”,在foo_test.py中:“ from ..foo import this,that,other”,失败:“ ValueError:Attempted “相对于非软件包的相对导入”。parent_dir和test都包含init .py,因此我不确定为什么它们不是软件包。我怀疑这是因为即使在init .py 目录中,也无法将您从命令行运行的顶级脚本视为软件包的一部分。那么如何运行测试?我今天正在Windows上工作,请保佑我的棉袜。
乔纳森·哈特利

4
我发现,运行单元测试的最佳方法是安装您的库/程序,然后用鼻子运行单元测试。我建议使用virtualenv和virtualenvwrapper来简化此过程。
克里斯蒂安(Cristian)

@Tartley-您需要在“ tests”目录中的init .py文件才能使绝对解决方案导入生效。我有这种方法可以用鼻子,所以我不确定为什么会遇到麻烦。
cmcginty

4
感谢Casey-但我在所有相关目录中都有一个init .py文件。我不知道我做错了什么,但是我所有的Python项目都遇到了这个问题,我不明白为什么没有其他人这样做。哦,亲爱的
乔纳森·哈特利

8
针对我的问题的一种解决方案,使用Python2.6或更高版本,我们使用以下命令从项目的根目录运行测试:python -m project.package.tests.module_tests(而不是python project / package / tests / module_tests.py) 。这会将测试模块的目录放在路径上,因此测试可以相对地导入其父目录以获取被测模块。
乔纳森·哈特利

32

编写Pythoscope(https://pypi.org/project/pythoscope/)时,我们遇到了同样的问题,该问题会为Python程序生成单元测试。在选择目录之前,我们对python列表中的测试人员进行了调查,结果有很多不同的见解。最后,我们选择将“ tests”目录放置在与源代码相同的目录中。在该目录中,我们为父目录中的每个模块生成一个测试文件。


2
太棒了!只需在一些旧代码上运行Pythoscope(伟大的徽标),那就太神奇了。即时最佳实践,您可以让其他开发人员填写现在失败的测试存根。现在要把它挂在竹子上吗?:)
2012年

27

正如杰里米·坎特雷尔(Jeremy Cantrell)所述,我也倾向于将单元测试放在文件本身中,尽管我倾向于不将测试功能放在主体中,而是将所有内容放在一个文件中。

if __name__ == '__main__':
   do tests...

块。最后,将文档添加到文件中作为“示例代码”,以说明如何使用要测试的python文件。

我应该补充一点,我倾向于编写非常紧凑的模块/类。如果您的模块需要大量测试,则可以将它们放在另一个测试中,但是即使如此,我仍然要添加:

if __name__ == '__main__':
   import tests.thisModule
   tests.thisModule.runtests

这使任何阅读您的源代码的人都知道在哪里可以找到测试代码。


“作为杰里米·坎特雷尔的笔记”在哪里?
endlith 2014年

我喜欢这种工作方式。可以将最简单的编辑器配置为使用热键运行文件,因此,在查看代码时,可以立即运行测试。不幸的是,在Python以外的语言中,这看起来很可怕,因此,如果您在C ++或Java商店中,您的同事可能会对此感到不满意。它也不适用于代码覆盖工具。
Keeely

19

我偶尔会检查一下测试放置的主题,大多数人每次都在库代码旁边推荐一个单独的文件夹结构,但是我发现每次参数都相同且并不那么令人信服。我最终将测试模块放在核心模块旁边。

这样做的主要原因是:重构

当我四处移动时,我确实希望测试模块随代码一起移动。如果测试位于单独的树中,则很容易丢失测试。老实说,迟早您会得到一个完全不同的文件夹结构,例如djangoflask和许多其他文件夹。如果您不在乎,那很好。

您应该问自己的主要问题是:

我在写:

  • a)可重用的库或
  • b)构建项目而不是将一些半分隔的模块捆绑在一起?

如果一个:

一个单独的文件夹以及保持其结构的额外工作可能会更适合。没有人会抱怨您的测试被部署到生产环境中

但是,将测试与核心文件夹混合时,也可以将测试从分发中排除出去,这同样容易。把它放在setup.py中

find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"]) 

如果b:

就像我们每个人一样,您可能希望您正在编写可重用的库,但是大多数时候它们的生命与项目的生命息息相关。轻松维护项目的能力应该是首要任务。

然后,如果您做得很好,并且您的模块非常适合另一个项目,则可能会将其复制(而不是分叉或制作成单独的库)复制到此新项目中,并将位于其旁边的测试移动到同一文件夹结构中与在一个单独的测试文件夹变得混乱的情况下进行测试相比,这很容易。(您可能会争辩说,一开始它不应该是一团糟,但让我们在这里变得现实)。

因此,选择仍然是您的选择,但我认为,通过混合测试,您可以实现与使用单独的文件夹相同的所有功能,但是可以使工作保持整洁。


1
实际上,将测试部署到生产中有什么问题?以我的经验,它非常有用:允许用户在其环境中实际运行测试...当收到错误报告时,您可以要求用户运行测试套件以确保一切正常,并为测试发送热补丁套件直接...此外,这不是因为您将测试放入module.tests最终在生产中使用,除非您在设置文件中做错了什么……
anarcat

2
如我在回答中所写,我通常不分开测试。但。将测试放入分发包中可能会导致执行通常在生产环境中不想要的内容(例如,某些模块级别的代码)。当然,这取决于测试的编写方式,但是为了安全起见,请不要进行测试,没有人会误操作某些有害的东西。我不反对在发行版中包含测试,但据我所知,这样做更安全。并将它们放在单独的文件夹中非常容易-无需将它们包含在dist中。
Janusz Skonieczny

15

我使用tests/目录,然后使用相对导入来导入主要应用程序模块。因此,在MyApp / tests / foo.py中,可能有:

from .. import foo

导入MyApp.foo模块。


5
“ ValueError:尝试在非软件包中进行相对导入”
cprn

12

我认为没有公认的“最佳实践”。

我将测试放在应用程序代码之外的另一个目录中。然后,在运行所有测试之前,在测试运行器脚本(还执行其他一些操作)中,将主应用程序目录添加到sys.path中(允许您从任何位置导入模块)。这样,我发布时就不必从主代码中删除测试目录,从而节省了时间和精力。


3
然后,我将主应用程序目录添加到sys.path中。问题是,如果您的测试包含一些帮助程序模块(例如测试Web服务器),并且您需要从适当的测试中导入此类帮助程序模块。
Piotr Dobrogost,2011年

这对我来说是这样的:os.sys.path.append(os.dirname('..'))
Matt M.

11

根据我在Python中开发测试框架的经验,我建议将python单元测试放在单独的目录中。保持对称目录结构。这将有助于仅打包核心库而不打包单元测试。下面是通过示意图实现的。

                              <Main Package>
                               /          \
                              /            \
                            lib           tests
                            /                \
             [module1.py, module2.py,  [ut_module1.py, ut_module2.py,
              module3.py  module4.py,   ut_module3.py, ut_module.py]
              __init__.py]

这样,当您使用rpm打包这些库时,您可以仅打包主库模块(仅)。这有助于维护性,尤其是在敏捷环境中。


1
我已经意识到这种方法的潜在优势是可以将测试作为自己的独立应用程序进行开发(甚至可以进行版本控制)。当然,对每个优点都有一个缺点-保持对称性基本上是在进行“两次输入记帐”,并使重构变得更加繁琐。
Jasha

软性后续问题:为什么敏捷环境特别适合这种方法?(即,敏捷工作流程如何使对称目录结构如此有益?)
Jasha,

11

我建议您检查GitHub上的一些主要Python项目并获得一些想法。

当代码变大并添加更多库时,最好在具有setup.py的目录中创建一个测试文件夹,并为每种测试类型(unittest,integration等)镜像项目目录结构。

例如,如果您具有如下目录结构:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
setup.py

添加测试文件夹后,您将具有以下目录结构:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
test/
   unit/
      myapp/
         moduleA/
            module_A_test.py
         moduleB/
            module_B_test.py
   integration/
          myapp/
             moduleA/
                module_A_test.py
             moduleB/
                module_B_test.py
setup.py

许多正确编写的Python软件包都使用相同的结构。Boto软件包就是一个很好的例子。检查https://github.com/boto/boto


1
建议使用“测试”而不是“测试”,因为“测试”是Python内置模块。docs.python.org/2/library/test.html
brodul

并非总是..例如matplotlibmatplotlib/lib/matplotlib/testsgithub.com/matplotlib/matplotlib/tree/…sklearn下有它,在scikitelearn/sklearn/testsgithub.com/scikit-learn/scikit-learn/tree/master/sklearn/tests)下有它
alpha_989

7

我该怎么做...

资料夹结构:

project/
    src/
        code.py
    tests/
    setup.py

Setup.py指向src /作为包含我的项目模块的位置,然后运行:

setup.py develop

它将我的项目添加到站点程序包中,指向我的工作副本。要运行测试,我使用:

setup.py tests

使用我配置的任何测试运行程序。


您似乎已经超越了术语“模块”。大多数Python程序员可能会认为模块是您调用的文件code.py。将顶层目录称为“项目”会更有意义。
blokeley 2011年

4

我更喜欢顶级测试目录。这确实意味着进口变得更加困难。为此,我有两个解决方案:

  1. 使用setuptools。然后,您可以test_suite='tests.runalltests.suite'进入setup(),并可以简单地运行测试:python setup.py test
  2. 运行测试时设置PYTHONPATH: PYTHONPATH=. python tests/runalltests.py

M2Crypto中的代码如何支持这些东西:

如果您希望通过鼻子测试运行测试,则可能需要做一些不同的事情。


3

我们用

app/src/code.py
app/testing/code_test.py 
app/docs/..

在每个测试文件,我们插入../src/sys.path。这不是最好的解决方案,但可以。我认为,如果有人想出了java中的maven之类的东西,无论您从事什么项目,它都会为您提供可以正常工作的标准约定,那就太好了。


1

如果测试很简单,只需将它们放在docstring中-大多数适用于Python的测试框架都可以使用:

>>> import module
>>> module.method('test')
'testresult'

对于其他涉及更多的测试,我会将它们放在../tests/test_module.py或中tests/test_module.py


1

在C#中,我通常将测试分为一个单独的程序集。

到目前为止,在Python中,我倾向于编写doctest,该测试位于函数的docstring中,或者将它们放在if __name__ == "__main__"模块底部的块中。


0

在编写名为“ foo”的程序包时,我会将单元测试放入单独的程序包“ foo_test”中。这样,模块和子软件包将与SUT软件包模块具有相同的名称。例如,在foo_test.xy中找到模块foo.xy的测试。然后,每个测试包的__init__.py文件都包含一个AllTests套件,其中包括该包的所有测试套件。setuptools提供了一种方便的方法来指定主要的测试包,以便在“ python setup.py development”之后,您可以仅对“ python setup.py test”或“ python setup.py test -s foo_test.x.SomeTestSuite”使用只是一个特定的套件。


0

我将测试与被测代码(CUT)放在同一目录中。用于foo.py测试将在foo_ut.py或相似。(我调整了测试发现过程以找到这些。)

这会将测试放在目录列表中的代码旁边,从而使测试很明显,并且使测试在单独文件中时的打开变得尽可能容易。(对于命令行编辑器,vim foo*以及在使用图形文件系统浏览器时,只需单击CUT文件,然后单击紧邻的测试文件。)

正如其他人指出的那样,如果需要的话,这也使得重构和提取代码以在其他地方使用变得更加容易。

我真的不喜欢将测试放在完全不同的目录树中的想法;为什么在使用CUT打开文件时,使开发人员更难以打开测试?并不是说绝大多数开发人员都热衷于编写或调整测试,以至于他们会忽略这样做的任何障碍,而不是以障碍为借口。(根据我的经验,情况恰恰相反;即使您使它尽可能地容易,我也知道许多开发人员不会为编写测试而烦恼。)


-2

我最近开始用Python编程,所以我还没有真正找到最佳实践的机会。但是,我编写了一个模块,可以查找并运行所有测试。

所以我有:

应用/
 appfile.py
测试/
 appfileTest.py

我必须查看进展到更大项目时的情况。


4
嗨,camelCase在python世界中有点奇怪。
斯图尔特·阿克森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.