Logotipo para GIGAOM 365x70

Vea qué solución de pruebas de API resultó ganadora en el informe GigaOm Radar. Obtenga su informe analítico gratuito >>

Mejores prácticas de pruebas unitarias: cómo aprovechar al máximo la automatización de pruebas

Foto de cabeza de Brian McGlauflin,
14 de mayo de 2019
8 min leer

Las pruebas unitarias ayudan a las organizaciones a probar sus cargas de trabajo unidad por unidad, pero la pregunta es, ¿cuáles son las mejores formas de abordar las pruebas unitarias? Consulte esta publicación para conocer las mejores prácticas de pruebas unitarias.

Las pruebas unitarias son una práctica bien conocida, ¡pero hay mucho margen de mejora! Esta publicación cubre las mejores prácticas de pruebas unitarias más efectivas, incluidos enfoques para maximizar sus herramientas de automatización en el camino. También discutiremos la cobertura del código, las dependencias simuladas y las estrategias generales de prueba.

¿Qué son las pruebas unitarias?

Prueba unitaria Es la práctica de probar unidades o componentes individuales de una aplicación, con el fin de validar que cada una de esas unidades esté funcionando correctamente. Generalmente, una unidad debe ser una pequeña parte de la aplicación; en Java suele ser una sola clase. Tenga en cuenta que no estoy definiendo estrictamente "unidad" aquí, y corresponde al desarrollador decidir el alcance del código probado para cada prueba.

Las personas a veces contrastan el término "prueba unitaria" con "prueba de integración" o "prueba de un extremo a otro". La diferencia es que, en general, las pruebas unitarias se realizan para validar el comportamiento de una unidad comprobable individual, mientras que las pruebas de integración validan el comportamiento de varios componentes juntos o la aplicación en su conjunto. Como dije, la definición de lo que constituye una "unidad" no está estrictamente definida, y depende de usted decidir el alcance de cada prueba.

¿Por qué prueba unitaria?

Las pruebas unitarias son una técnica probada para garantizar la calidad del software, con muchos beneficios. Aquí hay (más de) algunas buenas razones para realizar la prueba unitaria:

  • Prueba unitaria valida que cada pieza de su software no solo funciona correctamente hoy, sino que continúa funcionando en el futuro, proporcionando una base sólida para el desarrollo futuro.
  • Prueba unitaria identifica defectos en las primeras etapas del proceso de producción, que reduce los costos de arreglarlos en etapas posteriores del ciclo de desarrollo.
  • El código probado por unidad es generalmente más seguro para refactorizar, ya que las pruebas se pueden volver a ejecutar rápidamente para validar que el comportamiento no ha cambiado.
  • Escribir pruebas unitarias obliga a los desarrolladores a considerar qué tan bien está diseñado el código de producción para hacerlo adecuado para pruebas unitariasy hace que los desarrolladores miren su código desde una perspectiva diferente, animándoles a considerar casos extremos y condiciones de error en su implementación.
  • Incluyendo pruebas unitarias en el proceso de revisión de código puede revelar cómo se supone que funciona el código modificado o nuevo. Además, los revisores pueden confirmar si las pruebas son buenas o no.

Es lamentable que, con demasiada frecuencia, los desarrolladores no escriban pruebas unitarias, no escriban suficientes pruebas o no las mantengan. Entiendo: las pruebas unitarias a veces pueden ser difíciles de escribir o llevar mucho tiempo mantenerlas. A veces hay una fecha límite que cumplir, y parece que escribir exámenes nos hará perder esa fecha límite. Pero no escribir suficientes pruebas unitarias o no escribir buenas pruebas unitarias es una trampa arriesgada en la que caer.

Por lo tanto, considere mis siguientes recomendaciones de mejores prácticas sobre cómo escribir pruebas automatizadas, limpias y fáciles de mantener que le brinden todos los beneficios de las pruebas unitarias, con una cantidad mínima de tiempo y esfuerzo.

Mejores prácticas de pruebas unitarias

Veamos algunas de las mejores prácticas para crear, ejecutar y mantener pruebas unitarias, a fin de lograr los mejores resultados.

Las pruebas unitarias deben ser confiables

La prueba debe fallar si el código está roto y solo si el código está roto. Si no es así, no podemos confiar en lo que nos dicen los resultados de la prueba.

Las pruebas unitarias deben ser mantenibles y legibles

Cuando el código de producción cambia, las pruebas a menudo deben actualizarse y posiblemente también depurarse. Por lo tanto, debe ser fácil de leer y comprender la prueba, no solo para quien la escribió, sino también para otros desarrolladores. Siempre organice y nombre sus pruebas para mayor claridad y legibilidad.

Las pruebas unitarias deben verificar un caso de uso único

Las buenas pruebas validan una cosa y solo una cosa, lo que significa que, por lo general, validan un solo caso de uso. Las pruebas que siguen esta práctica recomendada son más simples y comprensibles, y eso es bueno para la capacidad de mantenimiento y la depuración. Las pruebas que validan más de una cosa pueden volverse fácilmente complejas y su mantenimiento requiere mucho tiempo. No dejes que esto suceda.

