python - 在 Python 中模拟 ImportError

标签 python unit-testing mocking doctest zope.component

我现在已经尝试了将近两个小时,但没有任何运气。

我有一个看起来像这样的模块:

try:
    from zope.component import queryUtility  # and things like this
except ImportError:
    # do some fallback operations <-- how to test this?

后面的代码:

try:
    queryUtility(foo)
except NameError:
    # do some fallback actions <-- this one is easy with mocking 
    # zope.component.queryUtility to raise a NameError

有什么想法吗?

编辑:

Alex 的建议似乎行不通:

>>> import __builtin__
>>> realimport = __builtin__.__import__
>>> def fakeimport(name, *args, **kw):
...     if name == 'zope.component':
...         raise ImportError
...     realimport(name, *args, **kw)
...
>>> __builtin__.__import__ = fakeimport

运行测试时:

aatiis@aiur ~/work/ao.shorturl $ ./bin/test --coverage .
Running zope.testing.testrunner.layer.UnitTests tests:
  Set up zope.testing.testrunner.layer.UnitTests in 0.000 seconds.


Error in test /home/aatiis/work/ao.shorturl/src/ao/shorturl/shorturl.txt
Traceback (most recent call last):
  File "/usr/lib64/python2.5/unittest.py", line 260, in run
    testMethod()
  File "/usr/lib64/python2.5/doctest.py", line 2123, in runTest
    test, out=new.write, clear_globs=False)
  File "/usr/lib64/python2.5/doctest.py", line 1361, in run
    return self.__run(test, compileflags, out)
  File "/usr/lib64/python2.5/doctest.py", line 1282, in __run
    exc_info)
  File "/usr/lib64/python2.5/doctest.py", line 1148, in report_unexpected_exception
    'Exception raised:\n' + _indent(_exception_traceback(exc_info)))
  File "/usr/lib64/python2.5/doctest.py", line 1163, in _failure_header
    out.append(_indent(source))
  File "/usr/lib64/python2.5/doctest.py", line 224, in _indent
    return re.sub('(?m)^(?!$)', indent*' ', s)
  File "/usr/lib64/python2.5/re.py", line 150, in sub
    return _compile(pattern, 0).sub(repl, string, count)
  File "/usr/lib64/python2.5/re.py", line 239, in _compile
    p = sre_compile.compile(pattern, flags)
  File "/usr/lib64/python2.5/sre_compile.py", line 507, in compile
    p = sre_parse.parse(p, flags)
AttributeError: 'NoneType' object has no attribute 'parse'



Error in test BaseShortUrlHandler (ao.shorturl)
Traceback (most recent call last):
  File "/usr/lib64/python2.5/unittest.py", line 260, in run
    testMethod()
  File "/usr/lib64/python2.5/doctest.py", line 2123, in runTest
    test, out=new.write, clear_globs=False)
  File "/usr/lib64/python2.5/doctest.py", line 1351, in run
    self.debugger = _OutputRedirectingPdb(save_stdout)
  File "/usr/lib64/python2.5/doctest.py", line 324, in __init__
    pdb.Pdb.__init__(self, stdout=out)
  File "/usr/lib64/python2.5/pdb.py", line 57, in __init__
    cmd.Cmd.__init__(self, completekey, stdin, stdout)
  File "/usr/lib64/python2.5/cmd.py", line 90, in __init__
    import sys
  File "<doctest shorturl.txt[10]>", line 4, in fakeimport
NameError: global name 'realimport' is not defined

但是,当我从 python 交互式控制台运行相同的代码时,它确实起作用。

更多编辑:

我正在使用 zope.testing 和一个测试文件 shorturl.txt,其中包含我的模块的这一部分特定的所有测试。首先,我导入了可用的 zope.component 模块,以演示和测试通常的用法。缺少 zope.* 包被认为是一种边缘情况,所以我稍后会对其进行测试。因此,在以某种方式使 zope.* 不可用之后,我必须 reload() 我的模块。

到目前为止,我什至尝试过使用 tempfile.mktempdir() 和空的 zope/__init__.pyzope/component/__init__.py 文件,然后将 tempdir 插入 sys.path[0],并从 sys.modules 中删除旧的 zope.* 包>.

也没用。

更多编辑:

同时,我试过这个:

>>> class NoZope(object):
...     def find_module(self, fullname, path):
...         if fullname.startswith('zope'):
...             raise ImportError
... 

>>> import sys
>>> sys.path.insert(0, NoZope())

它适用于测试套件的命名空间(= shorturl.txt 中的所有导入),但它没有在我的主模块 ao.shorturl。甚至当我 reload() 时也不行。知道为什么吗?

>>> import zope  # ok, this raises an ImportError
>>> reload(ao.shorturl)    <module ...>

导入 zope.interfaces 引发了一个 ImportError,所以它没有到达我导入 zope.component 的部分,并且 < strong>它保留在 ao.shorturl 命名空间中。为什么?!

>>> ao.shorturl.zope.component  # why?! 
<module ...>

最佳答案

只需将 monkeypatch 放入您自己的 __import__ 版本的 builtins 中——当它识别出在您想要的特定模块上调用它时,它可以引发您想要的任何东西模拟错误。参见 the docs为丰富的细节。大致:

try:
    import builtins
except ImportError:
    import __builtin__ as builtins
realimport = builtins.__import__

def myimport(name, globals, locals, fromlist, level):
    if ...:
        raise ImportError
    return realimport(name, globals, locals, fromlist, level)

builtins.__import__ = myimport

代替 ...,您可以对 name == 'zope.component' 进行硬编码,或者使用您自己的回调更灵活地安排事情根据您的特定测试需求,在不同情况下按需增加导入,而不需要您编写多个 __import__-like 函数;-)。

另请注意,如果您使用的不是 import zope.componentfrom zope.component import something,而是 from zope import componentname 将是 'zope''component' 将成为 fromlist.

编辑:__import__ 函数的文档说要导入的名称是builtin(就像在 Python 3 中一样),但实际上你需要 __builtins__ -- 我已经编辑了上面的代码,所以它可以以任何一种方式工作。

关于python - 在 Python 中模拟 ImportError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2481511/

相关文章:

python - 用于调整图像大小的独立 Python 脚本

支持实际结果和静态方法的 Java Mock 框架

javascript - 如何使用 Jest 测试忽略导入语句?

java - 模拟静态方法,该方法调用传递给其中的参数的 void 方法

python - 如何导入和猴子修补与测试位于不同包中的 Python 模块?

python - 机器学习模型持久性选项

python - 如何为联合网格中的每个 kdeplot 设置线条样式

unit-testing - 如何在Grails单元测试中获取抽象域类的实例?

python - .capitalize 函数的奇怪行为

java - 使用不同的参数运行两个相同的测试