Python单元测试在哪里?

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

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

这里是否存在最佳实践?

118409 次浏览

我不相信存在既定的“最佳实践”。

我把我的测试放在应用程序代码之外的另一个目录中。然后,我将主应用程序目录添加到sys。路径(允许您从任何地方导入模块)在我的测试运行脚本(它也做一些其他的事情)之前运行所有的测试。这样,当我发布主代码时,我就不必从主代码中删除测试目录,节省了我的时间和精力,即使时间和精力非常少。

一种常见的做法是将测试目录放在与模块/包相同的父目录中。因此,如果你的模块名为foo.py,你的目录布局将如下所示:

parent_dir/
foo.py
tests/

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

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

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

所以,我有:

app/
appfile.py
test/
appfileTest.py

当我进入更大的项目时,我得看看情况如何。

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

from .. import foo

导入MyApp.foo模块。

在c#中,我通常将测试分离到单独的程序集中。

在Python中——到目前为止——我倾向于编写doctests,其中测试位于函数的文档字符串中,或者将它们放在模块底部的if __name__ == "__main__"块中。

对于module.py文件,单元测试通常应该称为test_module.py,遵循python命名约定。

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

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

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

当编写一个名为“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”到一个特定的套件。

如果测试很简单,就把它们放在文档字符串中——大多数Python测试框架都可以使用它:

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

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

我也倾向于把我的单元测试放在文件本身中,正如Jeremy Cantrell上面所指出的,尽管我倾向于不把测试函数放在主体中,而是把所有东西都放在一个文件中

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

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

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

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

这让任何阅读源代码的人都知道到哪里去寻找测试代码。

我们使用

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

在每个测试文件中,我们在sys.path中插入../src/。这不是最好的解决办法,但很有效。我认为如果有人提出类似java中的maven之类的东西,给你提供标准的约定,无论你在做什么项目,都可以工作,那就太好了。

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

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

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

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

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

我是怎么做的…

文件夹结构:

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

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

setup.py develop

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

setup.py tests

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

根据我用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个测试文件,建议将其放在顶级目录中:

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

使用# EYZ1

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

tests/文件夹中创建__init__.py

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

参考

单元测试框架

  • nose
  • < a href = " https://docs.nose2。io / en /最近/ noreferrer“rel = > nose2 < / >
  • pytest

我建议你在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包就是一个很好的例子。 检查# EYZ0 < / p >

每隔一段时间,我发现自己检查了测试放置的主题,每次大多数人都建议在库代码旁边使用单独的文件夹结构,但我发现每次的论点都是一样的,而且没有那么令人信服。我最终把我的测试模块放在核心模块旁边的某个地方。

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

当我移动东西时,我确实希望测试模块与代码一起移动;如果测试位于单独的树中,则很容易丢失测试。老实说,迟早你会得到一个完全不同的文件夹结构,像django和许多其他的。如果你不在乎也没关系。

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

我在写:

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

如果一个:

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

但是,当测试与核心文件夹混合在一起时,也很容易将它们排除在分发之外;# EYZ0:

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

如果b:

您可能希望—正如我们每个人都希望—您正在编写可重用的库,但是大多数时候它们的生命与项目的生命紧密相连。能够轻松地维护您的项目应该是优先考虑的。

然后,如果你做得很好,你的模块很适合另一个项目,它可能会被复制到这个新项目中,而不是被分叉或被做成一个单独的库,并且与在一个单独的测试文件夹中搜索测试相比,在相同的文件夹结构中移动放在它旁边的测试很容易。(你可能会说,它一开始就不应该一团糟,但让我们现实一点)。

所以选择仍然是你的,但我认为混合测试可以实现与单独文件夹相同的功能,但在保持文件整洁方面花费的精力更少。

我将测试放在与测试代码(CUT)相同的目录中。在项目中,我可以用我的插件调整pytest,对于foo.py,我使用foo.pt进行测试,这使得编辑一个特定的模块及其测试非常容易:vi foo.*

在不能这样做的地方,我使用foo_ut.py或类似的方法。你仍然可以使用vi foo*,尽管这也将捕获foobar.pyfoobar_ut.py,如果它们存在的话。

在这两种情况下,我调整测试发现过程来找到这些。

这将测试放在目录列表中代码的旁边,使测试明显地存在于那里,并使打开测试尽可能容易,当它们位于单独的文件中时。(对于从命令行开始的编辑器,如上所述;对于GUI系统,单击代码文件和相邻的(或非常接近相邻的)测试文件。

作为其他人则指出,这也使它更容易重构和提取代码,以便在必要时在其他地方使用。

我真的不喜欢把测试放在一个完全不同的目录树中;当开发人员使用CUT打开文件时,为什么要让他们更难打开测试呢?并不是绝大多数开发人员如此热衷于编写或调整测试,以至于他们会忽略任何障碍,而不是将障碍作为借口。(根据我的经验,情况恰恰相反;即使你让测试变得尽可能简单,我知道很多开发人员都懒得写测试。)