Otra práctica recomendada es utilizar un número mínimo de afirmaciones. Algunas personas recomiendan solo una afirmación por prueba (esto puede ser un poco demasiado restrictivo); la idea es centrarse en validar solo lo que se necesita para el caso de uso que está probando.

Las pruebas unitarias deben estar aisladas

Las pruebas deben poder ejecutarse en cualquier máquina, en cualquier orden, sin que se afecten entre sí. Si es posible, las pruebas no deben depender de factores ambientales o del estado global / externo. Las pruebas que tienen estas dependencias son más difíciles de ejecutar y, por lo general, inestables, lo que las hace más difíciles de depurar y corregir, y terminan costando más tiempo del que ahorran (consulte digno de confianza, encima).

Martin Fowler, hace unos años, escribió sobre el código "solitario" versus el "sociable", para describir el uso de la dependencia en el código de la aplicación y cómo las pruebas deben diseñarse en consecuencia. En su artículo, el código "solitario" no depende de otras unidades (es más autónomo), mientras que el código "sociable" interactúa con otros componentes. Si el código de la aplicación es solitario, entonces la prueba es simple ... pero para el código sociable bajo prueba, puede construir una prueba "solitaria" o "sociable". Una "prueba sociable" se basaría en dependencias reales para validar el comportamiento, mientras que una "prueba solitaria" aísla el código bajo prueba de las dependencias. Puede usar simulacros para aislar el código bajo prueba y construir una prueba "solitaria" para el código "sociable". Veremos cómo hacerlo a continuación.
Imagen que explica la prueba de unidades sociables frente a las pruebas de unidades solitarias.
En general, usar simulacros para las dependencias nos facilita la vida como testers, porque podemos generar “tests solitarios” para código sociable. Una prueba sociable para código complejo puede requerir mucha configuración y puede violar los principios de ser aislado y repetible. Pero dado que el simulacro se crea y configura en la prueba, es autónomo y tenemos más control sobre el comportamiento de las dependencias. Además, podemos probar más rutas de código. Por ejemplo, puedo devolver valores personalizados o lanzar excepciones desde el simulacro, para cubrir condiciones de límite o error.

Las pruebas unitarias deben automatizarse

Asegúrese de ejecutar pruebas unitarias en un proceso automatizado. Esto puede ser diario, o cada hora, o en un proceso de Integración Continua o Entrega. Los informes deben ser accesibles y revisados ​​por todos los miembros del equipo. Como equipo, hablen sobre las métricas que les interesan: cobertura de código, cobertura de código modificado, número de pruebas que se ejecutan, rendimiento, etc.

Se puede aprender mucho al observar estos números, y un gran cambio en esos números a menudo indica regresiones que se pueden abordar de inmediato.

Ebook: Mejore las pruebas unitarias para Java con automatización

Utilice una buena combinación de pruebas unitarias y de integración

El libro de Michael Cohn, Tener éxito con Agile: Desarrollo de software con Scrum, aborda esto utilizando un modelo piramidal de prueba (vea la ilustración en la imagen a continuación). Este es un modelo de uso común para describir la distribución ideal de los recursos de prueba. La idea es que a medida que asciende en la pirámide, las pruebas suelen ser más complejas de construir, más frágiles, más lentas de ejecutar y más lentas de depurar. Los niveles inferiores están más aislados y más integrados, más rápidos y más sencillos de crear y depurar. Por lo tanto, las pruebas unitarias automatizadas deben constituir la mayor parte de sus pruebas.

Imagen de la pirámide de pruebas con el valor de las pruebas en cada nivel, así como las complejidades asociadas.

Las pruebas unitarias deben validar todos los detalles, los casos de esquina y las condiciones de contorno, etc. Las pruebas de componentes, integración, interfaz de usuario y funcionales deben usarse con más moderación para validar el comportamiento de las API o la aplicación en su conjunto. Las pruebas manuales deben ser un porcentaje mínimo de la estructura piramidal general, pero siguen siendo útiles para la aceptación de versiones y las pruebas exploratorias. Este modelo proporciona a las organizaciones un alto nivel de automatización y cobertura de pruebas, de modo que puedan escalar sus esfuerzos de prueba y mantener los costos asociados con la construcción, ejecución y mantenimiento de pruebas al mínimo.

Las pruebas unitarias deben ejecutarse dentro de una práctica de prueba organizada

Para impulsar el éxito de sus pruebas en todos los niveles y hacer que el proceso de pruebas unitarias sea escalable y sostenible, necesitará algunas prácticas adicionales. En primer lugar, esto significa escribir pruebas unitarias a medida que escribe el código de su aplicación. Algunas organizaciones escriben las pruebas antes del código de la aplicación (impulsado por pruebas or impulsado por el comportamiento programación). Lo importante es que las pruebas van de la mano con el código de la aplicación. Las pruebas y el código de la aplicación incluso deberían revisarse juntos en el proceso de revisión del código. Las revisiones lo ayudan a comprender el código que se está escribiendo (porque pueden ver el comportamiento esperado) y también mejoran las pruebas.

