【问题标题】:sharing a module between tests and core - appropriate project structure在测试和核心之间共享一个模块 - 适当的项目结构
【发布时间】:2019-03-14 10:05:40
【问题描述】:

我正在尝试在添加到代码库的同时改进项目结构。我找到了一个示例结构here,它看起来像这样:

README.rst
LICENSE
setup.py
requirements.txt
sample/__init__.py
sample/core.py
sample/helpers.py
docs/conf.py
docs/index.rst
tests/test_basic.py
tests/test_advanced.py

我特别注意到requirements.txtsetup.py 的级别高于tests/sample/

如果我添加sample/classes.py,您只需在sample/core.py 中写入from classes import MyClass 即可获得它。然而,它不能那么容易地导入tests/test_basic.py,在导入时看起来不像 python '看起来在拐角处'。

在我的例子中,还有一个 MANIFEST.in 与 requirements.txt 和一些不是真正的 python 文件在同一级别上,但只是为运行它的平台设置了一些东西。

如果classes.pyrequirements.txt 处于同一级别,我认为tests/sample/ 及其子目录中的所有内容都可以轻松导入它,但它可能需要__init__.py感觉不错。

如果tests/sample/ 都需要使用它,那么它应该去哪里呢?

【问题讨论】:

  • 测试的重点是他们应该可以访问sample中的代码;他们只需要以sample.classes 的名义导入classes
  • 我认为那里还没有框架,最近正在阅读那些可能是未来的计划。现在我运行这样的测试:python -m unittest discover path/to/tests
  • @jwodder 我刚试过,但它说ImportError: No module named 'sample' 所以我试着在from .sample.classes import classes 之前加一个点,它说SystemError: Parent module '' not loaded, cannot perform relative import

标签: python python-3.x python-module directory-structure pep8


【解决方案1】:

让我们让它变得简单。

如果我理解正确,问题是How to import simple module in test。这意味着你想使用from simple.classes import MyClass之类的东西。

这很简单,只需在执行python test/test_basic.py 之前将根路径添加到PYTHONPATH

这也是 IDE 在您通过它执行测试时为您做的事情。

【讨论】:

  • 你的意思是在导入语句之前,import sys 然后sys.path.append('/path/to/project')?是的,这可能会奏效,但我只是认为有一种更优雅的方式来做到这一点。
  • 你可以操纵你的环境变量:PYTHONPATH=/path/to/project python test/test_basic.py.
  • 好的,所以classes.py will continue to live in sample/`。我去了 linux 终端并运行 export PYTHONPATH=/path/to/project 然后 printenv | grep PYTHON 以确保它在那里,但这似乎还不足以让它工作。导入语句附近还需要其他一些东西,但我正在尝试找出什么..
  • 应该够了,也许您可​​以更新问题以澄清您现在的问题。
  • 谢谢顺便说一句,是的,PYTHONPATH 的解决方案确实有效,我在测试时遇到了一个错字。让两个终端打开一个带环境变量的终端,一个不带环境变量的终端,并证明它有效。学到了一些新东西。关于这是否是一个好主意的很多意见,例如负面意见herehere 和正面意见here。问题是关于风格和最佳实践的,但我放弃了,只做有效且优雅的一半。
【解决方案2】:

假设您使用 Python >= 3.3,您只需在包中添加 __init__.py 模块即可将测试文件夹转换为包。然后在那个__init__.py(并且只有在那里)中,将父包的路径添加到sys.path。如果足以让 unittest 发现将其用于tests 中的所有模块。

我的只是:

import os
import sys

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

如果您需要从其中一个测试模块访问classes.py,您可以使用:

from sample import classes

或者直接导入MyClass:

from sample.classes import MyClass

之所以能正常工作,是因为 sample 已经是一个包,并且当 python unittest 加载了 test 包时,它的父文件夹已添加到 sys.path


当然,这仅适用于您可以将测试放在一个包中。如果由于任何原因它不是一个选项,例如因为您需要单独运行测试模块,那么您应该将 sys.path 修改直接放在所有测试文件中。

在tests文件夹中写一个path_helper.py文件:

import os
import sys

core_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if core_path not in sys.path:  # don't add it if it is already here
    sys.path.append(core_path)

然后您可以将其导入所有测试文件中:

import path_helper
...

【讨论】:

  • 谢谢很有用,虽然我已经花了很多时间,但还是无法让它完全正常工作。如果您在建议的__init__.py 中导入类也有帮助打包失败。
  • @cardamom:你说得对,我的解决方案要求所有测试都作为一个包运行。我刚刚添加了一种没有此要求的替代方式。
  • 谢谢,这看起来很优雅。很快就会尝试一下,它至少可以避免将长而丑陋的 sys.append os.path 东西放在文件的顶部。
  • @cardamom:它在所有文件中始终如一地执行,但至少你只写了一次sys.path.append(...,并且在所有其他文件中只写了一个更好的import...
猜你喜欢
  • 2016-10-19
  • 1970-01-01
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-15
  • 1970-01-01
  • 2019-11-05
相关资源
最近更新 更多