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.

War es hilfreich?

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“ PYTHONPATH 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

    1. testen Sie es separat und
    2. 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() ...

entfernen
>>> 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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top