使用鼻子进行测试的 Python 导入 - 在当前包之上导入模块的最佳实践是什么

使用鼻子进行测试的 Python 导入 - 在当前包之上导入模块的最佳实践是什么

问题描述:

这是一个经常以不同形式提出的问题,并且经常得到哈哈,你做得不对"的回答.很确定这是因为人们(包括我在内)试图将一个常识场景用作实现,而解决方案并不明显(如果您以前没有这样做过).

This is a question which is asked frequently in different forms, and often obtains "lol you're not doing it properly" responses. Pretty sure that's because there's a common sense scenario people (including me) are trying to use as an implementation, and the solution is not obvious (if you've not done it before).

会接受让瓶子飞出"的答案.

Would accept an answer which "lets the fly out of the bottle".

给定

project/
    __init__.py
    /code
        __init__.py
        sut.py
    /tests
        __init__.py
        test_sut.py

tests_sut.py 从哪里开始:

Where tests_sut.py starts:

import code.sut

在根目录中运行鼻子测试导致:

Running nosetests in the root dir leads to:

ImportError: No module named code.sut

走过的路:

a) 使用

from ..code import sut

b) 将项目根目录添加到 PYTHONPATH

b) add root of project to PYTHONPATH

c) 使用

sys.path.append

在每个测试模块开始的导入之前添加 .. 路径.

to add the .. path before the imports at the start of each test module.

d) 只记得做一个

setup.py 

在运行测试之前在项目中将模块安装到站点包中.

on the project to install the modules into the site-packages before running tests.

所以要求是在测试包根目录下放置测试,这些测试包可以访问项目.以上每一项对我来说都不自然",已被证明有问题或看起来太辛苦了!

So the requirement is to have tests located beneath the test package root which have access to the project. Each of the above don't feel "natural" to me, have proved problematic or seem like too much hard work!

在 java 中这是可行的,但基本上是通过您的构建工具/IDE 将所有类放在类路径上.也许问题是我期待 Python 的魔法"?在 Flask webframework 测试中已经注意到,选项 d) 似乎是首选.

In java this works, but basically by dint of your build tool / IDE placing all your classes on the classpath. Perhaps the issue is I'm expecting "magic" from Python? Have noted in the Flask webframework tests, option d) seems to be preferred.

无论如何,下面推荐首选解决方案的陈述会消除我自己的不自然"的感觉.

In any case, statements below recommending a preferred solution would remove the feeling of "unnaturalness" in my own.

您已经很好地回答了您的问题.D(安装到系统位置)是可分发代码的首选.我通常使用 C(修改 sys.path),因为我不希望在系统范围内安装数百个自定义库.理论上 A(相对导入)看起来更好,但也有失败的情况.B (PYTHONPATH) 是正确的,在我看来仅用于测试目的.

You have answered your question pretty well already.. D (install to system location) is preferred for distributable code. I usually use C (modify sys.path) because I don't want system-wide installs of my hundreds of custom libs. In theory A (relative import) seems nicer, but there are cases where it fails. B (PYTHONPATH) is right out, really only for testing purposes in my opinion.

这几乎总结了所有选项.您喜欢的选项(Python 神奇地知道在哪里查找)实际上不是一个可行的解决方案,因为它可能导致不可预测的结果,例如从不相关的项目中自动查找库.

That pretty much sums up all of the options. The option you prefer (Python magically knows where to look) is really not a workable solution because it can lead to unpredictable results, such as automagically finding libraries from unrelated projects.

在我看来,最好的做法是将其放在程序的入口点:

In my opinion, the best thing to do is put this at the entry point(s) to your program:

import sys, os
sys.path = [os.path.abspath(os.path.dirname(__file__))] + sys.path