Pruebas unitarias: un enfoque simplista y lingüístico-agnóstico

"No hay problema con los pulgares arriba" a través de Giphy

Hoy en día, las pruebas unitarias son una habilidad requerida para un ingeniero de software. Pero, muchos de ellos no lo saben, y piensan que es algo de otro mundo.

El hecho es:

Las pruebas unitarias son solo algunas líneas de código escritas para garantizar que otras líneas de códigos funcionen como se esperaba.

Estas líneas de código probadas son las características de su aplicación. Eso es todo, no hay ningún tipo de magia.

No quiero asustar a nadie. Por lo tanto, no utilizaré bibliotecas, estándares, clases y palabras clave. Trataré de explicar las pruebas unitarias de una manera simplista. Usando código puro, sin importar clases o bibliotecas. Los ejemplos están en Python.

Motivación

¿Por qué tenemos que hacer pruebas unitarias? Las pruebas unitarias son la forma más sencilla de evitar o advertirle de problemas. Como cuando algún cambio de código rompe otra funcionalidad de la aplicación.

Veamos un código por ejemplo:

Como esperábamos, la función get_company_as_string funciona bien. Aquí hay ejemplos de algunos valores y los retornos respectivos:

 En [1]: get_company_as_string ('Samsung') 
Fuera [1]: 'Nombre: Samsung | Fundadores: Lee Byung-chul. '
 En [2]: get_company_as_string ('Apple Inc.') 
Fuera [2]: 'Nombre: Apple Inc. | Fundadores: Steve Jobs, Steve Wozniak y Ronald Wayne.
 En [3]: get_company_as_string ('Microsoft') 
Fuera [3]: 'Nombre: Microsoft | Fundadores: Bill Gates, Paul Allen.
 En [4]: ??get_company_as_string ('XPTO') 
 En [5]: 

Pero, si, ¿necesitamos crear una función que devuelva la suma de las edades de los fundadores en la base de la compañía? Entonces, necesitamos ajustar la estructura de datos para que la lista de fundadores mantenga su edad.

Después de eso, tenemos el siguiente código. Esa es la base del código anterior, con una modificación en la estructura de los fundadores y una función más nueva:

Y, cuando ejecutamos nuestra nueva función, tenemos los siguientes resultados:

 En [1]: get_sum_ages_of_company_founders ('Samsung') 
Fuera [1]: 26
 En [2]: get_sum_ages_of_company_founders ('Apple Inc.') 
Fuera [3]: 87
 En [3]: get_sum_ages_of_company_founders ('Microsoft') 
Fuera [3]: 41
 En [4]: ??get_sum_ages_of_company_founders ('XPTO') 
 En [5]: 

Entonces, la implementación de la nueva característica fue un éxito y podemos implementar una nueva versión de nuestra aplicación. ¿Lo es? ¡No no no!

Vamos a ejecutar la función get_company_as_string . Tenga en cuenta que no hemos cambiado esa función, y ya hemos verificado que funcionaba bien. Veamos si sigue funcionando bien:

 En [6]: get_company_as_string ('Apple Inc.') 
Fuera [6]: "Nombre: Apple Inc. | Fundadores: [{'nombre': 'Steve Jobs', 'edad': 21}, {'nombre': 'Steve Wozniak', 'edad': 25}, { 'nombre': 'Ronald Wayne', 'edad': 41}] ".

¡Sorpresa! Los resultados no son los mismos, y la función ya no funciona como esperábamos.

Sí, es un ejemplo simple, pero imagine si nuestra aplicación tenía más de 100 o 1000 funciones. ¿Cómo podemos asegurar que todas las funciones sigan funcionando como esperamos? ¿O recibe una alerta cuando algo sale mal?

Usted sabe la respuesta: pruebas unitarias!

Assert / Expect

Esa es la base, y probablemente, la cosa más simple de las pruebas unitarias. Es una pequeña charla con la función. Le decimos algo a la función y verificamos si la respuesta es lo que debería ser.

Por ejemplo, ¿cómo podemos probar una función que suma dos números? Dale a la función dos números. Compare el rendimiento con la suma de esos dos números, que ya conocemos. Si la función es incorrecta, la comparación será fallida.

Sabemos que 2 + 2 es 4, -3 + -2 es -5 y -1 + 3 es 2. Entonces, si enviamos 2 y 2 a la función, esperamos recibir 4. Si enviamos -3 y -2 , esperamos recibir -5 y, si enviamos -1 y 3, esperamos recibir 2. Si uno de ellos es diferente de lo que esperamos, la función es incorrecta. Es simple, ¿verdad?

Veamos cómo las pruebas unitarias podrían habernos ayudado en nuestra refactorización previa. Recordando: No estoy usando ninguna biblioteca o clase específica. Quiero simplificar la explicación y mantenerla agnóstica.

En la versión anterior del código de la aplicación, la función get_company_as_string funcionaba bien. En este punto, sabíamos algunas cosas:

  • Si le decimos a 'Apple Inc.' a la función, esperamos recibir 'Nombre: Apple Inc. | Fundadores: Steve Jobs, Steve Wozniak y Ronald Wayne. ', Y
  • Si le decimos a 'XPTO' , esperamos recibir ninguno .

Entonces, en la forma de codificación:

  • get_company_as_string ('Apple Inc.') debe ser igual a 'Nombre: Apple Inc. | Fundadores: Steve Jobs, Steve Wozniak y Ronald Wayne. y
  • get_company_as_string ('XPTO') debe ser igual a None .

Aquí está el código:

Ejemplo de prueba minimalista sin clases y librerías

… y la ejecución de pruebas:

 En 1]: 
...: si get_company_as_string ('Apple Inc.') == 'Nombre: Apple Inc. | Fundadores: Steve Jobs, Steve Wozniak
...: y Ronald Wayne. ':
...: print ('Éxito en la prueba n. ° 1')
...: más:
...: print ('Error en la prueba n. ° 1')
...:
...: si get_company_as_string ('XPTO') es None:
...: print ('Éxito en la prueba n. ° 2')
...: más:
...: print ('Error en la prueba n. ° 2')
...:
Éxito en la prueba # 1.
Éxito en la prueba # 2.

Ahora, tenemos el código que asegura el correcto funcionamiento de la función. Entonces, podemos probar la funcionalidad después de la implementación de una nueva característica.

 En 1]: 
...: si get_company_as_string ('Apple Inc.') == 'Nombre: Apple Inc. | Fundadores: Steve Jobs, Steve Woznia
...: k y Ronald Wayne. ':
...: print ('Éxito en la prueba n. ° 1')
...: más:
...: print ('Error en la prueba n. ° 1')
...:
...: si get_company_as_string ('XPTO') es None:
...: print ('Éxito en la prueba n. ° 2')
...: más:
...: print ('Error en la prueba n. ° 2')
...:
Error en la prueba n. ° 1
Éxito en la prueba # 2.

Tenemos un error ¿O eso sería un éxito? Los cambios que hicimos cuando implementamos una nueva característica rompieron la función get_company_as_string. Pero nuestras pruebas podrían identificar este problema y ahora podemos solucionarlo.

Entonces, con un pequeño cambio en la función, podemos verificar que todas las pruebas pasaron y asegurar la calidad de nuestro software:

Función get_company_as_string refactorizada para que funcione después de los cambios en la estructura de datos de los fundadores de las empresas.