Python单元测试在哪里?

时间:2008-09-14 05:41:12

标签: python unit-testing code-organization

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

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

这里有最好的做法吗?

18 个答案:

答案 0 :(得分:183)

对于文件module.py,通常应按照Pythonic命名约定将单元测试称为test_module.py

有几个普遍接受的地方test_module.py

  1. module.py相同的目录。
  2. ../tests/test_module.py中(与代码目录处于同一级别)。
  3. tests/test_module.py中(代码目录下的一个级别)。
  4. 我更喜欢#1,因为它很容易找到测试并导入它们。无论您使用何种构建系统,都可以轻松配置为以test_开头的文件运行。实际上,default unittest pattern used for test discovery is test*.py

答案 1 :(得分:50)

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

parent_dir/
  foo.py
  tests/

当然没有一种方法可以做到这一点。您还可以创建一个tests子目录并使用absolute import导入模块。

无论您在哪里进行测试,我都建议您使用nose来运行它们。 Nose在您的目录中搜索测试。这样,您可以将测试放在组织最有意义的任何地方。

答案 2 :(得分:50)

仅1个测试文件

如果没有很多测试文件,将它放在顶级目录中是很好的(我认为这是一种pythonic(推荐)方式):

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

许多测试文件

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

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

但如果您将测试放在tests文件夹中,则在CLI中测试不能import ..lib,因为__main__无法导入相关模块,因此我们可以使用{{3}或者我们可以将父目录添加到python导入路径,为此我将创建一个

env.py

import sys
import os

# append module root directory to sys.path
sys.path.append(
    os.path.dirname(
        os.path.dirname(
            os.path.abspath(__file__)
        )
    )
)

module/
  tests/
    test_module.py
    env.py
测试导入模块

之前的

import env

test_module.py

import unittest
# append parent directory to import path
import env
# now we can import the lib module
from lib import module

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

答案 3 :(得分:32)

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

答案 4 :(得分:27)

我也倾向于将我的单元测试放在文件本身中,就像Jeremy Cantrell上面说的那样,虽然我倾向于不将测试功能放在主体中,而是将所有内容放在一个

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

块。这最终会将文档添加到文件中作为“示例代码”,以了解如何使用您正在测试的python文件。

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

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

这使得任何阅读源代码的人都知道在哪里查找测试代码。

答案 5 :(得分:16)

每隔一段时间我就会发现自己检查了测试放置的主题,每次大多数建议在库代码旁边都有一个单独的文件夹结构,但我发现每次参数都是相同的并且不那么令人信服。我最终将我的测试模块放在核心模块旁边。

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

当我移动东西时,我确实希望测试模块与代码一起移动;如果它们在一个单独的树中,它很容易丢失测试。说实话,迟早你会得到一个完全不同的文件夹结构,例如djangoflask和其他许多文件夹结构。如果你不在乎,这很好。

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

我写的是:

  • a)可重复使用的库或
  • b)建立一个项目,而不是将一些半分离的模块捆绑在一起?

如果是:

单独的文件夹和维护其结构的额外努力可能更适合。没有人会抱怨你的部署到生产的测试。

但是当它们与核心文件夹混合时,排除测试的同样容易; put this in the setup.py

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

如果b:

您可能希望 - 正如我们每个人所做的那样 - 您正在编写可重复使用的库,但大多数时候他们的生活与项目的生命息息相关。能够轻松维护您的项目应该是一个优先事项。

然后,如果你做得很好并且你的模块非常适合另一个项目,它可能会被复制 - 而不是分叉或制作成一个单独的库 - 进入这个新项目,并将它旁边的测试移到与在单独的测试文件夹变成的混乱中进行钓鱼测试相比,相同的文件夹结构很容易。 (你可能会说,首先它不应该是一团糟,但在这里让它变得现实。)

所以选择仍然是你的,但我认为,通过混合测试,你可以实现与单独文件夹相同的所有功能,但不会花太多精力来保持整洁。

答案 6 :(得分:14)

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

from .. import foo

导入MyApp.foo模块。

答案 7 :(得分:12)

我不相信有一个既定的“最佳实践”。

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

答案 8 :(得分:10)

根据我在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打包这些库时,您只需打包主库模块(仅限)。这有助于维护,特别是在敏捷环境中。

答案 9 :(得分:9)

我建议您在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

答案 10 :(得分:6)

我是怎么做的......

文件夹结构:

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

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

setup.py develop

将我的项目添加到site-packages中,指向我的工作副本。要运行我的测试,我使用:

setup.py tests

使用我配置的测试运行器。

答案 11 :(得分:4)

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

  1. 使用setuptools。然后,您可以将test_suite='tests.runalltests.suite'传递到setup(),并且可以简单地运行测试:python setup.py test
  2. 运行测试时设置PYTHONPATH:PYTHONPATH=. python tests/runalltests.py
  3. 以下是M2Crypto中代码支持的内容:

    如果您更喜欢使用nosetests运行测试,则可能需要做一些不同的事情。

答案 12 :(得分:1)

我们使用

应用程序/ SRC / code.py

app / testing / code_test.py

应用程序/文档/..

在每个测试文件中,我们在sys.path中插入“../src/”。这不是最好的解决方案,但有效。我认为如果有人在java中提供像maven这样的东西会给你标准的约定,无论你在哪个项目上工作都会很棒。

答案 13 :(得分:1)

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

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

对于其他更复杂的测试,我会将它们放在../tests/test_module.pytests/test_module.py中。

答案 14 :(得分:1)

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

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

答案 15 :(得分:0)

当编写一个名为“foo”的包时,我会将单元测试放入一个单独的包“foo_test”中。然后,模块和子包将与SUT包模块具有相同的名称。例如。模块foo.x.y的测试可以在foo_test.x.y中找到。然后,每个测试包的__init__.py文件包含一个AllTests套件,其中包含该软件包的所有测试套件。 setuptools提供了一种指定主测试包的便捷方式,因此在“python setup.py develop”之后你可以使用“python setup.py test”或“python setup.py test -s foo_test.x.SomeTestSuite”来只是一个特定的套房。

答案 16 :(得分:0)

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

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

others have pointed out一样,如果需要的话,这也使得重构和提取代码供其他地方使用更加容易。

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

答案 17 :(得分:-2)

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

所以,我有:

app/
 appfile.py
test/
 appfileTest.py

随着我进入大型项目,我将不得不看看它是如何发展的。