我具有以下Python 2.7简化的项目结构:
project/
├── libs/
| └── zipfile.py
├── tests/
| ├── __init__.py
| └── test_hello.py
├── hello.py
└── main.py
我希望该项目使用位于zipfile
中的Python内置模块之一(在本示例中为libs
)的修补版本。请注意,这是一个外部要求,我无法更改项目结构。
下面是每个文件的简化实现:
libs / zipfile.py
def is_zipfile(filename):
return "Patched zipfile called"
tests / test_hello.py
from hello import hello
def test_hello():
assert hello() == "Patched zipfile called"
hello.py
import os
import sys
libs_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "libs"))
if libs_path not in sys.path:
sys.path.insert(1, libs_path)
import zipfile
def hello():
print(zipfile.__file__) # to check which zipfile module is imported
result = zipfile.is_zipfile("some_path")
return result
main.py
from hello import hello
def main():
print(hello())
if __name__ == "__main__":
main()
直接运行程序(python main.py
)时,我得到了预期的结果:
/home/project/libs/zipfile.pyc
Patched zipfile called
但是,以project
作为工作目录(pytest -s
)运行pytest时,它失败了:
/usr/lib/python2.7/zipfile.pyc
================================== FAILURES ===================================
_________________________________ test_hello __________________________________
def test_hello():
> assert hello() == "Patched zipfile called"
E assert False == 'Patched zipfile called'
E + where False = hello()
tests/test_hello.py:4: AssertionError
========================== 1 failed in 0.13 seconds ===========================
我尝试了this SO post中介绍的几种解决方案,例如运行python -m pytest
,但没有一个解决方案对我有用。有没有办法以一种非骇客的方式成功运行此测试?
答案 0 :(得分:1)
未导入修补的zipfile
模块的原因是它已经在测试开始之前被导入(可能是pytest或其依赖项之一)
我通过将其放在hello.py
的顶部来验证了这一点:
if 'zipfile' in sys.modules:
raise AssertionError('zipfile already imported')
然后我得到:
$ ./venv/bin/python -mpytest tests
============================= test session starts ==============================
platform linux -- Python 3.6.7, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
rootdir: /tmp/x, inifile:
collected 0 items / 1 errors
==================================== ERRORS ====================================
_____________________ ERROR collecting tests/test_hello.py _____________________
tests/test_hello.py:1: in <module>
from hello import hello
hello.py:5: in <module>
raise AssertionError('zipfile already imported')
E AssertionError: zipfile already imported
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.14 seconds ============================
您可以从zipfile
中删除sys.modules
,然后也许您的副本将是唯一导入的副本:
sys.modules.pop('zipfile', None)
也就是说,所有这一切似乎都是一个坏主意,因为已经导入该模块的任何人都可以访问旧的zipfile
,而取消stdlib的实现则很有可能破坏那些第三方库。没想到。
通过直接修补zipfile
模块上的各个方法(使用类似mock.patch.object(zipfile, 'fn', ...)