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.

Qué es Docker

En la actualidad tanto para el uso profesional como el personal existen 3 principales plataformas o sistemas operativos para los ordenadores de escritorio: Linux, Windows y MacOS. Estas 3 plataformas publican actualizaciones y nuevas versiones para que todas las personas puedan acceder a una versión más optimizada y segura de su entorno de trabajo. Esta premisa debería permitir que una aplicación o proyecto software se pueda ejecutar en cualquier ordenador que esté corriendo la misma plataforma para la que se diseñó la aplicación pero esto no es así.

Dentro de un ordenador que está ejecutando una de las 3 plataformas que hemos mencionado antes se ejecutan multitud de procesos, programas de apoyo y se disponen de distintas librerías que hacen de herramientas de apoyo a otras aplicaciones. Cada proceso, librería y programas de apoyo también tienen sus correspondientes actualizaciones y nuevas versiones. Unido a todo esto se ha de mencionar que tanto Windows, MacOS y Linux permiten al usuario personalizar ciertas características y configuraciones que permiten al usuario optimizar su máquina a su gusto o necesidad. Por ejemplo, la configuración y personalización de un ordenador dedicado a hacer de servidor web para miles de usuarios es muy distinta a la configuración de un ordenador que se utiliza en casa para tareas educativas, ócio y personales.

En mi máquina funciona

Dentro de la ingeniería del software es muy habitual que la persona encargada del desarrollo de un proyecto no pueda utilizar el ordenador que se utilizará como servidor en producción por lo que tendrá que configurar su ordenador de trabajo con una configuración lo más semejante posible a la que tendrá ese servidor en producción. Esto casi siempre es imposible debido tanto a las diferencias en el hardware como en las posibles diferencias entre versiones y configuraciones del conjunto de librerías, procesos y programas de apoyo entre una máquina y otra.

En un primer momento la industria del software optó por el uso de máquinas virtuales pero esto implicaba que los equipos dedicados al desarrollo fuesen más potentes que los equipos de producción ya que la ejecución de una máquina virtual requería tanta potencia como la máquina anfitriona y la virtualizada.

Con el cambio de arquitectura de proyectos monolíticos a proyectos con microservicios la situación mejoró. En lugar de tener que utilizar una maquina completa para ejecutar toda la aplicación empaquetada en un único ejecutable (arquitectura monolítica) se pasó a la arquitectura de microservicios en la que un proyecto software completo se divide en muchos módulos pequeños y cada uno de estos módulos sólo se encarga de resolver uno de los problemas existiendo una comunicación interna entre cada uno de los microservicios que forman un proyecto completo.

Con este nuevo paradigma el uso de una máquina virtual para desarrollar un microservicio era innecesario ya que el microservicio requería de muy poca potencia para su ejecución tanto simulada durante el desarrollo como durante su explotación en producción. Era necesario la aparición de un nuevo método de virtualización que permitiese la ejecución de un microservicio que requiera sólo los recursos mínimos necesarios para su correcta ejecución y que al ejecutarse ya incluya todo lo necesario sin necesidad de depender de la máquina que lo ejecute. Nace Docker, un sistema de virtualización muy pequeño especializado en la ejecución de microservicios.

De esta forma un desarrollador instala Docker para crear sus entornos de ejecución y desarrollar su microservicio. Una vez desarrollado puede instalar Docker en la máquina de producción, trasladar el microservicio desarrollado y ejecutarlo en la máquina final. Como Docker facilita que el microservicio se ejecute siempre con la misma configuración en cualquier máquina ya no sucede el problema de que en la máquina de desarrollo todo iba bien y en producción todo va mal.

En conclusión podemos decir que Docker es una herramienta que permite ejecutar programas y aplicaciones de forma aislada, sin que se afecten entre sí. Es como una caja virtual que contiene todo lo que un programa necesita para funcionar correctamente. Esto facilita la instalación, ejecución y distribución de aplicaciones en diferentes máquinas.

Obtener el contenido del portapapeles con AppleScript

Ya hemos visto que en AppleScript podemos acceder a mucha de la información que está disponible en nuestro equipo pero aún no hemos visto una de las funciones más simples y útiles de los sistemas operativos modernos: el portapapeles.

El portapapeles es un espacio en memoria donde se aloja una copia del contenido que hayamos copiado o cortado con las funciones del sistema operativo.

