Tests dentro de la ingeniería del software

Dentro de la mayoría de metodologías de diseño y desarrollo de software existe un apartado enfocado a las comprobaciones de las funciones, aspecto y comportamiento de una aplicación. Es lo que se conoce como testing dentro del proyecto.

Beneficios del testing

Dentro de la multitud beneficios que aporta realizar testing en nuestro proyecto se encuentran los siguientes.

  • Se pueden realizar pruebas específicas y generales del comportamiento de clases y funciones de nuestro proyecto. De esta forma podemos validar que las funciones y clases que desarrollamos dentro de nuestro proyecto cumplen los requisitos definidos por el documento de análisis.
  • Se pueden realizar pruebas completas de una experiencia de usuario. De esta forma podemos verificar que nada se ha roto cuando añadimos algún elemento nuevo a nuestro proyecto.
  • Las pruebas de uso de la aplicación se pueden automatizar para que nuestro ordenador compruebe que la aplicación funciona como se espera.
  • Además permite automatizar otros procesos que pueden resultar laboriosos o tediosos como la toma de capturas de pantalla, validación del aspecto visual de cada pantalla de la aplicación e incluso se pueden crear comprobaciones de accesibilidad en algunas plataformas para la detección temprana de barreras de accesibilidad.

Problemas del testing

Incluir testing en nuestro proyecto implica la aparición de los siguientes problemas, sobre todo si el equipo de desarrollo no está acostumbrado al uso de testing:

  • Incremento en los costes de organización y tiempo. Las pruebas o tests no se planifican ni se escriben solas. Es necesario organizar el plan de trabajo para que el testing sea correctamente incluido en la metodología de trabajo utilizada por el equipo dentro del proyecto.
  • Prevención de falsos errores. Cuando se realizan cambios visuales o funcionales dentro de una ventana o una sección de nuestra aplicación todos los tests relacionados con dicha pantalla fallarán. Es necesario que algún miembro del equipo actualice esos tests para que acepten los nuevos cambios y validar cuales son los nuevos comportamientos y aspectos válidos para el proyecto.
  • No existe la fiabilidad al completo. Aunque podamos pensar que un proyecto software siempre se comportará igual en cualquier situación cuando el proyecto es complejo también aumenta la complejidad para cubrir todos los posibles casos de uso con los tests. Incluso así es posible que nuestro conjunto de tests no detecte algún posible error o problema de funcionamiento de nuestra aplicación. Cuando esto sucede es necesario ampliar el número de tests para verificar que el error detectado no vuelva a ocurrir.

Tipos de testing

Dentro de la creación y mantenimiento de un proyecto de software existen distintas necesidades de comprobación por esta razón existen diversos tipos de testing.

Cada tipo de testing tiene un objetivo claro que ayuda a detectar y solucionar un tipo de problema específico del proyecto.

Tests funcionales

Este tipo de test comprueba si la funcionalidad de un método o función se adapta al comportamiento esperado. De esta forma podemos comprobar si una función de conversión de divisas funciona como se espera o si el cálculo de el total del precio de un pedido no comete errores.

Dentro de este tipo de tests encontramos los tests unitarios, tests de aceptación, tests de integración y tests de regresión.

Tests no funcionales

Este tipo de test representa  las pruebas que se realizan en la aplicación o producto y que no están relacionadas con el código del proyecto.

Algunas de estas pruebas son las pruebas de rendimiento, las pruebas de carga y las pruebas de estrés.

Regla YAGNI – You aren’t gonna need it

La última de las 5 reglas para escribir software simple y robusto es la regla de YAGNI. Su acrónimo traducido dice: no lo vas a necesitar.

En muchas ocasiones cometemos el error de hacer sobreingeniería en un proyecto. Esto significa que el cliente nos ha pedido una calculadora para hacer sumas y terminamos desarrollando una hoja de cálculo con soporte para funciones y un lenguaje propio de scripting. Esto provoca que el mantenimiento y el esfuerzo para desarrollar y mantener el proyecto se ha sobredimensionado de forma incontrolada e innecesaria.

Para evitar caer en este código innecesario o código YAGNI debemos centrarnos en los requerimientos del producto final, no desarrollar código que no se esté utilizando en el proyecto, eliminar el código comentado (Si está comentado es que no es necesario) y utilizar ciertos patrones de trabajo como TDD (Test Driven Development) o trabajar con PullRequests pequeñas enfocadas en aspectos muy específicos de un requerimiento o característica del proyecto. También realizar procesos de refactorización del código al terminar un plazo de entrega o el desarrollo de un módulo facilita la desaparición de código YAGNI.

Regla SOC – Separation of concerns

