Excepción de aumento simulado de Python
-
Lanzar una excepción al usar la biblioteca de pruebas unitarias
unittest
en Python - Generar manualmente una excepción de una función
El objetivo principal de este artículo es demostrar cómo lanzar una excepción cuando se utiliza la biblioteca de pruebas unitarias unittest
.
Lanzar una excepción al usar la biblioteca de pruebas unitarias unittest
en Python
Para verificar que nuestro código sea estable y represente la mayoría de los casos de uso en el alcance del proyecto, debemos probarlo dándole entradas variadas. Una de las librerías presentes en Python, unittest
, ofrece dicha funcionalidad, permitiendo escribir y realizar diferentes casos de prueba.
Durante las pruebas, es necesario poder manejar los casos en los que la entrada es inesperada. Podemos manejar tales casos lanzando una excepción que detalla qué está mal con la entrada dada.
Considere el siguiente código:
class A:
def __init__(self):
pass
def computeData(self):
return 0
class B:
def method_to_test():
obj = A()
try:
print(obj.computeData())
except Exception as e:
print("Exception at method_to_test: " + str(e))
Digamos que la función que queremos probar es method_to_test
. Inicializa el objeto de otra clase A
y luego llama a uno de sus métodos llamado computeData
en un bloque try-except
para detectar cualquier excepción.
Si queremos probar el comportamiento de computeData
cuando se lanza una excepción específica, tenemos que hacerlo usando el módulo unittest
.
Generar excepción una vez
Teniendo en cuenta el escenario mencionado anteriormente, también puede darse el caso de que queramos tener más control sobre cuándo lanzar una excepción.
Puede haber muchas razones por las que puede surgir tal necesidad. Dependiendo de la implementación de su código, tener más control sobre el comportamiento puede ser muy útil.
Afirmar excepción
Mientras escribe casos de prueba, es posible que desee verificar si sus excepciones se activan o no. Esto es especialmente útil cuando desea saber si se lanzó una excepción o no.
Esto puede ayudarlo a detectar antes en sus casos de prueba si algo sale mal.
Módulos de terceros simulados
Los módulos de terceros, dependiendo de nuestra implementación, pueden variar desde casi ninguno hasta muchos. Para probar si un módulo de terceros se comporta según lo previsto, también debemos probar su comportamiento con salidas variadas.
Un ejemplo sería request
, un módulo muy utilizado cuando se comunica a través de una red o se envían/reciben datos mediante HTTP.
Generar manualmente una excepción de una función
Para lanzar manualmente una excepción desde la función computeData
, tenemos que usar el atributo side_effect
.
Considere el siguiente código:
import unittest
from unittest.mock import patch
from unittest.mock import MagicMock
class A:
def __init__(self) -> None:
pass
def computeData(self):
return 0
class B:
def method_to_test():
obj = A()
try:
print(obj.computeData())
except Exception as e:
print("Exception at method_to_test: " + str(e))
class Test(unittest.TestCase):
@patch.object(A, "computeData", MagicMock(side_effect=Exception("Test")))
def test_method(self):
B.method_to_test()
if __name__ == "__main__":
Test().test_method()
Producción :
Exception at method_to_test: Test
En la solución, se crea un nuevo método, test_method
, para probar el método method_to_test
. También es importante tener en cuenta que la función está decorada con patch.object
.
El decorador parche
se proporciona en el módulo para parchear módulos y atributos de nivel de clase. Para ser más específicos sobre qué parchear, usamos patch.object
en lugar de patch
para parchear el método directamente.
En el decorador, se pasa el nombre de clase A
. Esto indica que el objeto que se va a parchear es una parte de A
con el nombre de miembro computeData
que se pasa.
El último parámetro es un objeto MagicMock
, una subclase de Mock
, donde asociamos nuestro comportamiento requerido a computeData
usando el atributo side_effect
, que es lanzar una excepción en nuestro caso.
El flujo general del programa es el siguiente:
- Se llama al
test_method
. - Se parchea el
computeData
de la claseA
. Se asigna unside_effect
, que en nuestro caso es una excepción. - Se llama al
método_a_probar
de la claseB
. - Se llama al constructor de la clase
A
, con la instancia almacenada enobj
. - Se llama a
computeData
. Debido a que el método está parcheado, se lanza una excepción.
Generar una excepción una vez
Para generar la excepción solo una vez o tener más control sobre el comportamiento de la función, side_effect
admite más que solo una llamada de función.
Actualmente, side_effect
admite:
Iterable
Invocable
Excepción
(Instancia o Clase)
Usando Iterable
, podemos hacer que el método solo genere una excepción una vez.
class Test(unittest.TestCase):
@patch.object(A, "computeData", MagicMock(side_effect=[1, Exception("Test"), 3]))
def test_method(self):
B.method_to_test()
if __name__ == "__main__":
testClass = Test()
testClass.test_method()
testClass.test_method()
testClass.test_method()
Producción :
1
Exception at method_to_test: Test
3
Debido al Iterable
pasado, los elementos de nuestra elección se parchean y ganamos control sobre cuándo generar excepciones. Por lo tanto, la excepción solo se lanza la segunda vez que se llama a la función, y 1
y 3
se devuelven en otros casos.
Afirmar excepción
Para determinar si ocurrió una excepción específica o no, podemos usar unittest.TestCase.assertRaises
en caso de que no se produzca nuestra excepción deseada.
class Test(unittest.TestCase):
@patch.object(A, "computeData", MagicMock(side_effect=Exception("Test")))
def test_method(self):
self.assertRaises(KeyError, A.computeData)
if __name__ == "__main__":
Test().test_method()
Producción :
Exception at method_to_test: Test
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 28, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 25, in test_method
self.assertRaises(KeyError, A.computeData)
File "C:\Program Files\Python310\lib\unittest\case.py", line 738, in assertRaises
return context.handle('assertRaises', args, kwargs)
File "C:\Program Files\Python310\lib\unittest\case.py", line 201, in handle
callable_obj(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1104, in __call__
return self._mock_call(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1108, in _mock_call
return self._execute_mock_call(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1163, in _execute_mock_call
raise effect
File "d:\Python Articles\a.py", line 17, in method_to_test
Exception: Test
Estamos haciendo cambios menores a nuestro método test_method
, llamando a assertRaises
con dos parámetros, lo que resultó en el resultado anterior.
Los dos parámetros se pueden definir como:
KeyError
: aquí se pasa cualquier excepción que desee desencadenar.A.computeData
- El método que se supone que lanza la excepción.
Módulos de terceros simulados
Para burlarse de un post
, como antes, debes usar el decorador parche
. Un ejemplo sencillo puede ser el siguiente:
class Test(unittest.TestCase):
@patch(
"requests.post", MagicMock(side_effect=requests.exceptions.ConnectionError())
)
def test_method(self):
requests.post()
Producción :
Traceback (most recent call last):
File "d:\Python Articles\a.py", line 33, in <module>
Test().test_method()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
File "d:\Python Articles\a.py", line 30, in test_method
requests.post()
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1104, in __call__
return self._mock_call(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1108, in _mock_call
return self._execute_mock_call(*args, **kwargs)
File "C:\Program Files\Python310\lib\unittest\mock.py", line 1163, in _execute_mock_call
raise effect
requests.exceptions.ConnectionError
Las funciones de módulos de terceros también se pueden parchear utilizando el decorador patch
, lo que permite una mejor depuración y más control sobre la escala de confiabilidad del producto/programa.
Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.
LinkedInArtículo relacionado - Python Exception
- Manejo de excepciones de archivos abiertos en Python
- Relanzar excepción en Python
- Generar excepción en Python
- Python except Exception as e
- Python assert Excepción