Pruebas unitarias parametrizadas en Python
- Propósito de las pruebas unitarias parametrizadas en Python
- Ejemplos de pruebas unitarias parametrizadas en Python
- Beneficios de las pruebas unitarias parametrizadas en Python
- Bibliotecas de Python que admiten pruebas unitarias parametrizadas
Las pruebas unitarias son una herramienta poderosa para mantener la calidad del software. Las pruebas unitarias en el software son una serie de controles en una pieza de código para garantizar que el software se desarrolle de acuerdo con las especificaciones de diseño del software y se comporte o responda según lo previsto para el usuario final.
En Python, podemos generar una prueba para cada elemento o caso de prueba sobre la marcha mediante una prueba unitaria parametrizada. Este artículo explorará las pruebas unitarias parametrizadas de Python.
Propósito de las pruebas unitarias parametrizadas en Python
Cuando los desarrolladores escriben pruebas unitarias, a menudo adoptan el enfoque de una prueba para un caso. Las pruebas más similares o relacionadas se combinan luego en una suite.
Mire el conjunto de pruebas en el siguiente código:
class TestSuite(TestCase):
def test_first_case(self):
assert example_test()
def test_second_case(self):
assert more_example_tests()
def test_third_case(self):
assert another_example_test()
Probar a través de conjuntos como estos significaría que cada caso de prueba debe pasar por una serie de etapas personalizadas de preparación, ejecución y, finalmente, aserción. Si cada uno de estos casos de prueba es similar, la suite resultante sería un lío de código duplicado o redundante, lo cual es nada menos que una maldición en el mundo de los desarrolladores de software.
Estas suites también dan como resultado grandes bases de código que son extremadamente difíciles de mantener cuando se realizan cambios (lo cual es de esperar). Para contrarrestar este problema, tenemos la bendición de las pruebas unitarias dinámicas o parametrizadas en Python, lo que nos permite generar una prueba singular para múltiples casos.
Ejemplos de pruebas unitarias parametrizadas en Python
Profundicemos en un escenario o método de prueba de ejemplo que podemos usar como base para comprender las pruebas unitarias parametrizadas en Python y cómo nos facilita la vida como desarrolladores de software.
A continuación se muestra una función simple que queremos probar:
def compute(a, b):
return (a + b) / (a * b)
Ahora, como cualquier prueba, sabemos que hay múltiples entradas que debemos aplicar a la función anterior para probar que se comporta de la manera deseada. Para especificar los parámetros de prueba de manera más elaborada, necesitamos probar el método contra parámetros que son:
- números enteros positivos
- enteros negativos
- enteros largos
- enteros regulares
- número flotante
- uno o ambos números son cero
- uno o ambos argumentos no están definidos
- uno o ambos argumentos son cadenas u otros tipos de objetos
Si esto fuera a probarse usando el método de una prueba por caso, estaríamos escribiendo una larga serie de código repetitivo para un método de una sola línea. Puede imaginar lo agitado y tedioso que puede ser esto para los métodos basados en múltiples líneas de código que involucran bucles y condiciones.
Con la parametrización, solo necesitamos hacer esto:
def test_compute(self, a, b, expected, raises=None):
if raises is not None:
with self.assertRaises(raises):
compute(a, b)
else:
assert compute(a, b) == expected
Con esto, necesitaríamos preparar una lista de parámetros que prueben mejor todas las posibilidades del método de cálculo. La lista de parámetros puede ser tan larga como queramos o simplemente una colección del caso que necesitamos probar.
Aquí hay un ejemplo de una lista de parámetros que se pueden pasar a la prueba unitaria anterior.
La salida del código:
a | b | expected | raises
------------+-----+----------+-------
-2 | 2 | 0 |
0 | 2 | | ZeroDivisionError
2 | 2 | 1 |
0.5 | 0.4 | 4.5 |
None | 2 | | TypeError
"10" | "1" | | TypeError
3000000000L | 2 | 0.5 |
Como puede verse, esta lista y la prueba unitaria parametrizada anterior hacen que sea mucho más eficiente para nosotros probar los diversos escenarios de argumentos que se pueden pasar al método compute()
. También hace que registrar los resultados variables sea mucho más fácil.
Beneficios de las pruebas unitarias parametrizadas en Python
Escribir pruebas unitarias regulares puede ser muy repetitivo, exhaustivo, lento y estresante, especialmente cuando queremos que las pruebas recorran cada escenario de software y garanticen que todo esté a la altura.
El uso de pruebas unitarias parametrizadas en Python significa que nosotros, como desarrolladores, no tenemos que preocuparnos por escribir cientos de líneas de código para probar los diversos escenarios que pueden surgir en el software que se está desarrollando para mantener su calidad. Todo lo que necesitamos es una sola prueba para probar todos los escenarios que necesitan ser probados.
Esto significa que la base del código de prueba siempre será fácil de mantener con poca o ninguna repetición, lo que dará como resultado casos de prueba eficientes y resultados que ahorrarán tiempo.
Además, las pruebas unitarias parametrizadas obligan al desarrollador en cuestión a pensar de manera más clara y definitiva sobre todas las entradas posibles que entran en el método. Ayuda a realizar un seguimiento de los casos extremos, como los números enteros grandes o los tipos de entrada inconsistentes para nuestro ejemplo anterior y cualquier caso erróneo, como las entradas indefinidas.
Esta prueba también obliga naturalmente al desarrollador a pensar más en cómo se pueden manejar los casos extremos en lugar de solo pensar en el tipo de entrada que puede romper su código. Ayuda a mantener la calidad del código al obligarnos a predecir qué se espera que haga la función en una combinación particular de circunstancias y conduce al desarrollo de soluciones conceptuales y eficientes para cualquier problema que se presente.
Para agregar a todo esto, si decidimos usar una lista más exhaustiva o extensa de parámetros para probar nuestros métodos, podemos terminar teniendo resultados y soluciones para casos de prueba que al principio pensamos que eran demasiado triviales para escribir un caso de prueba. Sin embargo, con una prueba parametrizada a nuestra disposición, solo necesitamos ingresar la combinación que queremos probar para ver cómo responde el código.
Bibliotecas de Python que admiten pruebas unitarias parametrizadas
Python ofrece hacernos la vida más fácil con la biblioteca parametrizada
, lo que facilita el desarrollo del método de prueba. Todo lo que tenemos que hacer es importar “parametrizado” y pasar los parámetros que queremos que se prueben a través del decorador “parametrizado” en nuestro método en el conjunto de pruebas de la siguiente manera:
from parameterized import parameterized
class TestSuite(TestCase):
@parameterized.expand([("test_1", 0, 0, 0), ("test_2", 0, 1, 1)])
def test_my_feature(self, name, in_1, in_2, expected):
assert my_feature(in_1, in_2) == expected
Otra forma de hacerlo es crear un archivo .csv
con una lista de todos los parámetros. Luego, se lo pasamos al decorador para que pruebe así:
def load_test_cases():
return load_from_csv("my_feature_parameters.csv")
class TestSuite(TestCase):
@parameterized.expand(load_test_cases)
def test_my_feature(self, name, in_1, in_2, expected):
assert my_feature(in_1, in_2) == expected
Hemos cubierto todo, desde por qué necesitamos usar pruebas unitarias dinámicas (parametrizadas) hasta las diferentes formas en que podemos implementarlas y sus beneficios en el ciclo de vida del desarrollo de software. Esperamos que este artículo le resulte útil para comprender cómo generar pruebas unitarias en Python.
My name is Abid Ullah, and I am a software engineer. I love writing articles on programming, and my favorite topics are Python, PHP, JavaScript, and Linux. I tend to provide solutions to people in programming problems through my articles. I believe that I can bring a lot to you with my skills, experience, and qualification in technical writing.
LinkedIn