¿Para qué podemos querer acceder a la información de portapapeles desde un script? Pues desde una simple función para mostrar el contenido del portapapeles en un cuadro de diálogo para verificar qué tenemos almacenado en esa memoria o para crear un script que nos traduzca el texto del portapapeles al idioma que necesitemos.

Con AppleScript podemos acceder a ese espacio de memoria utilizando la variable the clipboard. Esta variable de AppleScript puede almacenar cualquier tipo de contenido como imágenes, texto, audio o la información de un fichero de una carpeta. Esto nos obliga a convertir esta información en algo comprensible para nuestro script.

Veamos un ejemplo en el que obtenemos como texto la información guardada en el portapapeles:

set clipboardContent to (the clipboard as text)

Con esto podemos crear un script para verbalizar el contenido del portapapeles utilizando la voz del sistema. El código sería algo como esto:

set clipboardContent to the clipboard as text

say clipboardContent

Este código funciona pero no tiene en cuenta si en el portapapeles el contenido que está almacenado es convertible a texto. Para evitar posibles errores de ejecución de nuestro script debemos emplear un bloque try que nos permita controlar errores de ejecución en un script. El código quedaría así:

try

  set clipboardContent to the clipboard as text

  say clipboardContent

on error

  say "El portapapeles no contiene texto"

end try

Con esta modificación nuestro script siempre verbalizará algo aunque sea para notificarnos que el contenido de nuestro portapapeles no es texto.

Obtener la dirección IP de tu Mac con AppleScript

En AppleScript hay varias formas de obtener información sobre el estado de la máquina que está ejecutando un script. 

Una de esas formas es mediante system info.

Con get system info obtenemos un diccionario con información sobre qué versión de MacOS, nombre de usuario, nombre completo, dirección IPv4, memoria RAM instalada y más datos interesantes.

Ejemplo básico

Con esta información es muy sencillo crear un script de AppleScript que obtenga la información de la dirección Ip de nuestra máquina y mostrarla en un cuadro de diálogo.

El código sería algo como:

set ipText to IPv4 address of (get system info)
display alert "IP: " & ipText

Código limpio con comentarios útiles

Un código con buenos comentarios ayuda a su legibilidad como vimos en el artículo sobre cómo escribir código limpio y legible.

Cada bloque de código tiene un propósito, puede recibir parámetros y puede devolver un resultado. Toda esta información debería estar explicada brevemente en un bloque comentado, preferentemente antes de los bloques de ejecución. Por ejemplo:

// Función calculaPrecioFinalDeCarrito

//    Calcula el precio final del carrito ajustando impuestos y conversión de moneda

//

// Entrada:

//    lista de productos, tipo de impuestos, tipo de moneda

// Salida:

//    precio final a pagar por el cliente

function calculaPrecioFinalDeCarrito(listaProductos, tipoDeImpuestos, tipoMoneda) {

    var precioSinImpuestos = calculaPrecioDeListaDeProductos(listaProductos)

    var precioConImpuestos = calculaImpuestos(precioSinImpuestos, tipoDeImpuestos)

    var precioFinal = convierteAMoneda(precioConImpuestos, tipoMoneda)

    return precioFinal

}

Pero se debe ser breve y evitar comentar todo lo que hace el código línea a línea explicando sólo lo esencial.

Se debe explicar el porqué de las cosas y no cómo se hacen. Un buen código se da a entender por sí solo y no necesita extensos comentarios para ello.

Consistencia y legibilidad para un código limpio

En el artículo sobre cómo escribir código limpio y legible hablamos de la necesidad de coherencia cuando escribimos código.

Legibilidad por encima de la simplicidad

Algunos lenguajes de programación permiten realizar varias operaciones en una sola línea de código. Por ejemplo en Swift, Java o C++ podemos crear una instancia de clase, llamar a uno de sus métodos y realizar una comparación dependiendo del resultado de esa llamada todo en una sola línea de código. Esto reduce el número de líneas que pueda tener una función pero no mejora la legibilidad del código ya que quizás esas características del lenguaje de programación no son conocidas por todos los programadores.

Recuerda escribir el código para que lo pueda leer otra persona, porque incluso hay altas probabilidades que esa otra persona seas tú y puede que no entiendas tu propio código pasado unos meses o años.

Consistencia en la arquitectura de un proyecto

Una de las decisiones principales a tomar cuando empezamos un proyecto software es la de qué patrón de diseño utilizaremos para la arquitectura de nuestra aplicación.

Una vez tomada esta decisión debemos evitar mezclar arquitecturas ya que aumentamos la complejidad a la hora de entender cómo se organiza nuestro proyecto.