pytest无法导入本地模块(而是导入内置模块)

时间:2019-03-08 21:26:17

标签: python python-2.7 pytest

我具有以下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,但没有一个解决方案对我有用。有没有办法以一种非骇客的方式成功运行此测试?

1 个答案:

答案 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', ...)

的方法,可能会好运一些)