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

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

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

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

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

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


当前回答

实际运行测试的通常方式是什么

我使用的是Python 3.6.2

cd new_project

pytest test/test_antigravity.py

安装pytest: sudo pip install pytest

我没有设置任何路径变量,我的导入不会失败与相同的“测试”项目结构。

我注释掉了这些东西:如果__name__ == '__main__'像这样:

test_antigravity.py

import antigravity

class TestAntigravity(unittest.TestCase):

    def test_something(self):

        # ... test stuff here


# if __name__ == '__main__':
# 
#     if __package__ is None:
# 
#         import something
#         sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
#         from .. import antigravity
# 
#     else:
# 
#         from .. import antigravity
# 
#     unittest.main()

其他回答

如果你运行“python setup.py develop”,那么包就会在路径中。但你可能不想这样做,因为你可能会感染你的系统python安装,这就是virtualenv和buildout等工具存在的原因。

如果没有一些巫术,就不能从父目录导入。下面是另一种至少适用于Python 3.6的方法。

首先,创建一个包含以下内容的test/context.py文件:

import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

然后在test/test_antigravity.py文件中导入如下内容:

import unittest
try:
    import context
except ModuleNotFoundError:
    import test.context    
import antigravity

请注意,这个try-except子句的原因是

导入测试。当使用“python test_antigravity.py”运行时,上下文将失败 在new_project目录下使用"python -m unittest"运行时导入context失败。

通过这种诡计,他们都成功了。

现在你可以运行test目录下的所有测试文件:

$ pwd
/projects/new_project
$ python -m unittest

或者运行一个单独的测试文件:

$ cd test
$ python test_antigravity

好吧,这并不比在test_antigravity。py中包含context。py的内容漂亮多少,但也许会漂亮一点。欢迎提出建议。

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

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文件夹,我的单元测试或我的环境变量,一切都运行顺利。

你真的应该使用pip工具。

使用pip install -e。以开发模式安装包。这是pytest推荐的一种非常好的实践(请参阅他们的良好实践文档,其中还可以找到两种可以遵循的项目布局)。

如果你使用VS Code,并且你的测试与你的项目位于同一级别,那么运行和调试你的代码就不能开箱即用。你能做的就是改变启动方式。json文件:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python",
            "type": "python",
            "request": "launch",
            "stopOnEntry": false,
            "pythonPath": "${config:python.pythonPath}",
            "program": "${file}",
            "cwd": "${workspaceRoot}",
            "env": {},
            "envFile": "${workspaceRoot}/.env",
            "debugOptions": [
                "WaitOnAbnormalExit",
                "WaitOnNormalExit",
                "RedirectOutput"
            ]
        }    
    ]
}

这里的关键行是envFile

"envFile": "${workspaceRoot}/.env",

在项目的根目录中添加.env文件

在.env文件中添加项目根目录的路径。这将临时添加

PYTHONPATH = C: \ \ PYTHON项目\ \ ROOT_DIRECTORY

路径到你的项目,你将能够使用调试单元测试从VS Code