1 usuario conectado
Pruebas en Python.Aislamiento y control con Mocking
Pruebas en Python.Aislamiento y control con Mocking
En el ámbito de las pruebas unitarias, un desafío
común surge cuando el código que queremos probar depende de otros componentes o
servicios que son difíciles de controlar, lentos, no deterministas o aún no
están implementados. Aquí es donde entra en juego la técnica de mocking.
Mocking consiste en reemplazar
las dependencias reales de nuestro código bajo prueba con objetos simulados (los
"mocks"). Estos mocks tienen un comportamiento predefinido que podemos controlar
y verificar durante la ejecución de la prueba. Al aislar la unidad de código que
estamos probando de sus dependencias, podemos centrarnos en verificar su lógica
interna sin vernos afectados por el comportamiento o el estado de los
componentes externos.
Python proporciona la librería
unittest.mock
(a partir de Python 3.3, anteriormente disponible
como un módulo de terceros llamado mock
) para facilitar la
creación y el uso de mocks en nuestras pruebas.
¿Por qué usar Mocking?
- Aislamiento: Permite
probar una unidad de código de forma aislada, sin depender del estado o
comportamiento real de sus dependencias.
- Velocidad: Las dependencias
externas (como bases de datos, APIs, sistemas de archivos) pueden ser
lentas. Los mocks son rápidos y eficientes.
- Determinismo: Las
dependencias reales pueden tener un comportamiento no determinista (por
ejemplo, respuestas de una API que varían). Los mocks permiten definir un
comportamiento predecible.
- Prueba de casos difíciles:
Facilita la prueba de escenarios que serían difíciles o imposibles de
simular con las dependencias reales (por ejemplo, errores de red, respuestas
específicas de una API).
- Desarrollo paralelo: Permite
probar código que depende de componentes que aún no están completamente
implementados.
Conceptos Clave de unittest.mock
:
-
Mock
: La clase
principal para crear objetos simulados. Un Mock
es un
objeto dinámico que puede tener atributos y métodos definidos sobre la
marcha.
-
MagicMock
: Una
subclase de Mock
que proporciona implementaciones por
defecto para la mayoría de los métodos mágicos de Python (como
__str__
, __len__
, __iter__
,
etc.), lo que la hace más útil en muchos escenarios.
-
patch
: Un
decorador o context manager que permite reemplazar temporalmente objetos en
un módulo o clase con mocks durante la ejecución de una prueba. Es una forma
conveniente de inyectar mocks en el código bajo prueba.
-
Configuración de Comportamiento:
Podemos configurar el comportamiento de nuestros mocks definiendo sus
valores de retorno (return_value
), lanzando excepciones (side_effect
),
o definiendo qué se devuelve cuando se acceden a sus atributos o se llaman a
sus métodos.
-
Verificación de Interacciones:
Una de las funcionalidades clave del mocking es la capacidad de verificar
cómo interactuó el código bajo prueba con sus dependencias mockeadas.
Podemos verificar si se llamaron ciertos métodos, cuántas veces se llamaron
y con qué argumentos.
Ejemplo Básico:
Supongamos que tenemos una función
obtener_datos_remotos(url)
que hace una llamada a una API externa y
devuelve los datos:
Para probar una función que utiliza
obtener_datos_remotos
, sin hacer una llamada real a la API, podemos
mockear la función requests.get
:
En este ejemplo:
- Utilizamos el decorador
@patch('mi_modulo.requests.get')
para reemplazar temporalmente la
función requests.get
con un mock (mock_get
).
- En
test_obtener_datos_exito
,
configuramos el return_value
del
mock para simular una respuesta exitosa de la API y verificamos que la
función bajo prueba devuelve los datos esperados y que
requests.get
fue llamada con la URL correcta.
- En
test_obtener_datos_error
,
configuramos el side_effect
del método raise_for_status
del mock para simular una excepción HTTP y verificamos que la función bajo
prueba la propaga.
Conclusión:
La librería unittest.mock
es una
herramienta esencial para escribir pruebas unitarias efectivas en Python,
especialmente cuando el código bajo prueba interactúa con dependencias externas.
Al utilizar mocks, podemos aislar nuestro código, acelerar las pruebas, simular
escenarios complejos y verificar las interacciones con las dependencias, lo que
conduce a pruebas más robustas y un software de mayor calidad. Dominar el arte
del mocking es una habilidad valiosa para cualquier desarrollador de Python que
se tome en serio la calidad de su código.