对于一个简单的Python模块来说,非常常见的目录结构似乎是将单元测试分离到它们自己的测试目录中:

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.

我的问题很简单,实际运行测试的通常方式是什么?我怀疑这对每个人来说都是显而易见的,除了我,但你不能只是从测试目录运行python test_antigravity.py,因为它的导入antigravity将失败,因为模块不在路径上。

我知道我可以修改PYTHONPATH和其他与搜索路径相关的技巧,但我不能相信这是最简单的方法——如果您是开发人员,这很好,但如果用户只是想检查测试是否通过,那么期望他们使用这种方法是不现实的。

另一种替代方法是将测试文件复制到另一个目录中,但这似乎有点愚蠢,并且没有注意到将它们放在一个单独的目录中。

那么,如果您刚刚下载源代码到我的新项目,您将如何运行单元测试?我更喜欢这样的答案:“要运行单元测试,请执行x。”


当前回答

如果在测试目录中有多个目录,则必须向每个目录添加__init__.py文件。

/home/johndoe/snakeoil
└── test
    ├── __init__.py        
    └── frontend
        └── __init__.py
        └── test_foo.py
    └── backend
        └── __init__.py
        └── test_bar.py

然后一次运行所有测试,运行:

python -m unittest discover -s /home/johndoe/snakeoil/test -t /home/johndoe/snakeoil

来源:python -m unittest -h

  -s START, --start-directory START
                        Directory to start discovery ('.' default)
  -t TOP, --top-level-directory TOP
                        Top level directory of project (defaults to start
                        directory)

其他回答

我也遇到了同样的问题,使用了一个单独的单元测试文件夹。根据上述建议,我将绝对源路径添加到sys.path中。

以下解决方案的好处是,你可以运行test/test_yourmodule.py文件,而不需要一开始就切换到test目录:

import sys, os
testdir = os.path.dirname(__file__)
srcdir = '../antigravity'
sys.path.insert(0, os.path.abspath(os.path.join(testdir, srcdir)))

import antigravity
import unittest

同样的问题我已经有很长时间了。我最近选择的目录结构是这样的:

project_path
├── Makefile
├── src
│   ├── script_1.py
│   ├── script_2.py
│   └── script_3.py
└── tests
    ├── __init__.py
    ├── test_script_1.py
    ├── test_script_2.py
    └── test_script_3.py

在test文件夹的__init__.py脚本中,我写了以下内容:

import os
import sys
PROJECT_PATH = os.getcwd()
SOURCE_PATH = os.path.join(
    PROJECT_PATH,"src"
)
sys.path.append(SOURCE_PATH)

对于共享项目来说,Makefile非常重要,因为它强制正确地运行脚本。下面是我放在Makefile中的命令:

run_tests:
    python -m unittest discover .

The Makefile is important not just because of the command it runs but also because of where it runs it from. If you would cd in tests and do python -m unittest discover ., it wouldn't work because the init script in unit_tests calls os.getcwd(), which would then point to the incorrect absolute path (that would be appended to sys.path and you would be missing your source folder). The scripts would run since discover finds all the tests, but they wouldn't run properly. So the Makefile is there to avoid having to remember this issue.

我真的很喜欢这种方法,因为我不需要触及我的src文件夹,我的单元测试或我的环境变量,一切都运行顺利。

对于用户来说,最简单的解决方案是提供一个可执行脚本(runtests.py或类似的脚本),该脚本引导必要的测试环境,包括(如果需要的话)将根项目目录添加到sys. py。临时道路。这并不需要用户设置环境变量,类似这样的东西在引导脚本中工作得很好:

import sys, os

sys.path.insert(0, os.path.dirname(__file__))

然后你给用户的指令可以像“python runtests.py”一样简单。

当然,如果你需要的路径确实是os.path.dirname(__file__),那么你不需要将它添加到sys. path.dirname(__file__)。道路;Python总是将当前运行脚本的目录放在sys. exe的开头。路径,因此根据您的目录结构,将runtests.py定位到正确的位置可能就足够了。

此外,Python 2.7+中的unittest模块(在Python 2.6及更早的版本中被反向移植为unittest2)现在内置了测试发现,所以如果你想自动化测试发现,nose不再是必要的:你的用户指令可以像Python -m unittest discover一样简单。

在我看来,最好的解决方案是使用unittest命令行界面,它会将目录添加到sys. exe目录。路径,因此您不必(在TestLoader类中完成)。

例如,对于这样的目录结构:

new_project
├── antigravity.py
└── test_antigravity.py

你可以直接运行:

$ cd new_project
$ python -m unittest test_antigravity

对于像你这样的目录结构:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

在测试包中的测试模块中,可以像往常一样导入反重力包及其模块:

# import the package
import antigravity

# import the antigravity module
from antigravity import antigravity

# or an object inside the antigravity module
from antigravity.antigravity import my_object

运行单个测试模块:

要运行单个测试模块,在本例中为test_antigravity.py:

$ cd new_project
$ python -m unittest test.test_antigravity

引用测试模块的方法与导入测试模块的方法相同。

运行单个测试用例或测试方法:

你也可以运行一个TestCase或者一个测试方法:

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method

运行所有测试:

你也可以使用测试发现,它会发现并运行所有的测试,它们必须是命名为test*.py的模块或包(可以使用-p,——pattern标志进行更改):

$ cd new_project
$ python -m unittest discover
$ # Also works without discover for Python 3
$ # as suggested by @Burrito in the comments
$ python -m unittest

这将运行测试包中的所有test*.py模块。

Python 3 +

添加到@Pierre

使用这样的unittest目录结构:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

运行测试模块test_antigravity.py:

$ cd new_project
$ python -m unittest test.test_antigravity

或者一个单独的TestCase

$ python -m unittest test.test_antigravity.GravityTestCase

强制性的不要忘记__init__.py,即使为空,否则将无法工作。