Dentro de las reglas para escribir software simple y robusto tenemos la regla de SOC cuyo acrónimo significa Separation of concerns. Esto se traduce como separación de intereses. Este término de ingeniería fue creado por Edsger Dijkstra en un artículo para indicar la necesidad de centrar la atención en aspectos específicos dentro de las distintas estructuras de manipulación de la información dentro del software. Esta separación de intereses dentro del software se puede aplicar a nivel de arquitectura del proyecto y a nivel de codificación de los métodos y funciones.

Arquitectura de intereses

A nivel de arquitectura debemos agrupar los distintos intereses dentro de módulos o microservicios. Cada módulo o servicio deberá encargarse de un aspecto del proyecto.

Se busca que cada módulo o microservicio sea independiente y tengan claras sus responsabilidades.

Dentro de la modularización del código se aplican patrones de diseño como MVC (Modelo vista controlador), MVP (Modelo vista presentador) y patrones de diseño similares. Esta forma de organizar el código nos permite diferenciar cláramente las clases relacionadas con el modelo de datos, la interfaz de usuario y la forma de presentar y manipular los datos en la interfaz de usuario. 

Aplicando estos patrones de diseño a la hora de modularizar nuestro código podremos realizar modificaciones en la interfaz de usuario sin necesidad de afectar al modelo de datos o a la lógica de negocio ya que las responsabilidades de cada parte del módulo están conceptualmente definidas.

Separación del código por intereses

Para separar el código por intereses se debe realizar un ejercicio de análisis observando desde el punto más alto de abstracción de nuestro proyecto, tanto en funcionalidad como en la información que se manipula, hasta delimitar la responsabilidad y relación entre las distintas clases y módulos del proyecto.

Con este esfuerzo se busca obtener las siguientes ventajas: eliminar la posible duplicación en la lógica de negocio, ampliar la capacidad de mantenimiento del proyecto, incremento de la cohesión entre clases y reducción del acoplamiento entre clases y módulos.

Una vez realizado este análisis se aplican distintos paradigmas de programación como la programación orientada a aspectos, arquitectura hexagonal, inyección de dependencias y la separación de métodos por responsabilidad.

Regla LOD – Law of Demeter

Una de las reglas para escribir software simple y robusto que también podemos aplicar a otros aspectos de la vida es la regla de Demeter. Esto es lo que significa LOD: law of Demeter.

Esta regla se resume en el concepto de: no hables con desconocidos.

Dentro de la ingeniería de software esta regla representa al principio del mínimo conocimiento.

Este principio nos dice que una función o método X perteneciente a un objeto Y sólo puede llamar a otros métodos o funciones pertenecientes al objeto, un parámetro del método X, cualquier objeto creado dentro del método X o cualquier propiedad definida dentro del método X.

Esto establece que la comunicación del método X se limita al dominio de su objeto contenedor o al propio dominio del método X.

De forma más comprensible este principio nos permite escribir un método de forma que cualquier información que el método necesite consultar o manipular estará dentro del propio código del método o dentro del código de la clase que lo contiene. De esta forma podemos detectar fácilmente posibles errores o seguir el movimiento de la información que manipula ese método.

Regla KISS – Keep it super simple

Otra de las reglas para escribir software simple y robusto es la regla KISS.

El acrónimo de KISS significa Keep it super simple (manténlo super simple). 

Esta regla indica que la mayoría de las aplicaciones funcionan mejor si se mantienen simples en lugar de complicadas; Esta regla busca que la simplicidad debe ser un objetivo clave en el diseño y se debe evitar la complejidad innecesaria.

Algunas soluciones para seguir esta regla es aplicar código limpio y utilizar algunos paradigmas para abordar la resolución de problemas como pueden ser el de divide y vencerás y las reglas de programación SOLID.

Siguiendo la regla de KISS crearemos un software estructurado en clases y módulos simples donde sea fácil localizar cada dato y función.

Regla DRY – Don’t repeat yourself

En el artículo sobre reglas para escribir software simple y robusto hablamos de 5 reglas para mantener la simplicidad. Una de estas cinco reglas y la más fácil de entender es la regla DRY cuyo acrónimo significa Don’t repeat yourself (No te repitas a ti mismo). Esta regla aparece por primera vez en el libro The pragmatic programmer.

Esta regla implica que cada pieza de conocimiento debe tener una representación única dentro del proyecto. Esto implica que no se deben repetir partes como la lógica de negocio en distintas partes del proyecto.

Esto reduce el código repetido en el proyecto y hace que nuestras clases y módulos sean más reutilizables.

Si no seguimos esta regla y tenemos elementos repetidos para hacer algún proceso del modelo de negocio, el día que tengamos que actualizar esa función o proceso deberemos invertir tiempo en localizar todas las partes donde se repite dicho proceso y actualizar el código.