将测试文件与主要应用程序代码分离是很好的做法,但将它们放在应用程序根目录下的“tests”子目录中会使导入要测试的模块更加困难。
这里是否存在最佳实践?
对于一个名为module.py
的文件,单元测试通常应该命名为test_module.py
,遵循Pythonic的命名惯例。
有几个通常接受的位置可以放置test_module.py
:
module.py
在同一目录中。../tests/test_module.py
(与代码目录相同级别)中。tests/test_module.py
(代码目录下一级)中。我更喜欢第一种方法,因为它能够简单地找到测试并导入它们。无论你使用哪种构建系统,都可以轻松配置以运行以test_
开头的文件。实际上,默认的unittest用于测试发现的模式是test*.py
。
如果只有1个测试文件,建议将其放在顶层目录中:
module/
lib/
__init__.py
module.py
test.py
在命令行界面下运行测试
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
可以找到包文件夹中的所有测试。
在 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
from lib import module
时,我会收到一个错误:ImportError: No module named 'lib'
。 - Shervin Radmodule
文件夹中运行,而不是 lib
或 tests
文件夹,Python 使用相对路径进行导入。 - Steely Wingnose
和nose2
处于维护模式。来自nose
网站的消息:在过去的几年中,nose
一直处于维护模式,并且如果没有新的个人/团队接管维护工作,它很可能会停止更新。新项目应该考虑使用Nose2
、py.test
或普通的unittest/unittest2
。 - alpha_989我偶尔会查阅关于测试位置的话题,大多数建议在库代码旁边使用单独的文件夹结构,但每次争论都是相同的,不太有说服力。因此,我最终将我的测试模块放在核心模块旁边。
这样做的主要原因是:重构。
当我移动文件时,测试模块也随着核心代码一起移动;如果它们在一个单独的树中,很容易丢失测试。坦白说,迟早会得到完全不同的文件夹结构,例如django、flask和许多其他项目。如果你不介意,这也没问题。
你应该问自己的主要问题是:
我是在编写:
如果是a:
一个单独的文件夹和额外的维护结构可能更适合。没有人会抱怨你的测试被部署到生产环境中。
但是,如果测试与核心文件夹混合在一起,则将测试排除在分发之外也同样容易;在setup.py中添加此代码即可:
find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"])
如果 b:
你可能希望像我们每个人一样编写可重用的库,但大多数情况下它们的寿命与项目的寿命绑定在一起。轻松维护您的项目应该是首要考虑的事情。
然后,如果你做得很好,你的模块适合另一个项目,它可能会被复制——而不是分叉或制作成单独的库——进入这个新项目中,并移动与其相邻的测试文件到同一文件夹结构中比在杂乱无章的测试文件夹中查找测试文件容易得多。(你可能会说,它一开始就不应该是一团糟,但让我们现实点吧)。
所以选择仍然取决于您,但我会认为,通过混合测试,您可以像使用单独文件夹时一样完成所有相同的工作,但减少了保持整洁的努力。
我也倾向于像Jeremy Cantrell所说的那样将我的单元测试放在文件本身中,尽管我倾向于不将测试函数放在主体中,而是将所有内容都放在一个
if __name__ == '__main__':
do tests...
块。这最终会将文档添加到文件中作为“示例代码”,以说明如何使用正在测试的Python文件。
我应该补充说,我倾向于编写非常紧凑的模块/类。如果您的模块需要大量的测试,您可以将它们放在另一个模块中,但即使如此,我仍然会添加:
if __name__ == '__main__':
import tests.thisModule
tests.thisModule.runtests
这样做可以让任何阅读您源代码的人知道在哪里查找测试代码。
我使用一个tests/
目录,并使用相对导入来导入主应用程序模块。因此,在MyApp/tests/foo.py中,可能会有:
from .. import foo
导入 MyApp.foo
模块。
我建议你在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
matplotlib
将其放在 matplotlib/lib/matplotlib/tests
下 (https://github.com/matplotlib/matplotlib/tree/38be7aeaaac3691560aeadafe46722dda427ef47/lib/matplotlib/tests) ,sklearn
将其放在 scikitelearn/sklearn/tests
下 (https://github.com/scikit-learn/scikit-learn/tree/master/sklearn/tests) - alpha_989根据我在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打包这些库时,你只需要打包主要的库模块(仅此而已)。这有助于在敏捷环境下保持可维护性。
我不认为有已经确立的“最佳实践”。
我把我的测试放在应用代码之外的另一个目录中。然后,在运行所有测试之前,我会在我的测试运行脚本中将主应用程序目录添加到sys.path中(允许您从任何地方导入模块)。这样,当我发布代码时,我永远不必将测试目录从主代码中删除,节省了时间和精力,即使只是微小的一点。
os.sys.path.append(os.dirname('..'))
- Mattwmaster58