Python, Einheit-Prüfung und spöttische Einfuhren
-
05-07-2019 - |
Frage
Ich bin in einem Projekt, wo wir Refactoring einig massive Code-Basis beginnen. Ein Problem, das sofort entstanden ist, dass jede Datei eine Menge anderer Dateien importiert. Wie kann ich in einem eleganten Weg, dies in meinem Unit-Test verspotten, ohne den eigentlichen Code zu ändern, so kann ich anfangen Einheit-Tests zu schreiben?
Als Beispiel: Die Datei mit den Funktionen Ich möchte testen, importiert zehn andere Dateien, die Teil unserer Software und nicht Python Kern libs
.Ich möchte in die Lage, die Unit-Tests möglichst getrennt und jetzt habe ich nur Funktionen gehen zu testen, bin zu laufen, der auf Dinge aus den Dateien hängen nicht, die importiert werden.
Danke für alle Antworten.
Ich wusste nicht wirklich, was ich von Anfang an tun wollte, aber jetzt ich glaube, ich weiß.
Das Problem war, dass einige der Einfuhr war nur möglich, wenn die gesamte Anwendung, weil einige von Drittanbietern Auto-Magie lief. So hatte ich für diese Module in einem Verzeichnis einige Stubs zu machen, die ich mit sys.path wies darauf hin,
Jetzt kann ich die Datei importieren, die die Funktionen enthält Ich möchte Tests schreiben in meiner Unit-Test-Datei ohne Beschwerden über Module fehlen.
Lösung
Wenn Sie möchten, um ein Modul importieren, während gleichzeitig sichergestellt wird, dass er nichts importieren, können Sie die __import__
eingebaute Funktion ersetzen.
Zum Beispiel verwenden diese Klasse:
class ImportWrapper(object):
def __init__(self, real_import):
self.real_import = real_import
def wrapper(self, wantedModules):
def inner(moduleName, *args, **kwargs):
if moduleName in wantedModules:
print "IMPORTING MODULE", moduleName
self.real_import(*args, **kwargs)
else:
print "NOT IMPORTING MODULE", moduleName
return inner
def mock_import(self, moduleName, wantedModules):
__builtins__.__import__ = self.wrapper(wantedModules)
try:
__import__(moduleName, globals(), locals(), [], -1)
finally:
__builtins__.__import__ = self.real_import
Und in Ihrem Testcode, statt import myModule
schreiben, schreiben Sie:
wrapper = ImportWrapper(__import__)
wrapper.mock_import('myModule', [])
Das zweite Argument für mock_import
ist eine Liste der Modulnamen Sie Sie will in Innenmodul importieren.
Dieses Beispiel weiter modifiziert werden, um z.B. importieren andere Modul als statt einfach nicht importieren es erwünscht oder sogar das Modulobjekt mit einigen benutzerdefinierten Objekt der eigenen spöttisch.
Andere Tipps
Wenn Sie wirklich mit dem Python-Import-Mechanismus verarschen wollen, werfen Sie einen Blick auf die ihooks
Modul. Es bietet Werkzeuge für das Verhalten der __import__
Einbau-Wechsel. Aber es ist nicht klar, aus Ihrer Frage, warum diese müssen tun.
„importiert eine Menge anderer Dateien“? Importiert eine Menge anderer Dateien, die Teil Ihrer kundenspezifischen Code-Basis sind? Oder importiert eine Menge anderer Dateien, die Teil der Python-Distribution sind? Oder importiert viele andere Open-Source-Projekt-Dateien?
Wenn Ihre Importe nicht funktionieren, haben Sie ein „einfaches“ PYTHONPAT
H Problem. Erhalten Sie alle Ihre verschiedenen Projektverzeichnisse auf ein PYTHONPATH
, die Sie zum Testen verwenden können. Wir haben einen ziemlich komplexen Pfad, in Windows verwalten wir es wie folgt
@set Part1=c:\blah\blah\blah
@set Part2=c:\some\other\path
@set that=g:\shared\stuff
set PYTHONPATH=%part1%;%part2%;%that%
Wir halten jedes Stück des Weges getrennt, so dass wir (a) wissen, wo die Dinge herkommen und (b) Änderung verwalten, wenn wir die Dinge bewegen.
Da die PYTHONPATH
um gesucht wird, können wir kontrollieren, was durch Einstellen der Reihenfolge auf dem Weg verwendet wird.
Wenn Sie „alles“ haben, wird es eine Frage des Vertrauens.
Entweder
- Sie vertrauen etwas (das heißt, die Python-Code-Basis) und importieren Sie es einfach.
oder
-
Sie haben kein Vertrauen in etwas (das heißt, Ihren eigenen Code), und Sie
- testen Sie es separat und
- verspotten es für Stand-alone-Test.
Möchten Sie die Python-Bibliotheken testen? Wenn ja, haben Sie eine Menge Arbeit. Wenn nicht, dann sollten Sie vielleicht nur die Dinge, verspotten Sie gehen, um tatsächlich zu testen.
Keine schwierige Manipulation ist notwendig, wenn Sie eine schnelle und unsaubere fix bevor Sie das Gerät-Tests wollen.
Wenn die Unit-Tests in der gleichen Datei wie der Code sind Sie testen mögen, löschen Sie einfach unerwünschten Modul aus dem globals()
Wörterbuch.
Hier ist ein ziemlich langwieriges Beispiel: Angenommen, Sie ein Modul impp.py
mit Inhalt haben:
value = 5
Nun, in der Testdatei können Sie schreiben:
>>> import impp
>>> print globals().keys()
>>> def printVal():
>>> print impp.value
['printVal', '__builtins__', '__file__', 'impp', '__name__', '__doc__']
Beachten Sie, dass impp
unter den Globals ist, weil es importiert wurde. Der Aufruf der Funktion printVal
die impp
Modul verwendet immer noch funktioniert:
>>> printVal()
5
Aber jetzt, wenn Sie impp
Schlüssel aus globals()
...
>>> del globals()['impp']
>>> print globals().keys()
['printVal', '__builtins__', '__file__', '__name__', '__doc__']
... und versuchen printVal()
zu nennen, die Sie erhalten:
>>> printVal()
Traceback (most recent call last):
File "test_imp.py", line 13, in <module>
printVal()
File "test_imp.py", line 5, in printVal
print impp.value
NameError: global name 'impp' is not defined
... was wahrscheinlich ist genau das, was Sie erreichen wollen.
Um es in Ihren Gerät-Tests zu verwenden, können Sie die Globals löschen, kurz bevor die Testsuite, z.B. in __main__
:
if __name__ == '__main__':
del globals()['impp']
unittest.main()
In Ihrem Kommentar oben , sagen Sie, Sie überzeugen wollen python, dass bestimmte Module importiert bereits hat. Dies scheint immer noch wie ein seltsames Ziel, aber wenn das wirklich das, was Sie tun wollen, grundsätzlich können Sie schleichen um hinter dem Rücken des Importmechanismus und sys.modules
ändern. Nicht sicher, wie this'd Arbeit für Paket-Importe, sollte aber für absolute Importe in Ordnung sein.