Función simulada en Python
La función unittest.mock
o Mock
es una biblioteca para pruebas en Python que le permite reemplazar componentes de su sistema bajo prueba con objetos mock
y hacer afirmaciones sobre cómo se han utilizado las partes.
El unittest.mock
brinda una clase central Mock
, eliminando la necesidad de crear una gran cantidad de stubs en todo su conjunto de pruebas.
Después de ejecutar un proceso, puede afirmar qué métodos o atributos se usaron y los argumentos con los que se llamaron. También puede especificar los valores de retorno y establecer los atributos necesarios de la forma habitual.
Además, Mock
proporciona un decorador patch()
que puede manejar el módulo de aplicación de parches y los atributos de nivel de clase dentro del alcance de la prueba, junto con un asistente centinela
, para crear objetos únicos.
Mock
se creó para usar con unittest
y se basa en el patrón acción a afirmación
en lugar de grabación a reproducción
, que se usa en la mayoría de los marcos de simulación. Hay un backport de unittest.mock
para versiones anteriores de Python.
Configurar e instalar Pytest Mock en Python
A diferencia de unittest
, pytest
no es un paquete integrado de Python y necesita instalación. Esto se puede instalar ingresando el siguiente comando en la terminal.
pip install pytest
Como nota, las mejores prácticas para unittest
también se aplican a pytest
, de modo que:
- Se requiere un directorio
tests/
para contener todas las pruebas unitarias. - Los nombres de los archivos siempre deben comenzar con
tests_
. - Los nombres de las funciones siempre deben comenzar con
la prueba
.
Se deben observar los estándares de nomenclatura para que el verificador pueda encontrar las pruebas unitarias realizadas.
Es necesario instalar pytest-mock
antes de poder usarlo. A continuación se muestra cómo instalarlo usando pip
:
pip install pytest-mock
Este es un complemento para Pytest. Por lo tanto, si aún no lo ha hecho, instalará Pytest.
Simular una función en Python
Para comenzar, crea una función simple get_os()
para decirnos si estamos usando Windows o Linux como sistema operativo.
Cree un archivo llamado app.py
de la siguiente manera:
from time import sleep
def isWindows():
# This sleep can be some complex operation
sleep(5)
return True
def get_os():
return "Windows is the current OS" if isWindows() else "Linux is the current OS"
Esta función utiliza la función isWindows
para determinar si el sistema operativo actual es Windows o no. Suponiendo que esta función isWindows
es bastante compleja y tarda varios segundos en ejecutarse, aquí podemos simular esta función lenta poniendo el programa en suspensión durante 5 segundos cada vez que se llama.
Lo siguiente sería un pytest
para la función get_os()
:
Cree un archivo llamado test_app.py
a pytest
:
from app import get_os
def test_get_os():
assert get_os() == "Windows is the current OS"
Dado que get_os()
llama a una función más lenta isWindows
, la prueba continúa siendo lenta. Esto se puede ver en el siguiente resultado de la ejecución de pytest
.
Tardó 5,02 segundos y puede variar según la instancia actual.
Las pruebas unitarias deben ser rápidas y deberíamos poder realizar cientos de pruebas en cuestión de segundos. El conjunto de pruebas se ralentiza con una sola prueba que tarda 5 segundos, por lo que aplicar burlas nos facilita la vida.
Si parcheamos la función lenta, podemos verificar el comportamiento de la función get_os()
sin esperar 5 segundos.
Simulamos esta función con pytest-mock
.
Pytest-mock
proporciona un accesorio llamado mocker
y una excelente interfaz además de las construcciones de simulación integradas de Python. Puede emplear un simulador invocando las funciones de simulación y parche y enviándolos como argumentos a su función de prueba.
En caso de que desee que la función isWindows
devuelva True
sin esperar esos preciados 5 segundos, podemos parchearlo de la siguiente manera:
mocker.patch("app.isWindows", return_value=True)
Aquí, debe referirse a isWindows
como app.isWindows
, dado que es la función en el módulo app
. Si solo parcheamos isWindows
, intentará parchear una función llamada isWindows
en el archivo test_app
, que no existe.
El formato siempre es <nombre_módulo>.<nombre_función>
, y saber cómo simular correctamente es fundamental.
A continuación se describe la función de prueba modificada después del parche:
from app import get_os
# using'mocker' fixture provided by pytest-mock
def test_get_os(mocker):
# mocking the slow-going function and returning True always
mocker.patch("app.isWindows", return_value=True)
assert get_os() == "Windows is the current OS"
Ahora, esta prueba terminará mucho más rápido cuando la ejecutes.
Como puede ver, la prueba tomó solo 0,02 segundos, por lo que hemos parcheado con éxito la función lenta y acelerado el conjunto de pruebas.
Otra ventaja de la simulación es que puede hacer que la función de simulación devuelva cualquier cosa e incluso puede generar errores para probar cómo se procesa su código en esos escenarios.
Aquí, si desea probar el caso en el que isWindows
devuelve False
, escriba la siguiente prueba:
from app import get_os
def test_os_isLinux(mocker):
mocker.patch(
"app.isWindows", return_value=False
) # set the return value to be False
assert get_os() == "Linux is the current OS"
Los simulacros y parches que vienen con el simulador tienen un ámbito de función, lo que significa que solo se pueden usar con esa función en particular. Como resultado, no habrá conflictos entre parches para la misma función en diferentes pruebas.
Conclusión
La simulación es la práctica de reemplazar el componente de la aplicación que está probando con un simulacro, que es una implementación ficticia de ese componente. A través de la simulación, podemos obtener beneficios como una mayor velocidad, donde las pruebas que se ejecutan más rápido son muy beneficiosas, evitando efectos secundarios no deseados durante las pruebas, etc.
Nimesha is a Full-stack Software Engineer for more than five years, he loves technology, as technology has the power to solve our many problems within just a minute. He have been contributing to various projects over the last 5+ years and working with almost all the so-called 03 tiers(DB, M-Tier, and Client). Recently, he has started working with DevOps technologies such as Azure administration, Kubernetes, Terraform automation, and Bash scripting as well.