如果您正在编写库或应用程序,那么单元测试文件在哪里?
将测试文件与主应用程序代码分开很好,但将它们放在应用程序根目录内的“测试”子目录中会很尴尬,因为它会使导入您将要测试的模块变得更加困难。
这里有最佳实践吗?
如果您正在编写库或应用程序,那么单元测试文件在哪里?
将测试文件与主应用程序代码分开很好,但将它们放在应用程序根目录内的“测试”子目录中会很尴尬,因为它会使导入您将要测试的模块变得更加困难。
这里有最佳实践吗?
对于文件module.py
,通常应该调用单元测试test_module.py
,遵循 Pythonic 命名约定。
有几个普遍接受的地方可以放置test_module.py
:
module.py
。../tests/test_module.py
(与代码目录同级)。tests/test_module.py
(代码目录下一级)。我更喜欢#1,因为它可以简单地找到测试并导入它们。无论您使用什么构建系统,都可以轻松配置为运行以test_
. 实际上,用于测试发现的默认unittest
test*.py
模式是.
如果只有 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__.py
intests/
文件夹
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
一种常见的做法是将测试目录放在与模块/包相同的父目录中。因此,如果您的模块名为 foo.py,您的目录布局将如下所示:
parent_dir/
foo.py
tests/
当然,没有一种方法可以做到这一点。您还可以创建一个测试子目录并使用absolute import导入模块。
无论您将测试放在哪里,我都建议您使用鼻子来运行它们。鼻子在您的目录中搜索测试。这样,您就可以在组织上最有意义的地方进行测试。
我们在编写 Pythoscope ( https://pypi.org/project/pythoscope/ )时遇到了同样的问题,它为 Python 程序生成单元测试。在选择目录之前,我们在 python 列表中测试了人们,有很多不同的意见。最后我们选择在与源代码相同的目录下放置一个“tests”目录。在该目录中,我们为父目录中的每个模块生成一个测试文件。
我也倾向于将我的单元测试放在文件本身中,正如上面提到的 Jeremy Cantrell 所说,虽然我倾向于不将测试功能放在主体中,而是将所有内容放在一个
if __name__ == '__main__':
do tests...
堵塞。这最终将文档作为“示例代码”添加到文件中,以了解如何使用您正在测试的 python 文件。
我应该补充一点,我倾向于编写非常紧凑的模块/类。如果您的模块需要大量测试,您可以将它们放在另一个中,但即便如此,我仍然会添加:
if __name__ == '__main__':
import tests.thisModule
tests.thisModule.runtests
这让任何阅读您的源代码的人都知道在哪里寻找测试代码。
每隔一段时间,我就会发现自己检查了测试放置的主题,并且每次大多数人都会在库代码旁边推荐一个单独的文件夹结构,但我发现每次的论点都是相同的,并且没有那么令人信服。我最终把我的测试模块放在核心模块旁边的某个地方。
这样做的主要原因是:重构。
当我移动东西时,我确实希望测试模块与代码一起移动;如果它们在单独的树中,很容易丢失测试。老实说,迟早你会得到一个完全不同的文件夹结构,比如django、flask等等。如果你不在乎,这很好。
你应该问自己的主要问题是:
我在写:
如果一个:
单独的文件夹和维护其结构的额外努力可能更适合。没有人会抱怨您的测试被部署到生产环境中。
但是,当测试与核心文件夹混合时,也很容易将测试排除在分发之外;把它放在 setup.py 中:
find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"])
如果 b:
你可能希望——就像我们每个人一样——编写可重用的库,但大多数时候它们的生命与项目的生命息息相关。能够轻松维护您的项目应该是一个优先事项。
然后,如果您做得很好并且您的模块非常适合另一个项目,那么它可能会被复制(而不是分叉或制作成单独的库)到这个新项目中,并将位于它旁边的测试移动到相同的文件夹结构中与在一个单独的测试文件夹已成为的混乱中钓鱼测试相比,这很容易。(你可能会争辩说它不应该是一团糟,但让我们在这里现实一点)。
所以选择仍然是你的,但我认为通过混合测试你可以获得与使用单独文件夹相同的所有东西,但在保持事物整洁方面的努力更少。
我使用一个tests/
目录,然后使用相对导入来导入主要的应用程序模块。所以在 MyApp/tests/foo.py 中,可能有:
from .. import foo
导入MyApp.foo
模块。
我不相信有一个既定的“最佳实践”。
我将测试放在应用程序代码之外的另一个目录中。然后,在运行所有测试之前,我将主应用程序目录添加到我的测试运行程序脚本(它还执行一些其他操作)中的 sys.path(允许您从任何地方导入模块)。这样,当我发布它时,我永远不必从主代码中删除测试目录,如果数量很少的话,可以节省我的时间和精力。
根据我在 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 打包这些库时,您可以只打包主库模块(仅)。这有助于可维护性,尤其是在敏捷环境中。
我建议你在 GitHub 上查看一些主要的 Python 项目并获得一些想法。
当您的代码变得更大并且您添加更多库时,最好在您拥有 setup.py 的同一目录中创建一个测试文件夹,并为每种测试类型(单元测试、集成等)镜像您的项目目录结构
例如,如果您的目录结构如下:
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
我是怎么做到的...
文件夹结构:
project/
src/
code.py
tests/
setup.py
Setup.py 指向 src/ 作为包含我的项目模块的位置,然后我运行:
setup.py develop
它将我的项目添加到站点包中,指向我的工作副本。要运行我的测试,我使用:
setup.py tests
使用我配置的任何测试运行器。
我更喜欢顶级测试目录。这确实意味着进口变得更加困难。为此,我有两个解决方案:
test_suite='tests.runalltests.suite'
,setup()
并且可以简单地运行测试:python setup.py test
PYTHONPATH=. python tests/runalltests.py
以下是 M2Crypto 中的代码如何支持这些东西:
如果你更喜欢用鼻子测试运行测试,你可能需要做一些不同的事情。
我将测试与被测代码 (CUT) 放在同一目录中。在我可以使用我的插件调整 pytest 的项目中,因为foo.py
我foo.pt
用于测试,这使得编辑特定模块及其测试变得非常容易:vi foo.*
.
在我不能这样做的地方,我使用foo_ut.py
或类似的。你仍然可以使用vi foo*
它,但它也会捕获foobar.py
,foobar_ut.py
如果存在的话。
无论哪种情况,我都会调整测试发现过程以找到这些。
这将测试放在目录列表中的代码旁边,使测试明显存在,并且使打开测试尽可能容易,因为它们位于单独的文件中。(对于从命令行启动的编辑器,如上所述;对于 GUI 系统,单击代码文件和相邻(或非常接近)的测试文件。
正如其他人所指出的那样,这也使得重构和提取代码以在必要时在其他地方使用变得更加容易。
我真的不喜欢将测试放在完全不同的目录树中的想法。为什么让开发人员在使用 CUT 打开文件时打开测试变得比必要的困难?并不是说绝大多数开发人员都热衷于编写或调整测试,以至于他们会忽略这样做的任何障碍,而不是以障碍为借口。(根据我的经验,恰恰相反;即使你让它尽可能简单,我也知道许多开发人员懒得写测试。)
我们用
app/src/code.py
app/testing/code_test.py
app/docs/..
在我们插入的每个测试文件../src/
中sys.path
. 这不是最好的解决方案,但有效。我认为,如果有人想出类似 java 中的 maven 之类的东西,无论您从事什么项目,它都会为您提供可以正常工作的标准约定,那将是很棒的。
在 C# 中,我通常将测试分成单独的程序集。
在 Python 中——到目前为止——我倾向于编写 doctest,其中测试位于函数的 docstring 中,或者将它们放在if __name__ == "__main__"
模块底部的块中。
如果测试很简单,只需将它们放在文档字符串中——大多数 Python 测试框架都可以使用它:
>>> import module
>>> module.method('test')
'testresult'
对于其他更复杂的测试,我会将它们放入../tests/test_module.py
或放入tests/test_module.py
.
在编写一个名为“foo”的包时,我会将单元测试放入一个单独的包“foo_test”中。然后,模块和子包将与 SUT 包模块具有相同的名称。例如,模块 foo.xy 的测试在 foo_test.xy 每个测试包的 __init__.py 文件中包含一个 AllTests 套件,该套件包括该包的所有测试套件。setuptools 提供了一种方便的方式来指定主要的测试包,所以在“python setup.py develop”之后你可以使用“python setup.py test”或者“python setup.py test -s foo_test.x.SomeTestSuite”只是一个特定的套房。
我最近开始用 Python 编程,所以我还没有机会找到最佳实践。但是,我编写了一个模块,可以找到所有测试并运行它们。
所以我有:
应用程序/ 应用文件.py 测试/ 应用文件测试.py
随着我进入更大的项目,我将不得不看看它是如何进行的。