Introducción a las pruebas unitarias en Python

Acaba de terminar de escribir un fragmento de código y se pregunta qué hacer. ¿Enviará una solicitud de extracción y hará que sus compañeros de equipo revisen el código? ¿O probará manualmente el código?

Debe hacer ambas cosas, pero con un paso adicional: debe realizar una prueba unitaria de su código para asegurarse de que funcione como se espera.

Las pruebas unitarias pueden aprobar o fallar, y eso las convierte en una gran técnica para verificar su código. En este tutorial, demostraré cómo escribir pruebas unitarias en Python y verás lo fácil que es ponerlas en marcha en tu propio proyecto.

Empezando

La mejor manera de entender las pruebas es hacerlo de forma práctica. Para ese propósito, en un archivo llamado name_function.py, escribiré una función simple que toma un nombre y apellido, y devuelve un nombre completo:

#Generate a formatted full name def formatted_name(first_name, last_name): full_name = first_name + ' ' + last_name return full_name.title()

La función formatted_name () toma el nombre y el apellido y los combina con un espacio entre para formar un nombre completo. Luego, escribe en mayúscula la primera letra de cada palabra. Para comprobar que este código funciona, debe escribir algún código que utilice esta función. En names.py escribiré un código simple que permita a los usuarios ingresar su nombre y apellido:

from name_function import formatted_name print("Please enter the first and last names or enter x to E[x]it.") while True: first_name = input("Please enter the first name: ") if first_name == "x": print("Good bye.") break last_name = input("Please enter the last name: ") if last_name == "x": print("Good bye.") break result = formatted_name(first_name, last_name) print("Formatted name is: " + result + ".")

Este código importa formatted_name () de name_function.py y, al ejecutarlo, permite al usuario ingresar una serie de nombres y apellidos y muestra los nombres completos formateados.

Prueba unitaria y casos de prueba

Hay un módulo en la biblioteca estándar de Python llamado unittest que contiene herramientas para probar su código. Las pruebas unitarias verifican si todas las partes específicas del comportamiento de su función son correctas, lo que facilitará mucho su integración con otras partes.

El caso de prueba es una colección de pruebas unitarias que, en conjunto, demuestran que una función funciona según lo previsto, dentro de una gama completa de situaciones en las que esa función puede encontrarse y que se espera que maneje. El caso de prueba debe considerar todos los tipos posibles de entrada que una función podría recibir de los usuarios y, por lo tanto, debe incluir pruebas para representar cada una de estas situaciones.

Pasando una prueba

Aquí hay un escenario típico para escribir pruebas:

Primero necesitas crear un archivo de prueba. Luego, importe el módulo unittest, defina la clase de prueba que hereda de unittest.TestCase y, por último, escriba una serie de métodos para probar todos los casos del comportamiento de su función.

Hay una explicación línea por línea debajo del siguiente código:

import unittest from name_function import formatted_name class NamesTestCase(unittest.TestCase): def test_first_last_name(self): result = formatted_name("pete", "seeger") self.assertEqual(result, "Pete Seeger")

Primero, necesita importar una prueba unitaria y la función que desea probar, formatted_name (). Luego, crea una clase, por ejemplo, NamesTestCase, que contendrá pruebas para su función formatted_name (). Esta clase hereda de la clase unittest.TestCase.

NamesTestCase contiene un método único que prueba una parte de formatted_name (). Puede llamar a este método test_first_last_name ().

Recuerde que todos los métodos que comienzan con "test_" se ejecutarán automáticamente cuando ejecute test_name_function.py.

Dentro del método de prueba test_first_last_name (), llama a la función que desea probar y almacena un valor de retorno. En este ejemplo vamos a llamar a formatted_name () con los argumentos "pete" y "seeger", y almacenaremos el resultado en la variable resultante.

En la última línea usaremos el método assert. El método de aserción verifica que un resultado que recibió coincide con el resultado que esperaba recibir. Y en este caso sabemos que la función formatted_name () devolverá el nombre completo con las primeras letras en mayúscula, por lo que esperamos el resultado "Pete Seeger". Para comprobar esto, se está utilizando el método assertEqual () de unittest.

self.assertEqual(result, “Pete Seeger”)

Esta línea básicamente significa: Compare el valor en la variable resultante con “Pete Seeger” y si son iguales, está bien, pero si no, hágamelo saber.

