`py.test` 和 `__init__.py` 文件

10 投票
3 回答
26072 浏览
提问于 2025-04-17 06:55

我原以为 py.test 是“独立”的,意思是它会把 test_*.py 文件“原封不动”地处理,只会导入这些文件里指定的模块,而不管周围的其他文件。结果我发现我错了。下面是我和 py.test 的对话:

$ ls
__init__.py    test_pytest.py
$ cat __init__.py 
$ cat test_pytest.py 
def test_pytest():
    assert True
$ py.test test_pytest.py 
========================================================= test session starts ==========================================================
platform darwin -- Python 2.7.2 -- pytest-2.1.3
collected 0 items / 1 errors 

================================================================ ERRORS ================================================================
___________________________________________________ ERROR collecting test_pytest.py ____________________________________________________
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/py-1.4.5-py2.7.egg/py/_path/local.py:529: in pyimport
>           mod = __import__(modname, None, None, ['__doc__'])
E           ImportError: No module named test_pytest
======================================================= 1 error in 0.01 seconds ========================================================
$ rm __init__.py 
$ py.test test_pytest.py 
========================================================= test session starts ==========================================================
platform darwin -- Python 2.7.2 -- pytest-2.1.3
collected 1 items 

test_pytest.py .

======================================================= 1 passed in 0.01 seconds =======================================================
$ 

我该怎么做才能让 py.test 正常工作,同时又保留我的 __init__.py 文件呢?

更新

在评论中,Holger Krekel 问我父目录的名字是什么。结果我发现,只有在父目录的名字是特定的情况下(比如和某个已安装的包同名,如 distutils),我才能重现上面的错误。请看这里:

~/test_min 
$ tree
.
└── distutils
    ├── __init__.py
    └── test_pytest.py

1 directory, 2 files
~/test_min 
$ cat distutils/__init__.py 
~/test_min 
$ cat distutils/test_pytest.py 
def test_pytest():
    assert True
~/test_min 
$ py.test distutils/test_pytest.py 
======================== test session starts =========================
platform darwin -- Python 2.7.2 -- pytest-2.1.3
collected 0 items / 1 errors 

=============================== ERRORS ===============================
_____________ ERROR collecting distutils/test_pytest.py ______________
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/py-1.4.5-py2.7.egg/py/_path/local.py:529: in pyimport
>           mod = __import__(modname, None, None, ['__doc__'])
E           ImportError: No module named test_pytest
====================== 1 error in 0.01 seconds =======================
~/test_min 
$ rm distutils/__init__.py 
~/test_min 
$ py.test distutils/test_pytest.py 
======================== test session starts =========================
platform darwin -- Python 2.7.2 -- pytest-2.1.3
collected 1 items 

distutils/test_pytest.py .

====================== 1 passed in 0.01 seconds ======================
~/test_min 
$ touch __init__.py
~/test_min 
$ ls
__init__.py distutils
~/test_min 
$ touch distutils/__init__.py
~/test_min 
$ py.test distutils/test_pytest.py 
======================== test session starts =========================
platform darwin -- Python 2.7.2 -- pytest-2.1.3
collected 1 items 

distutils/test_pytest.py .

====================== 1 passed in 0.02 seconds ======================
~/test_min 
$ rm __init__.py 
~/test_min 
$ py.test distutils/test_pytest.py 
======================== test session starts =========================
platform darwin -- Python 2.7.2 -- pytest-2.1.3
collected 0 items / 1 errors 

=============================== ERRORS ===============================
_____________ ERROR collecting distutils/test_pytest.py ______________
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/py-1.4.5-py2.7.egg/py/_path/local.py:529: in pyimport
>           mod = __import__(modname, None, None, ['__doc__'])
E           ImportError: No module named test_pytest
====================== 1 error in 0.01 seconds =======================
~/test_min 
$ mv distutils foobar
~/test_min 
$ py.test foobar/test_pytest.py 
======================== test session starts =========================
platform darwin -- Python 2.7.2 -- pytest-2.1.3
collected 1 items 

foobar/test_pytest.py .

====================== 1 passed in 0.01 seconds ======================
~/test_min 
$ 

希望这些额外的信息能有所帮助。

3 个回答

4

你可以告诉pytest忽略特定的文件或文件模式,具体的做法可以在这里找到。你只需要在项目的根目录下放一个conftest.py文件,里面列出你希望pytest忽略的文件:

不过,很多项目会有一个setup.py文件,而你可能不想让它被导入。此外,有些文件可能只能被特定版本的Python导入。对于这种情况,你可以通过在conftest.py文件中动态定义要忽略的文件来解决:

 # content of conftest.py
 import sys

 collect_ignore = ["setup.py"]
7

我真的建议你把这个文件夹改个名字,不要叫“distutils”。为什么呢?因为你这样会覆盖掉一个已经存在的模块。当你的代码里出现“import distutils”或者“from distutils import *”时,它会优先使用你这个文件夹里的内容,而不是系统自带的那个模块。

如果distutils模块已经被加载过了,你的distutils就不会被加载,因为全局环境里已经有这个名字了。

其实,把这个文件夹改个名字(比如叫tests)要简单得多,而不是去和py.test或者Python内部的机制对着干。

17

看起来 py.test 在用 py._path.pyimport 来打开你的文件。如果这个文件夹里有一个 __init__.py 文件,它就会把你的文件当成一个模块来处理;如果没有这个文件,它就直接打开文件。简单来说,要么删掉 __init__.py,要么把你的测试放到项目代码之外的其他文件夹里(<--- 这是个好主意)。

https://py.readthedocs.io/en/latest/path.html#py._path.local.LocalPath.pyimport

撰写回答