Pruebas Unitarias – Conceptos Principales

Pruebas Unitarias – Conceptos Principales

Antes de empezar es importante familiarizarse con los principales conceptos. Si ya has hecho o estudiado algo de Pruebas Unitarias, seguramente ya los conoces y puedes saltarte este post, aunque te recomiendo leerlo para tener fresca la información. Si es tu primera vez, no son conceptos muy complicados, pero no olvides hacer un pequeño acordeón de los principales conceptos para poder repasarlos hasta que los tengas dominados.

Prueba Unitaria – Unit Test

Una prueba unitaria es código, cuya función es probar el correcto comportamiento de otra unidad de código, ofreciendo un resultado de pasado o fallido. La prueba debe de poder ser ejecutada de manera automática o manual y su resultado siempre debe ser consistente, siempre que el código no cambie. Esta definición puede sonar demasiado abstracta para la persona que nunca ha estudiado pruebas unitarias, pero lo importante es recordar estos 3 conceptos:

Es Unitaria – Una prueba debe de probar únicamente una unidad de código, y un comportamiento. Si tienes múltiples unidades, o múltiples comportamientos en la misma unidad, utilizarás múltiples pruebas. Cada prueba puede tener solo 2 resultados, pasada o fallida.

Prueba Comportamiento – Se debe de probar el comportamiento. No hay que enfocarse en probar cada función dentro de una clase, en especial si son funciones privadas. Es mejor concentrarse en probar los comportamientos que se esperan de cada clase. Es decir, que la clase funcione como debería funcionar en los lugares donde se debe usar.

Ejecución Automática – La prueba debe de poder ser ejecutada de manera Automática. Usualmente esto la hace una aplicación, a la que se le llama el “Test Runner”, la cual se encarga de ejecutar las pruebas y asegurarse que pasan, mostrándote los resultados. El “Test Runner” muchas veces es parte del mismo IDE con el que programas.

Los 3 Pasos de la Prueba Unitaria

Cada prueba unitaria está separada en 3 pasos. En inglés se les conoce como el AAA, y son:

Arrange – Preparar

Es la primera sección de la prueba. Es donde se proporcionan los datos y se inicializa la clase a probar.

Act – Ejecutar

La ejecución del comportamiento de la clase que se va a probar. Usualmente la llamada a un método o función.

Assert – Revisar

La revisión del resultado de la ejecución. Es aquí donde se revisa que el comportamiento de la clase arroje los resultados esperados. Esta sección define si la prueba paso, o fallo.

TDD – Test Driven Development – Desarrollo Dirigido por Pruebas

Si en algún momento has leído o estudiado acerca de Pruebas Unitarias, lo más posible es que haya sido de la mano por la filosofía TDD. TDD propone un proceso de desarrollo donde lo primero que se tiene que programar es la prueba, y que esta falle. Posteriormente implementar el código necesario para que esta prueba pase. De cierta manera es pensar primero en las pruebas, y después en el código para solucionarlas.

Esto es una filosofía, y una práctica. Pero hay que diferenciar que, aunque TDD significa tener Pruebas Unitarias, Pruebas Unitarias no significa forzosamente TDD. Y aquí si queda mucho a la interpretación de cada programador. Conozco programadores que prefieren hacer TDD, esto les obliga a pensar y realizar un mejor diseño de cómo se consumirá su clase. Pero también hay programadores que prefieren hacer el código, y posteriormente crear las Pruebas Unitarias para asegurarse que se cumplen con todos los escenarios.

No importa cual prefieras. Lo importante es el resultado final. Que tu código tenga Pruebas Unitarias que puedan validar su comportamiento de manera automática. Si tienes el tiempo, intenta y aprende TDD, ya que este también te empuja de manera automática a adoptar mejores prácticas y a aprender mejores patrones y prácticas.

Code Coverage – Cobertura de Código

