unittest unitarias pruebas prueba casos automatizadas python unit-testing refactoring python-import

casos - pruebas unitarias en python



Python, pruebas unitarias y burlas de importaciĆ³n (5)

Estoy en un proyecto en el que comenzamos a refacturar 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?

Como ejemplo: el archivo con las funciones que deseo probar, importa otros diez archivos que forman parte de nuestro software y no las librerías 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 elementos 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 era que algunas importaciones solo eran posibles cuando toda la aplicación se estaba ejecutando debido a la magia de un auto de terceros. Así que tuve que hacer algunos stubs para estos módulos en un directorio que señalé con sys.path

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


"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 PYTHONPAT H "simple". Obtenga todos sus diversos directorios de proyectos en una PYTHONPATH que puede usar para las pruebas. Tenemos un camino bastante complejo, en Windows lo gestionamos así

@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 vienen las cosas y (b) podemos gestionar el cambio cuando movemos las cosas.

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

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

Ya sea

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

O

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

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

¿Probarías 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.


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 la 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 extenso: supongamos que tiene un módulo impp.py con contenido:

value = 5

Ahora, en su archivo de prueba puede escribir:

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

Tenga en cuenta que impp está entre los globales, porque fue importado. Llamar a la función printVal que usa el módulo impp todavía 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 probablemente es exactamente lo que estás tratando de lograr.

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

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


Si realmente quieres 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.


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

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 para mock_import es una lista de nombres de módulos que desea importar en el módulo interno.

Este ejemplo se puede modificar para, por ejemplo, importar otro módulo distinto al deseado en lugar de simplemente no importarlo, o incluso burlar el objeto del módulo con algún objeto personalizado propio.


En tu comentario anterior , dices que quieres convencer a Python de que ciertos módulos ya se han importado. 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 los 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.