Pregunta

Estoy en un proyecto en el que estamos comenzando a refactorizar un código de base masiva. Un problema que surgió de inmediato es que cada archivo importa muchos otros archivos. ¿Cómo puedo simular esto de manera elegante en mi prueba de unidad sin tener que alterar el código real para poder comenzar a escribir pruebas de unidad?

Como ejemplo: el archivo con las funciones que deseo probar, importa otros diez archivos que forman parte de nuestro software y no de Python Core Libs.

Quiero poder ejecutar las pruebas unitarias lo más separadas 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.

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

El problema fue que algunas importaciones solo eran posibles cuando toda la aplicación se estaba ejecutando debido a la magia automática de terceros. Así que tuve que hacer algunos apéndices para estos módulos en un directorio que señalé con sys.path

Ahora puedo importar el archivo que contiene las funciones para las que quiero escribir pruebas en mi archivo de prueba de unidad sin quejas de que faltan módulos.

¿Fue útil?

Solución

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

Por ejemplo, usa 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 , escriba:

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

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

Este ejemplo puede modificarse aún más, por ejemplo, importe otro módulo del deseado en lugar de simplemente no importarlo, o incluso burlándose del objeto del módulo con un objeto personalizado propio.

Otros consejos

Si realmente quieres jugar con el mecanismo de importación de Python, echa un vistazo a < código> ihooks módulo. Proporciona herramientas para cambiar el comportamiento del __import__ incorporado. Pero de su pregunta no queda claro por qué necesita hacer esto.

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

Si sus importaciones no funcionan, tiene un " simple " PYTHONPAT H problema. Obtenga todos los directorios de su proyecto en un PYTHONPATH que puede usar para realizar pruebas. Tenemos una ruta bastante compleja, en Windows la administramos de esta manera

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

Mantenemos cada parte del camino separada para que (a) sepamos de dónde provienen las cosas y (b) podemos manejar el cambio cuando movemos las cosas.

Como se busca en orden el PYTHONPATH , podemos controlar qué se usa al ajustar el orden en la ruta.

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

O bien

  • confías en algo (es decir, en la base del código Python) y simplemente lo importas.

O

  • No confías en algo (es decir, en tu propio código) y en ti

    1. probarlo por separado y
    2. burlarse de él para pruebas independientes.

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

No es necesaria una manipulación difícil si quieres una solución rápida y sucia antes de tus pruebas unitarias.

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 () .

Este es un ejemplo bastante largo: supongamos que tiene un módulo impp.py con contenido:

value = 5

Ahora, en tu archivo de prueba puedes 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 los globales, porque se importó. La función printVal que utiliza el módulo impp aún funciona:

>>> printVal()
5

Pero ahora, si elimina la clave impp de globals () ...

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

... e intenta llamar a printVal () , obtendrás:

>>> 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 estás tratando de lograr.

Para usarlo en tus pruebas unitarias, puedes eliminar las variables globales justo antes de ejecutar el conjunto de pruebas, por ejemplo. en __main__ :

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top