¿Qué son las pruebas automatizadas?
En esta unidad, descubrirá las ventajas de las pruebas automatizadas y los tipos de pruebas que se pueden realizar. También aprenderá en qué consiste una buena prueba y conocerá algunas de las herramientas de prueba que tiene a su disposición.
¿Qué son las pruebas automatizadas?
Las pruebas automatizadas usan el software para ejecutar el código y comparar los resultados reales con los esperados. Compare esto con las pruebas manuales o exploratorias, donde normalmente una persona sigue instrucciones en un plan de pruebas para comprobar que el software funciona según lo esperado.
Las pruebas manuales tienen sus ventajas. Pero a medida que aumenta el tamaño de la base de código, probar manualmente todas las características (incluidos los casos extremos) puede resultar repetitivo, tedioso y propenso a errores. Las pruebas automatizadas pueden ayudar a eliminar parte de esta carga y permiten que los evaluadores manuales se centren en lo mejor que saben hacer: garantizar que los usuarios tengan una experiencia positiva con el software.
La pirámide de pruebas
Al pensar en pruebas automatizadas, es habitual separar las pruebas en capas. Mike Cohn propone este concepto, conocido como la pirámide de pruebas, en su libro Succeeding with Agile.
Aunque se trata de una versión simplista del modelo de Cohn, el concepto ilustra que se centra la mayor parte del esfuerzo en escribir pruebas que comprueben los niveles básicos del software (llamada 1 de la pirámide), como las funciones, las clases y los métodos. Invierte progresivamente menos esfuerzo a medida que se combinan las características, como en la capa de la interfaz de usuario (UI) (llamada 2 de la pirámide). La idea es que, si puede comprobar que cada componente del nivel inferior funciona según lo previsto por sí solo, las pruebas de los niveles superiores solo deberán comprobar que los diversos componentes funcionen en conjunto para obtener el resultado esperado.
¿Cuándo debería escribir pruebas?
La respuesta depende principalmente de sus necesidades y experiencia escribiendo pruebas.
Nunca es demasiado tarde para empezar a agregar pruebas para el código que ya ha escrito e implementado. Esto es especialmente cierto para las características que se interrumpen con frecuencia o que requieren más esfuerzo por parte del equipo de pruebas.
Cuando se relacionan las pruebas con las canalizaciones de integración y entrega continuas, los dos conceptos que oirá son pruebas continuas y desplazamiento a la izquierda.
Pruebas continuas significa que las pruebas se ejecutan al principio del proceso de desarrollo a medida que los cambios pasan a través de la canalización. El desplazamiento a la izquierda implica tener en consideración las pruebas y la calidad del software en un momento inicial del proceso de desarrollo.
Por ejemplo, los desarrolladores a menudo agregan casos de prueba mientras desarrollan su característica y ejecutan el conjunto de pruebas completo antes de enviar el cambio a la canalización. Este enfoque ayuda a asegurar que la característica que están creando se comporte según lo previsto y, también, que no interrumpa las existentes.
A continuación, se muestra un breve vídeo en el que Abel Wang, asesor de cuestiones relacionadas con la nube de Microsoft, explica la forma de asegurarse de mantener la calidad en un plan de DevOps.
Pregunte a Abel
El desplazamiento a la izquierda también suele requerir que los evaluadores se involucren en el proceso de diseño, incluso antes de que se escriba el código de la característica. Compare esto con el modelo de "entrega", en el que al equipo de pruebas se le presentan nuevas características que hay que probar solo cuando el software ya está diseñado y escrito. Un error detectado tarde en el proceso puede afectar a la programación de entrega del equipo. Además, los errores pueden detectarse semanas o incluso meses después de que el desarrollador haya creado originalmente la característica.
La contrapartida
Con las pruebas automatizadas, llega una contrapartida. Aunque las pruebas automatizadas permiten que los evaluadores empleen su tiempo comprobando la experiencia del usuario final, es posible que los desarrolladores deban dedicar más tiempo a escribir y mantener el código de prueba.
Pero el objetivo de las pruebas automatizadas es ayudar a garantizar que los evaluadores solo reciban el código de mayor calidad, un código que se han probado para que funcione como se espera. Por lo tanto, puede que los desarrolladores recuperen parte de ese tiempo gracias al hecho de no tener que controlar tantos errores ni tener que reescribir el código debido a un caso extremo que no habían considerado inicialmente.
Ventajas adicionales
Documentación y la capacidad de refactorizar el código más fácilmente son dos ventajas adicionales de las pruebas automatizadas.
Documentación
Los planes de pruebas manuales pueden actuar como un tipo de documentación sobre cómo debería comportarse el software y por qué existen determinadas características.
Las pruebas automatizadas pueden tener el mismo propósito. El código de pruebas automatizadas suele utilizar un formato legible. El conjunto de entradas que proporcione representa valores que podrían escribir los usuarios. Cada salida asociada especifica el resultado que esperarían los usuarios.
De hecho, muchos desarrolladores siguen el desarrollo controlado por pruebas, o método TDD, escribiendo su código de prueba antes de implementar una nueva característica. La idea consiste en escribir un conjunto de pruebas, a menudo denominado especificaciones, que inicialmente producirá un error. Luego el desarrollador escribirá cada vez más código para implementar la característica hasta que el resultado de todas las pruebas es satisfactorio. No solo las especificaciones documentan los requisitos, sino que el proceso de TDD ayuda a garantizar que solo se escribe la cantidad necesaria de código para implementar la característica.
Refactorización
Supongamos que tiene una base de código de gran tamaño que quiere refactorizar para hacer que algunas partes se ejecuten más rápido. ¿Cómo sabe que su trabajo de refactorización no provocará que se interrumpan algunas partes de la aplicación?
Las pruebas automatizadas sirven como un tipo de contrato. Es decir, se especifican las entradas y los resultados esperados. Cuando tiene un conjunto de pruebas satisfactorias, tiene mayor capacidad para experimentar y refactorizar el código. Cuando realiza un cambio, lo único que necesita hacer es ejecutar las pruebas y comprobar que el resultado sigue siendo satisfactorio. Una vez cumplidos los objetivos de refactorización, puede enviar el cambio a la canalización de compilación para que todo el mundo pueda beneficiarse, pero con un riesgo menor de interrupciones.
¿Qué tipos de pruebas automatizadas existen?
Hay muchos tipos de pruebas automatizadas. Cada una de ellas sirve para un propósito diferente. Por ejemplo, puede ejecutar las pruebas de seguridad para ayudar a comprobar que solo los usuarios autorizados pueden acceder a un componente de software o una de sus características.
Cuando se habla de la integración continua y la canalización de compilación, normalmente nos referimos a las pruebas de desarrollo. Las pruebas de desarrollo son las pruebas que se pueden ejecutar antes de implementar la aplicación en un entorno de pruebas o producción.
Por ejemplo, las pruebas de Lint, una forma de análisis de código estático, comprueba el código fuente para determinar si se ajusta a la guía de estilo de su equipo. Un código con un formato coherente es más fácil de leer y mantener para todos.
En este módulo, trabajará con las pruebas unitarias y las pruebas de cobertura de código.
Las pruebas unitarias comprueban los componentes más importantes del programa o biblioteca, como un método o una función individual. Se especifican una o varias entradas, junto con los resultados esperados. El ejecutor de pruebas realiza cada prueba y comprueba si los resultados reales coinciden con los esperados.
Por ejemplo, supongamos que tiene una función que realiza una operación aritmética que incluye una división. Puede especificar algunos valores que espera que escriban los usuarios junto con valores de caso extremo, como 0 y -1. Si una entrada determinada genera un error o una excepción, puede comprobar si la función produce el mismo error.
Las pruebas de cobertura de código calculan el porcentaje del código que está cubierto por las pruebas unitarias. Las pruebas de cobertura de código pueden incluir ramas condicionales en el código para asegurarse de que una función se cubra.
Cuanto mayor sea el porcentaje de cobertura de código, mayor confianza puede tener de que más adelante no se detectará un error en el código que no se haya probado completamente. No es necesario llegar al 100 % de cobertura de código. De hecho, cuando empiece, es probable que encuentre que tiene un porcentaje bajo, pero eso le proporciona un punto de partida desde el que puede agregar pruebas adicionales que cubran código problemático o usado con frecuencia.
Mantener las pruebas unitarias aisladas
Al informarse sobre las pruebas unitarias, es posible que oiga términos como simulaciones, códigos auxiliares e inserción de dependencias.
Recuerde que una prueba unitaria debería comprobar un método o función individual y no cómo interactúan varios componentes. Pero, si tiene una función que llama a una base de datos o un servidor web, ¿cómo controlará esta situación?
Una llamada a un servicio externo no solo interrumpe el aislamiento, sino que puede ralentizarlo todo. Si el servidor web o la base de datos deja de funcionar o no está disponible, la llamada también puede provocar una interrupción en la serie de pruebas.
Las técnicas como la simulación o la inserción de dependencias le permiten crear componentes que se comportan como esta funcionalidad externa. Verá un ejemplo posteriormente en este módulo.
Más adelante, puede ejecutar las pruebas de integración para comprobar que la aplicación funciona correctamente con un servidor web o de bases de datos real.
¿Qué hace que una prueba sea buena?
Identificará una prueba buena con mayor facilidad a medida que gane experiencia escribiendo las suyas propias y leyendo las que hayan creado otros usuarios. A continuación, se indican algunas directrices para comenzar:
- No realice pruebas por el simple hecho de probar: sus pruebas deben tener un propósito más allá de ser un elemento de la lista de verificación que deba tachar. Escriba pruebas que comprueben que el código crítico funcione según lo previsto y que no interrumpa ninguna funcionalidad existente.
- Realice pruebas breves: las pruebas deberían completarse lo más rápido posible, sobre todo las que se realizan durante las fases de desarrollo y compilación. Al ejecutar pruebas cuando los cambios pasan a través de la canalización, no es deseable que estas sean el cuello de botella.
- Asegúrese de que sus pruebas se puedan repetir: las series de pruebas deben producir los mismos resultados cada vez que se realizan, tanto si se ejecutan en su equipo como en el equipo de un compañero de trabajo o en la canalización de compilación.
- Realice pruebas específicas: una idea equivocada muy común es que las pruebas deben cubrir el código escrito por otros usuarios. Normalmente, las pruebas solo deben abarcar el código propio. Por ejemplo, si usa una biblioteca de gráficos de código abierto en el proyecto, no necesita probar esa biblioteca.
- Elija la granularidad correcta: por ejemplo, si va a realizar pruebas unitarias, una prueba individual no debería combinar ni probar varias funciones o métodos. Pruebe cada función por separado y, después, escriba pruebas de integración que comprueben que varios componentes interactúan correctamente.
¿Qué tipos de herramientas de prueba hay disponibles?
Los tipos de herramientas de prueba que use dependerán del tipo de aplicación que esté compilando y del tipo de pruebas que quiera realizar. Por ejemplo, puede usar Selenium para realizar pruebas de UI en muchos tipos de exploradores web y sistemas operativos.
No importa en qué lenguaje esté escrita su aplicación, muchas herramientas de prueba están disponibles para utilizarse.
Por ejemplo, para las aplicaciones de Java, es posible que elija Checkstyle para realizar pruebas lint y JUnit para realizar pruebas unitarias.
En este módulo, usaremos NUnit para las pruebas unitarias, ya que es popular en la comunidad de .NET.