我已经尝试了将近两个小时,没有任何运气。
我有一个看起来像这样的模块:
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
有任何想法吗?
编辑:
亚历克斯的建议似乎无效:
>>> 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.*
包被认为是一种极端情况,因此我稍后将对其进行测试。因此,以某种方式使reload()
不可用之后,我必须对模块进行zope.*
编码。到目前为止,我什至尝试过在tempdir中使用
tempfile.mktempdir()
以及空的zope/__init__.py
和zope/component/__init__.py
文件,然后将tempdir插入sys.path[0]
中,并从zope.*
中删除旧的sys.modules
包。也没用。
甚至更多编辑:
在此期间,我已经尝试过了:
>>> class NoZope(object):
... def find_module(self, fullname, path):
... if fullname.startswith('zope'):
... raise ImportError
...
>>> import sys
>>> sys.path.insert(0, NoZope())
它对于测试套件的 namespace (=对于
shorturl.txt
中的所有导入)都适用,但是在我的主模块ao.shorturl
中未执行。甚至当我reload()
时也没有。知道为什么吗?>>> import zope # ok, this raises an ImportError
>>> reload(ao.shorturl) <module ...>
导入
zope.interfaces
会引发一个ImportError
,因此它不会到达我导入zope.component
的部分,而仍保留在ao.shorturl命名空间中。为什么?!>>> ao.shorturl.zope.component # why?!
<module ...>
最佳答案
只需将自己的builtins
版本猴子化到__import__
中,当它识别出要模拟其错误的特定模块上正在调用它时,它可以引发您想要的任何事情。有关详细信息,请参见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__
的函数;-)。还要注意,如果您使用的不是
import zope.component
或from zope.component import something
,而是from zope import component
,那么name
将是'zope'
,然后'component'
将是fromlist
中的唯一项。编辑:
__import__
函数的文档说要导入的名称是builtin
(就像在Python 3中一样),但是实际上您需要__builtins__
-我已经编辑了上面的代码,因此无论哪种方式都可以工作。