Pythonunit testing去了哪里?
如果你正在编写一个库或一个应用程序,unit testing文件在哪里?
将testing文件与主应用程序代码分开是很好的做法,但将它们放到应用程序根目录内的“tests”子目录中是很尴尬的,因为这会导致您将要testing的模块更难导入。
这里有最佳做法吗?
对于一个文件module.py
,unit testing通常被称为test_module.py
,遵循Pythonic命名约定。
有几个普遍接受的地方把test_module.py
:
- 在与
module.py
相同的目录中。 - 在
../tests/test_module.py
(与代码目录在同一级别)。 - 在
tests/test_module.py
(代码目录下的一个级别)。
我更喜欢#1,因为它简单的findtesting并导入它们。 无论您使用的是哪种构build系统,都可以轻松configuration为以test_
开始运行文件。 实际上, 用于testing发现的默认unit testing模式是test*.py
。
通常的做法是将testing目录放在与您的模块/软件包相同的父目录中。 所以如果你的模块叫做foo.py,你的目录布局看起来像这样:
parent_dir/ foo.py tests/
当然没有办法做到这一点。 您也可以创build一个testing子目录并使用绝对导入导入模块。
无论你把testing放在哪里,我都会build议你用鼻子来运行它们。 鼻子search您的目录进行testing。 这样,你可以把testing放在任何组织最有意义的地方。
只有1个testing文件
如果没有很多testing文件,把它放在顶层目录是好的(我认为这是pythonic(推荐)):
module/ lib/ __init__.py module.py test.py
许多testing文件
如果有很多testing文件,把它放在tests
文件夹中:
module/ lib/ __init__.py module.py tests/ test_module.py test_module2.py
但是如果你把testing放到tests
文件夹中,testing不能在CLI中import ..lib
,因为__main__
不能导入相关模块,所以我们可以使用nose ,或者我们可以添加一个父目录到Python导入path,为此我将创build一个
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
并在testing导入模块之前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()
编写Pythoscope( http://pythoscope.org )时,我们遇到了同样的问题,Pythoscope为Python程序生成unit testing。 在我们select一个目录之前,我们对python列表中的testing进行了民意调查,结果有很多不同的意见。 最后,我们select将“testing”目录放在与源代码相同的目录中。 在那个目录中,我们为父目录中的每个模块生成一个testing文件。
我也倾向于把我的unit testing放在文件本身,就像上面提到的Jeremy Cantrell所说的那样,尽pipe我倾向于不把testing函数放在主体中,而是把所有东西都放在
if __name__ == '__main__': do tests...
块。 这最终将向文件添加文档作为“示例代码”,以了解如何使用正在testing的python文件。
我应该补充一点,我倾向于编写非常紧凑的模块/类。 如果你的模块需要非常大量的testing,你可以把它们放在另一个,但即使如此,我仍然会添加:
if __name__ == '__main__': import tests.thisModule tests.thisModule.runtests
这可以让任何人阅读你的源代码知道在哪里寻找testing代码。
我使用tests/
目录,然后使用相对导入来导入主应用程序模块。 所以在MyApp / tests / foo.py中,可能有:
from .. import foo
导入MyApp.foo
模块。
我不相信有一个确定的“最佳实践”。
我把我的testing放在应用程序代码之外的另一个目录中。 然后,在运行所有testing之前,在我的testing运行器脚本(也包括其他一些function)中,将主应用程序目录添加到sys.path(允许您从任何位置导入模块)。 这样,当我释放它时,我永远不必从主代码中移除testing目录,节省了我的时间和精力。
从我在开发Pythontesting框架方面的经验来看,我build议把pythonunit testing放在一个单独的目录中。 维护一个对称的目录结构。 这将有助于打包核心库而不打包unit testing。 下面通过一个示意图来实现。
<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打包这些库时,可以只包装主库模块(仅)。 这有助于可维护性,特别是在敏捷环境中。
我build议你在GitHub上检查一些主要的Python项目,并得到一些想法。
当你的代码变得更大,并且你添加更多的库时,最好在同一个目录下创build一个testing文件夹,并且为每个testingtypes(unittest,integration,…)镜像你的项目目录结构。
例如,如果你有一个目录结构如:
myPackage/ myapp/ moduleA/ __init__.py module_A.py moduleB/ __init__.py module_B.py setup.py
添加testing文件夹后,你将有一个目录结构,如:
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
其中我的项目添加到网站包,指向我的工作副本。 运行我的testing我使用:
setup.py tests
使用我configuration的任何一个testing运行器。
每隔一段时间,我都会发现自己检查了testing位置的主题,每次大多数人推荐一个单独的文件夹结构旁边的库代码,但我发现,每次参数是相同的,并没有那么令人信服。 我最终把我的testing模块放在核心模块的旁边。
这样做的主要原因是: 重构 。
当我移动时,我希望testing模块随代码一起移动; 如果他们在一个单独的树中,很容易失去testing。 说实话,迟早你会得到一个完全不同的文件夹结构,如django , 烧瓶等等。 如果你不在乎,这很好。
你应该问自己的主要问题是:
我在写:
- a)可重复使用的库或
- b)build立一个项目,而不是把一些半分离的模块捆绑在一起?
如果一个:
单独的文件夹和维护其结构的额外努力可能更适合。 没有人会抱怨你的testing部署到生产 。
但是,将testing与核心文件夹混合在一起时,排除testing也同样容易; 把它放在setup.py中 :
find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"])
如果b:
您可能希望 – 像我们每个人一样 – 您正在编写可重用的库,但是大部分时间他们的生活都与项目的生命息息相关。 轻松维护项目的能力应该是重中之重。
那么如果你做得很好,而且你的模块很适合另一个项目,那么它可能会被拷贝到这个新项目中,而不是把它们分支出来或者制作成一个单独的库,很容易相比,在一个单独的testing文件夹已成为混乱的testing。 (你可能会争辩说,它不应该是一个混乱,但让我们现实在这里)。
所以select仍然是你的select,但是我会争辩说,通过混合testing,你可以像使用单独的文件夹一样完成所有的任务,但是在保持整洁方面花费更less。
我更喜欢testing目录。 这确实意味着import变得更加困难。 为此,我有两个解决scheme:
- 使用setuptools。 然后,您可以将
test_suite='tests.runalltests.suite'
传递给setup()
,并且可以运行testing:python setup.py test
- 运行testing时设置PYTHONPATH:
PYTHONPATH=. python tests/runalltests.py
PYTHONPATH=. python tests/runalltests.py
以下是M2Crypto中代码的支持情况:
- http://svn.osafoundation.org/m2crypto/trunk/setup.py
- http://svn.osafoundation.org/m2crypto/trunk/tests/alltests.py
如果你喜欢用鼻子testing来运行testing,你可能需要做一些不同的事情。
我们用
应用程序/ SRC / code.py
应用程序/testing/ code_test.py
应用程序/文档/ ..
在每个testing文件中,我们在sys.path中插入“../src/”。 这不是最好的解决scheme,但工程。 我认为如果有人在java中使用maven之类的东西,那将会非常棒,它可以让你使用标准的约定,不pipe你在哪个项目上工作。
在C#中,我通常将testing分为单独的程序集。
在Python中 – 到目前为止 – 我倾向于编写doctests,其中testing位于函数的docstring中,或者将它们放在if __name__ == "__main__"
底部的if __name__ == "__main__"
块中。
当编写一个名为“foo”的包时,我会把unit testing放到一个单独的包“foo_test”中。 然后,模块和子包将与SUT包模块具有相同的名称。 例如,在foo_test.xy中find模块foo.xy的testing。每个testing包的__init__.py文件都包含一个AllTests套件,该套件包含该软件包的所有testing套件。 setuptools提供了一个方便的方法来指定主要的testing包,所以在“python setup.py develop”之后,你可以使用“python setup.py test”或者“python setup.py test -s foo_test.x.SomeTestSuite”只是一个特定的套件。
如果testing很简单,只需将它们放在文档string中 – 大多数Pythontesting框架将能够使用:
>>> import module >>> module.method('test') 'testresult'
对于其他更多涉及的testing,我会把它们放在../tests/test_module.py
或在tests/test_module.py
。
我最近开始用Python编程,所以我还没有机会find最佳实践。 但是,我写了一个模块,find所有的testing并运行它们。
所以我有:
应用程序/ appfile.py testing/ appfileTest.py
当我进入更大的项目时,我将不得不看看它是如何发展的。