2008-10-07 17 views
12

Estoy en un proyecto en el que estamos comenzando a refaccionar una base de código masiva. Un problema que surgió inmediatamente es que cada archivo importa muchos otros archivos. ¿Cómo me burlo de manera elegante de esto en mi prueba de unidad sin tener que alterar el código real para poder comenzar a escribir pruebas unitarias?Python, pruebas unitarias y burlas de importaciones

Como un ejemplo: el archivo con las funciones que deseo probar, importa otros diez archivos que forman parte de nuestro software y no de las bibliotecas básicas de python.

Quiero poder ejecutar las pruebas unitarias lo más separadamente posible y por ahora solo voy a probar funciones que no dependen de las cosas de los archivos que se importan.

Gracias por todas las respuestas.

No sabía realmente lo que quería hacer desde el principio, pero ahora creo que sé.

El problema fue que algunas importaciones solo fueron posibles cuando toda la aplicación se estaba ejecutando debido a la magia de un auto de terceros. Así que tuve que crear algunos stubs para estos módulos en un directorio que señalé con sys.path

Ahora puedo importar el archivo que contiene las funciones que quiero escribir pruebas en mi archivo de prueba unitaria sin quejas sobre módulos faltantes

Respuesta

7

Si desea importar un módulo y, al mismo tiempo, asegurarse de que no importa nada, puede reemplazar la función integrada __import__.

Por ejemplo, el uso de esta clase:

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 

Y en su código de prueba, en lugar de escribir import myModule, escribe:

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

El segundo argumento de mock_import es una lista de nombres de módulo do desea importar en el módulo interno.

Este ejemplo se puede modificar más a.importar otro módulo de lo deseado en lugar de simplemente no importarlo, o incluso burlar el objeto del módulo con algún objeto personalizado propio.

+0

Desea agregar 'try: finally:' en el método mock_import, para evitar salir del sistema con la importación envuelta en lugar de la predeterminada en caso de error – Yonatan

+0

@Yonatan: ¡tiene razón, gracias! Modifiqué mi código – DzinX

1

"importa muchos otros archivos"? Importa una gran cantidad de otros archivos que forman parte de su base de código personalizado? ¿O importa una gran cantidad de otros archivos que forman parte de la distribución de Python? ¿O importa muchos otros archivos de proyectos de código abierto?

Si sus importaciones no funcionan, tiene un problema "simple" PYTHONPAT H. Obtenga todos sus directorios de proyectos diferentes en un PYTHONPATH que puede usar para probar. Tenemos un camino bastante compleja, en Windows manejamos como esto

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

Mantenemos cada pieza de la trayectoria separada para que (a) sabemos dónde vienen las cosas y (b) pueden gestionar el cambio cuando nos movemos cosas alrededor.

Dado que el PYTHONPATH se busca en orden, podemos controlar lo que se usa ajustando el orden en la ruta.

Una vez que tiene "todo", se convierte en una cuestión de confianza.

De cualquier

  • que confiar en algo (es decir, la base de código Python) y acaba de importarlo.

O

  • Usted no confía en algo (es decir, su propio código) y

    1. prueba por separado y
    2. se burlan de él para la prueba independiente.

se podría probar las bibliotecas de Python? Si es así, tienes mucho trabajo.Si no, entonces, tal vez solo deberías burlarte de las cosas que realmente vas a probar.

1

Si realmente quiere perder el tiempo con el mecanismo de importación de Python, eche un vistazo al módulo ihooks. Proporciona herramientas para cambiar el comportamiento del __import__ incorporado. Pero no está claro por su pregunta por qué necesita hacer esto.

0

No es necesaria una manipulación difícil si quiere una solución rápida y sucia antes de las pruebas de su unidad.

Si las pruebas de unidad están en el mismo archivo que el código que desea probar, simplemente elimine el módulo no deseado del diccionario globals().

Aquí es un poco larga ejemplo: suponga que tiene un módulo de impp.py con contenidos:

value = 5 

Ahora, en su archivo de prueba se puede escribir:

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

Tenga en cuenta que impp se encuentra entre el globales, porque fue importado. Llamando a la función printVal que utiliza impp módulo sigue funcionando:

>>> printVal() 
5 

Pero ahora, si se quita impp clave de globals() ...

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

... y tratar de llamar printVal(), podrás consiga:

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

... que es probablemente lo que usted está intentando lograr.

Para utilizarlo en las pruebas unitarias, puede eliminar las globales antes de ejecutar el banco de pruebas, p. en __main__:

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

Gracias por su respuesta, aprendí mucho de ello. Pero en realidad es todo lo contrario que quiero hacer. Las pruebas están en otro archivo y cuando importo el archivo que contiene las funciones que deseo probar, quiero que ese archivo crea que todos los demás archivos ya se importaron. –

0

En su comentario above, usted dice que quiere convencer pitón que ciertos módulos ya han sido importados. Esto todavía parece un objetivo extraño, pero si eso es lo que realmente quieres hacer, en principio puedes escabullirte detrás del mecanismo de importación y cambiar sys.modules. No estoy seguro de cómo esto funcionaría para las importaciones de paquetes, pero debería estar bien para las importaciones absolutas.