Escribir pruebas junto con el código no es solo para nuevos comportamientos o cambios planificados, también es fundamental para la corrección de errores. Cada error que arregle debe tener una prueba que verifique que el error está arreglado. Esto asegura que el error permanezca arreglado en el futuro.

Adopte una política de tolerancia cero para las pruebas fallidas. Si su equipo ignora los resultados de las pruebas, ¿por qué realizar pruebas? Las fallas en las pruebas deberían indicar problemas reales ... así que resuelva esos problemas de inmediato, antes de que pierdan el tiempo de control de calidad o, peor aún, entren en el producto lanzado.

Cuanto más tiempo lleve abordar las fallas, más tiempo y dinero le costarán a su organización. Por lo tanto, ejecute pruebas durante la refactorización, ejecute pruebas justo antes de enviar el código y no permita que una tarea se considere "terminada" hasta que las pruebas también pasen.

Finalmente, mantener esas pruebas. Como dije antes, si no mantiene esas pruebas actualizadas cuando cambia la aplicación, pierden su valor. Especialmente si están fallando, las pruebas que fallan están costando tiempo y dinero para investigar cada vez que fallan. Refactorice las pruebas según sea necesario, cuando cambie el código.

Como puede ver, maximizar el rendimiento del dinero y el tiempo invertido en sus pruebas unitarias requiere cierta inversión en la aplicación de las mejores prácticas. Pero al final, las recompensas valen la inversión inicial.

¿Qué pasa con la cobertura del código?

En general, la cobertura del código es una medida de cuánto código de producción se ejecuta mientras su pruebas automatizadas estan corriendo. Al ejecutar un conjunto de pruebas y observar los datos de cobertura del código, puede tener una idea general de qué parte de su aplicación se está probando.

Hay muchos tipos de cobertura de código, los más comunes son la cobertura de línea y la cobertura de sucursales. La mayoría de las herramientas se centran en la cobertura de la línea, que solo le indica si se cubrió una línea específica. Branch es más granular, ya que le indica si se cubre cada ruta a través del código.

La cobertura del código es una métrica importante, pero recuerde que aumentarla es un medio para lograr un fin. Es genial para encontrar lagunas en las pruebas, pero no es lo único en lo que centrarse. Tenga cuidado de no gastar demasiado esfuerzo tratando de lograr una cobertura del 100%; puede que ni siquiera sea posible o factible, y realmente la calidad de sus pruebas es lo importante. Dicho esto, lograr al menos el 60% de cobertura para sus proyectos es un buen punto de partida, y el 80% o más es un buen objetivo a establecer. Obviamente, depende de usted decidir cuál debería ser ese objetivo.

También es valioso si tiene herramientas automatizadas que no solo miden la cobertura del código, sino que también realizan un seguimiento de la cantidad de código modificado que cubren las pruebas, porque esto puede proporcionar visibilidad sobre si se están escribiendo suficientes pruebas junto con cambios en el código de producción.

Vea aquí un ejemplo de informe de cobertura de código del centro de análisis e informes de Parasoft, por el que puede navegar si está utilizando Prueba J de Parasoft para su examen de la unidad:

Informe de cobertura de muestra de Parasoft DTP.

Otra cosa a tener en cuenta es que, al escribir nuevas pruebas, tenga cuidado de centrarse únicamente en la cobertura de línea, ya que una sola línea de código puede generar múltiples rutas de código, así que asegúrese de que sus pruebas validen estas rutas de código. La cobertura de línea es un indicador rápido útil, pero no es lo único que debe buscar.

La forma más obvia de aumentar la cobertura es simplemente agregar más pruebas para más rutas de código y más casos de uso del método bajo prueba. Una forma poderosa de aumentar la cobertura es utilizar pruebas parametrizadas. Para Junit4, existía la funcionalidad parametrizada de Junit4 incorporada y bibliotecas de terceros como JUnitParams. JUnit3 tiene parametrización incorporada.

Finalmente, si aún no está rastreando la cobertura de la prueba, le recomiendo que comience. Hay muchas herramientas que pueden ayudar, como Prueba J de Parasoft. Comience midiendo sus números de cobertura actuales, luego establezca metas donde debería estar, aborde primero las brechas importantes y luego trabaje desde allí.

Resum

Aunque la prueba unitaria es una técnica probada para garantizar la calidad del software, todavía se considera una carga para los desarrolladores y muchos equipos todavía están luchando con ella. Para aprovechar al máximo las pruebas y las herramientas de prueba automatizadas, las pruebas deben ser confiables, fáciles de mantener, legibles, autónomas y se deben utilizar para verificar un solo caso de uso. La automatización es clave para que las pruebas unitarias sean viables y escalables.

Además, los equipos de software deben practicar buenas técnicas de prueba, como escribir y revisar pruebas junto con el código de la aplicación, mantener las pruebas y asegurarse de que las pruebas fallidas se rastreen y remedian de inmediato. La adopción de estas mejores prácticas de pruebas unitarias puede mejorar rápidamente los resultados de las pruebas unitarias.

Aprenda a aumentar el rendimiento de las pruebas unitarias con Parasoft Jtest.