Al ejecutar test_name_function.py, se espera que obtenga un OK, lo que significa que la prueba ha pasado.

Ran 1 test in 0.001s OK

Reprobar una prueba

Para mostrarle cómo se ve una prueba fallida, voy a modificar una función formatted_name () incluyendo un nuevo argumento de segundo nombre.

Así que voy a reescribir la función para que se vea así:

#Generate a formatted full name including a middle name def formatted_name(first_name, last_name, middle_name): full_name = first_name + ' ' + middle_name + ' ' + last_name return full_name.title()

Esta versión de formatted_name () funcionará para personas con segundos nombres, pero cuando la pruebe, verá que la función no funciona para las personas que no tienen un segundo nombre.

Entonces, cuando ejecute test_name_function.py obtendrá el resultado que se parece a esto:

Error Traceback (most recent call last): File “test_name_function.py”, line 7, in test_first_last_name result = formatted_name(“pete”, “seeger”) TypeError: formatted_name() missing 1 required positional argument: ‘middle_name’ Ran 1 test in 0.002s FAILED (errors=1)

En el resultado, verá información que le dirá todo lo que necesita saber dónde falla la prueba:

  • El primer elemento de la salida es el Error que le indica que al menos una prueba en el caso de prueba resultó en un error.
  • A continuación, verá el archivo y el método en el que se produjo el error.
  • Después de eso, verá la línea en la que ocurrió el error.
  • Y qué tipo de error es, en este caso nos falta 1 argumento "middle_name".
  • También verá la cantidad de pruebas ejecutadas, el tiempo necesario para que las pruebas se completen y un mensaje de texto que representa el estado de las pruebas con la cantidad de errores que ocurrieron.

Qué hacer cuando la prueba falla

Una prueba satisfactoria significa que la función se comporta de acuerdo con lo que se espera de ella. Sin embargo, una prueba fallida significa que hay más diversión por delante.

He visto un par de programadores que prefieren cambiar la prueba en lugar de mejorar el código, pero no lo hagan. Dedique un poco más de tiempo a solucionar el problema, ya que le ayudará a comprender mejor el código y a ahorrar tiempo a largo plazo.

In this example, our function formatted_name() first required two  parameters, and now as it is rewritten it requires one extra: a middle name. Adding a middle name to our function broke the desired behavior of  it. Since the idea is not to make changes to the tests, the best solution is to make middle name optional.

After we do this the idea is to make the tests pass when the first and last name are used, for example “Pete Seeger”, as well as when first, last and middle names are used, for example “Raymond Red Reddington”. So  let’s modify the code of formatted_name() once again:

#Generate a formatted full name including a middle name def formatted_name(first_name, last_name,): if len(middle_name) > 0: full_name = first_name + ' ' + middle_name + ' ' + last_name else: full_name = first_name + ' ' + last_name return full_name.title()

Now the function should work for names with and without the middle name.

And to make sure it still works with “Pete Seeger” run the test again:

Ran 1 test in 0.001s OK
Y esto es lo que pretendía mostrarte: siempre es mejor hacer cambios en tu código para que se ajuste a tus pruebas que al revés. Ahora ha llegado el momento de agregar una nueva prueba para los nombres que tienen un segundo nombre.

Agregar nuevas pruebas

Escriba un nuevo método para la clase NamesTestCase que probará los segundos nombres:

import unittest from name_function import formatted_name class NamesTestCase(unittest.TestCase): def test_first_last_name(self): result = formatted_name("pete", "seeger") self.assertEqual(result, "Pete Seeger") def test_first_last_middle_name(self): result = formatted_name("raymond", "reddington", "red") self.assertEqual(result, "Raymond Red Reddington")

Después de ejecutar la prueba, ambas pruebas deben pasar:

Ran 2 tests in 0.001s OK
Sujetador gjort!

¡Bien hecho!

Ha escrito sus pruebas para verificar si la función funciona usando nombres con o sin un segundo nombre. Estén atentos a la parte 2, donde hablaré más sobre las pruebas en Python.

¡Gracias por leer! Vea más artículos como este en mi perfil de freeCodeCamp: //www.freecodecamp.org/news/author/goran/ y otras cosas divertidas que creo en mi página de GitHub: //github.com/GoranAviani