Domanda

Sono in un progetto in cui stiamo iniziando a refactoring di una base di codice enorme. Un problema che è emerso immediatamente è che ogni file importa molti altri file. Come posso deridere in modo elegante questo nel mio test di unità senza dover modificare il codice effettivo in modo da poter iniziare a scrivere test di unità?

Ad esempio: il file con le funzioni che voglio testare, importa altri dieci file che fanno parte del nostro software e non librerie core di Python.

Voglio essere in grado di eseguire i test unitari il più separatamente possibile e per ora ho intenzione di testare solo funzioni che non dipendono da cose dai file che vengono importati.

Grazie per tutte le risposte.

Non sapevo davvero cosa volevo fare dall'inizio, ma ora penso di saperlo.

Il problema era che alcune importazioni erano possibili solo quando l'intera applicazione era in esecuzione a causa di qualche auto-magia di terze parti. Quindi ho dovuto creare alcuni stub per questi moduli in una directory che ho sottolineato con sys.path

Ora posso importare il file che contiene le funzioni per cui voglio scrivere i test nel mio file unit-test senza lamentele sui moduli mancanti.

È stato utile?

Soluzione

Se vuoi importare un modulo e allo stesso tempo assicurarti che non importi nulla, puoi sostituire la funzione integrata __import__ .

Ad esempio, utilizzare questa classe:

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

E nel tuo codice di test, invece di scrivere import myModule , scrivi:

wrapper = ImportWrapper(__import__)
wrapper.mock_import('myModule', [])

Il secondo argomento per mock_import è un elenco di nomi di moduli che fai vuoi importare nel modulo interno.

Questo esempio può essere ulteriormente modificato ad es. importa un modulo diverso da quello desiderato invece di non importarlo, o addirittura di deridere l'oggetto del modulo con un tuo oggetto personalizzato.

Altri suggerimenti

Se vuoi davvero divertirti con il meccanismo di importazione di Python, dai un'occhiata al < codice> ihooks modulo. Fornisce strumenti per modificare il comportamento del __import__ integrato. Ma non è chiaro dalla tua domanda perché devi farlo.

" importa molti altri file " ;? Importa molti altri file che fanno parte della tua base di codice personalizzata? O importa molti altri file che fanno parte della distribuzione Python? O importa molti altri file di progetto open source?

Se le tue importazioni non funzionano, hai un " semplice " PYTHONPAT Problema H. Ottieni tutte le tue varie directory di progetto in un PYTHONPATH che puoi utilizzare per i test. Abbiamo un percorso piuttosto complesso, in Windows lo gestiamo in questo modo

@set Part1=c:\blah\blah\blah
@set Part2=c:\some\other\path
@set that=g:\shared\stuff
set PYTHONPATH=%part1%;%part2%;%that%

Manteniamo ogni parte del percorso separata in modo che (a) sappiamo da dove vengono le cose e (b) possiamo gestire il cambiamento quando spostiamo le cose.

Poiché il PYTHONPATH viene cercato in ordine, possiamo controllare ciò che viene utilizzato regolando l'ordine sul percorso.

Una volta che hai " tutto " ;, diventa una questione di fiducia.

O

  • ti fidi di qualcosa (ad es., la base di codice Python) e semplicemente lo importa

O

  • Non ti fidi di qualcosa (cioè il tuo codice) e tu

    1. testalo separatamente e
    2. deridilo per test autonomi.

Testereste le librerie di Python? Se è così, hai molto lavoro. Altrimenti, dovresti forse deridere solo le cose che stai per testare.

Non è necessaria alcuna manipolazione difficile se si desidera una soluzione rapida e sporca prima dei test unitari.

Se i test unitari si trovano nello stesso file del codice che si desidera testare, è sufficiente eliminare il modulo indesiderato dal dizionario globals () .

Ecco un esempio piuttosto lungo: supponiamo di avere un modulo impp.py con contenuti:

value = 5

Ora, nel tuo file di test puoi scrivere:

>>> import impp
>>> print globals().keys()
>>> def printVal():
>>>     print impp.value
['printVal', '__builtins__', '__file__', 'impp', '__name__', '__doc__']

Nota che impp è tra i globi, perché è stato importato. La chiamata della funzione printVal che utilizza il modulo impp funziona ancora:

>>> printVal()
5

Ma ora, se rimuovi la chiave impp da globals () ...

>>> del globals()['impp']
>>> print globals().keys()
['printVal', '__builtins__', '__file__', '__name__', '__doc__']

... e prova a chiamare printVal () , otterrai:

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

... che è probabilmente esattamente quello che stai cercando di ottenere.

Per utilizzarlo nei test unitari, è possibile eliminare i globi prima di eseguire la suite di test, ad es. in __main__ :

if __name__ == '__main__':
    del globals()['impp']
    unittest.main()

Nel tuo commento sopra , dici di voler convincere python che alcuni moduli sono già stati importati. Questo sembra ancora uno strano obiettivo, ma se è davvero quello che vuoi fare, in linea di principio puoi sgattaiolare dietro il meccanismo di importazione e cambiare sys.modules . Non sono sicuro di come funzionerebbe per le importazioni di pacchetti, ma dovrebbe andare bene per le importazioni assolute.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top