Otro concepto muy importante que va de la mano de las pruebas unitarias es el de “Code Coverage”, o Cobertura de Código. Este es el concepto de que porcentaje del total de las líneas, está cubierto por Pruebas Unitarias para validar su comportamiento. Si todo el flujo de tu código pasa por pruebas unitarias, tienes un Code Coverage del 100%. Si únicamente la mitad del código pasa por pruebas unitarias, solo tienes un Code Coverage del 50%. En si el concepto es un poco complicado de imaginar, y lo iremos entendiendo durante las siguientes lecciones. Lo importante es que conozcas el concepto, y que entiendas que, como regla general, NO se busca una cobertura del 100%. Existen funcionalidades muy sencillas o que se usan tan poco que no es necesario incluir todo su comportamiento en pruebas unitarias.

Como regla general, se considera que todo sistema debería de tener un Code Coverage del entre el 70% y 80%. Pero sé pragmático, identifica los principales puntos de complejidad de tu programa, las partes donde más posibilidades de errores puede haber, o donde suele haber más cambios, y asegúrate que tengas Pruebas Unitarias para validar todos esos escenarios. Recuerda también que el Software también suele cumplir la regla de Pareto, el 80% del tiempo, los usuarios utilizan solo el 20% de la funcionalidad. Asegúrate que ese 20% sea perfecto y tenga sus pruebas unitarias.

Framework y Test Runner

Finalmente hay que entender que las pruebas unitarias no suelen ser una parte nativa del lenguaje, usualmente requieren de un Framework que incluye todas las funciones requeridas para validar, y una aplicación (Test Runner) que se comunica con el framework para poder ejecutarlas y mostrar los resultados. En el caso de .Net Core veremos xUnit que es el Framework más utilizado en .Net, adoptado incluso por Microsoft. Pero no te preocupes, los conceptos que aprendas aquí aplican para cualquier otro Framework de pruebas unitarias.

Mocks o Proxys

Finalmente recuerda que una Prueba Unitaria solo prueba el comportamiento de una clase por si sola, nunca en la interacción con otros sistemas. Esto es especialmente importante ya que una Prueba Unitaria nunca debe de probar la comunicación con una Base de Datos, o la correcta llamada a un API. Esas pruebas se les llama Pruebas de Integración y son un tema separado que también veremos en el futuro.

¿Pero que haces si necesitas probar una clase que utiliza una librería para solicitar datos de una Base o de un API? aquí utilizamos una técnica llamada Mocks. Es un tema un poco más avanzado ya que requieren de la aplicación del patrón “Inversión de Control”, pero en general, Mocks son clases que sustituyen, de manera falsa, la funcionalidad de otra clase para la ejecución de una Prueba Unitaria. Es decir, en vez de ejecutar el comportamiento real de la dependencia, se ejecuta un comportamiento falso que responde igual que la dependencia real.

Para esto también se utilizan librerías, pero también puede implementarlos de manera manual.

No te preocupes si no te quedo claro. Mocks es algo indispensable en las Pruebas Unitarias, así que lo veremos en el futuro.

Resumen:

Bueno, en las siguientes lecciones veremos el “Hola Mundo” de las pruebas unitarias y como puedes comenzar a usarlas. Por lo pronto recuerda:

  • La prueba unitaria debe probar una única unidad de Código. Deben de verificar Comportamiento, no función ni interacción. Deben de poderse ejecutar de manera automática.
  • Las partes de una prueba unitaria son AAA. Arrange – Preparar. Act – Ejecutar. Assert – Revisar.
  • TDD es la práctica que propone hacer primero las pruebas, y luego hacer el código hace que dicha prueba pase.
  • Code Coverage o Cobertura de Código, es el porcentaje de tu código que está verificado por Pruebas Unitarias.
  • Mocks y Proxys son clases que sustituyen el comportamiento de una clase de la cual depende el comportamiento que estamos probando, con la finalidad de no probar el funcionamiento de dicha dependencia.

This Post Has 2 Comments

Deja un comentario

Close Menu