Ich versuche das jetzt für fast zwei Stunden, ohne Glück.Mocking ImportError in Python
Ich habe ein Modul, das wie folgt aussieht:
try:
from zope.component import queryUtility # and things like this
except ImportError:
# do some fallback operations <-- how to test this?
später im Code:
try:
queryUtility(foo)
except NameError:
# do some fallback actions <-- this one is easy with mocking
# zope.component.queryUtility to raise a NameError
Irgendwelche Ideen?
EDIT:
Alex Vorschlag nicht zu funktionieren scheint:
[email protected] ~/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
Aber es tut Arbeit wenn ich laufe:
>>> import __builtin__
>>> realimport = __builtin__.__import__
>>> def fakeimport(name, *args, **kw):
... if name == 'zope.component':
... raise ImportError
... realimport(name, *args, **kw)
...
>>> __builtin__.__import__ = fakeimport
Wenn die Tests laufen der gleiche Code von der interaktiven Python-Konsole.
MEHR EDIT:
Ich verwende zope.testing
und eine Testdatei, shorturl.txt
, die die Tests alle spezifisch für diesen Teil meines Moduls hat. Zuerst importiere ich das Modul mit zope.component
verfügbar, um zu demonstrieren & testen Sie die übliche Verwendung. Das Fehlen von zope.*
Paketen wird als Edge-Case betrachtet, daher teste ich es später. Also, ich muss reload()
mein Modul, nachdem zope.*
nicht verfügbar, irgendwie.
Bisher habe ich sogar versucht, tempfile.mktempdir()
und leer zope/__init__.py
und zope/component/__init__.py
Dateien im tempdir verwenden, dann tempdir zu sys.path[0]
Einsetzen und die alten zope.*
Pakete von sys.modules
entfernen.
Hat auch nicht funktioniert.
NOCH MEHR EDIT:
In der Zwischenzeit habe ich das versucht:
>>> class NoZope(object):
... def find_module(self, fullname, path):
... if fullname.startswith('zope'):
... raise ImportError
...
>>> import sys
>>> sys.path.insert(0, NoZope())
Und es funktioniert gut für den Namensraum des Testsuite (= für alle Einfuhren in shorturl.txt
) , aber es wird nicht in meinem Hauptmodul ao.shorturl
ausgeführt. Nicht einmal wenn ich es reload()
it. Irgendeine Idee warum?
>>> import zope # ok, this raises an ImportError
>>> reload(ao.shorturl) <module ...>
zope.interfaces
Importieren wirft ein ImportError
, so geht es nicht zu dem Teil, wo ich zope.component
importieren und es bleibt im ao.shorturl Namespace. Warum?!
>>> ao.shorturl.zope.component # why?!
<module ...>
Ah, danke! Aus irgendeinem Grund habe ich versucht, 'def __import __()' zu tun, habe es aber nicht 'builtind .__ import__' zugewiesen; wie dumm von mir. Interessant, ich habe gerade Ihre Antwort hier gelesen: http://stackoverflow.com/questions/2216828/mock-y-of-from-x-import-y-in-doctest-python/2217512#2217512 - meinst du? Es würde diese Situation einfacher machen, wenn ich nicht queryUtility in den Bereich meines Moduls importiere. –
@Attila, wenn Sie 'von Zope importieren Komponente' und dann' component.queryUtility' verwendet haben, würde es es einfacher machen, zum Beispiel das echte Ding eine Zeitlang zu verwenden, und eine gemockte/gefälschte Version an anderen Zeiten.Wie ich in dieser Antwort geschrieben habe, empfehle ich es als eine allgemeine Sache, und es ist ein Teil der Art, wie wir Python bei Google kodieren (manchmal ist eine 'as'-Klausel, um den Namen eines Imports zu verkürzen, natürlich gerechtfertigt, aber das tut nicht t ändern Sie die Semantik). –
Wenn Sie 'von Zope Import-Komponente', BTW, Ihre '__import__'-ähnliche Funktion wird' Zope' als das 'name' Argument, und' 'Komponente'' als ein Element in der 'fromlist'-Argument (der einzige, es sei denn du machst 'von zope importieren, das, komponente' oder ähnliches ;-); also sicher sein, entsprechend zu triggern. –