• No se han encontrado resultados

1. Planteamiento del Problema

1.2. Objetivos

1.3.2.3. Verificación de Resultados: las Pruebas de Algoritmos

Hasta este punto se ha tomado como premisa la existencia de una definición del problema, de la cual se identifican sus aspectos más relevantes, facilitando la estructuración de un conjunto de pasos que conllevarían a una solución adecuada y su representación algorítmica. El algoritmo es el citado resultado de este proceso de construcción, y conforme lo describe Harel (2004) en los

entradas asignadas a los problemas algorítmicos, y cumplan con las características esperadas para alimentar el algoritmo, sus resultados son invariantes independientemente del número de ocasiones en que sean ejecutados. Puede entones decirse que un problema ha sido resuelto en la medida que se ha construido el algoritmo que brinde una solución genérica y adecuada al problema durante cada ejecución del proceso, bajo la condición que se brinden entradas pertenecientes a un conjunto considerado permitido o legal.

Sin embargo, el comportamiento resultante del algoritmo puede no ser el esperado, bien sea por que la definición del problema sea ambigua o errónea, porque la estructuración de sus pasos no brinda una solución adecuada al problema, porque las estructuras de control que le conforman tienen algún tipo de desvío en su secuencia normal o por que los datos que le han sido ingresados pueden considerarse incorrectos o ilegales. De cualquier modo, puede entenderse que es interés y responsabilidad del problema algorítmico brindar tratamiento a cada una de estas vicisitudes con el fin de conservar su estructura y evitar resultados no contemplados o incorrectos, de acuerdo a lo manifestado por autores como Bond (2007) y Meinke (2004), entre otros.

Pero en este nivel surgen inquietudes diversas, por ejemplo: ¿Cómo es posible que un algoritmo no haga correctamente aquello para lo cual ha sido construido? Y si el algoritmo constituye una construcción mental orientada a la solución de un problema ¿Cómo esperar que tenga un comportamiento diferente al definido en su construcción y que sus resultados no sean los esperados? Si bien los algoritmos reflejan la manera en que su creador identifica las características de un problema, es él quien construye alrededor de la solución una estructura mental que le soporte, pero en pocas ocasiones, dicha estructura mental toma en consideración las diferentes variables que pueden ocasionar comportamientos no controlados o arrojar resultados no válidos. Podría decirse que la estructuración del algoritmo se orienta a que haga lo que tiene que hacer, pero en ningún momento a validar que no haga lo que no tiene que hacer. Es en este sentido que la verificación de un algoritmo debe ser concebida como un proceso a realizarse en diferentes etapas, permitiendo generar la trazabilidad de la solución desde el momento de su concepción hasta llegar a su aplicación en la vida diaria mediante su implementación en un lenguaje de programación.

La primera etapa en la verificación de un algoritmo corresponde a su “seguimiento mental” en el momento mismo de su concepción, haciendo un recorrido a través de su colección de pasos e identificando posibles inconsistencias en su estructura, de manera que permita evidenciar su idoneidad en la solución al problema planteado. En la Ilustración 12 puede observase que este tipo de verificación, si bien es simple en su aplicación, se hace compleja en la medida que el algoritmo se hace más denso, ya que el manejo de diversas características y sus transformaciones pueden estar más allá de la capacidad mental de quien lo construye. Así, el incremento en la complejidad limita el seguimiento mental, surgiendo la verificación a través de un registro escrito o “prueba de escritorio”.

Ilustración 12. Simplificación del proceso de seguimiento mental de un algoritmo. (Fuente: el autor)

La prueba de escritorio permite no solo observar el comportamiento del algoritmo paso a paso, sino que deja tras de sí un rastro físico de dicho comportamiento. Posee evidentes ventajas frente a la verificación mental, ya que no depende completamente de la memoria para el almacenamiento de los datos, facilitando en algunos casos devolverse sobre los pasos ya realizados, o inclusive partir de un resultado esperado y construir la serie de pasos necesarios para obtenerlo, algo que según Brandt (2002) puede entenderse como Backtracking. Este nivel de verificación requiere un mayor nivel de concentración en el seguimiento de los pasos ejecutados, dejando de lado la memorización del estado de las variables que son manipuladas por el algoritmo. Pero muy a pesar de sus ventajas, puede ser bastante monótono cuando se habla de algoritmos que poseen estructuras demasiado complejas o con un número alto de iteraciones. Tómese como ejemplo el contador presentado en la Ilustración 13, ¿Qué pasaría si en lugar de contar hasta tres lo hiciera hasta 100 o hasta 1’000.000? este es el motivo por el cual tiende a emplearse conjuntos pequeños de datos. El éxito de la prueba de escritorio consiste en registrar de manera detallada el comportamiento de las variables del algoritmo en cada paso.

Ilustración 13. Simplificación del proceso de seguimiento de un algoritmo empleando prueba de escritorio. (Fuente: el autor)

que los resultados obtenidos sean correspondientes con lo esperado. Este tipo de verificación se conoce como pruebas funcionales o testing.

Las pruebas funcionales, conforme son explicadas en los escritos de Patton (2001), Sommerville (2005) y Pressman (2005) se encaminan a la búsqueda de errores en la estructura funcional de un algoritmo, permitiendo identificar los puntos en los cuales ocurren y el impacto que pueden tener en su ejecución y los resultados obtenidos. En sus escritos es posible identificar al menos cuatro aproximaciones al proceso de ejecución de las pruebas: dos en consideración de la posibilidad de acceder al código (las pruebas de caja negra y las pruebas de caja blanca), y otras dos orientadas a la posibilidad de acceder a estructuras descriptivas o funcionales del software (las pruebas estáticas y las pruebas dinámicas). Ambos enfoques pueden ser complementarios en su aplicación, de forma que se logre una revisión más a profundidad de las capacidades del conjunto de algoritmos que soporta al software, partiendo de la premisa que cualquier algoritmo podrá ser probado de manera tan amplia como se desee, sin que por ello se considere que su verificación es exhaustiva.

En el caso de las pruebas estáticas, podría decirse que están más relacionadas con las etapas tempranas de definición del algoritmo que con la ejecución del mismo, ya que se orientan en mayor grado a observar que la especificación del problema sea consistente y que la estructuración de su solución pueda en sí, satisfacer las necesidades identificadas. Las pruebas dinámicas por su parte requieren de unidades algorítmicas que puedan ser ejecutadas o al menos verificables en su comportamiento a través de un seguimiento (p.ej., con una prueba de escritorio o unas pruebas funcionales).

En términos de ejecución de soluciones algorítmicas, las pruebas de caja negra solo permiten validar el comportamiento del algoritmo a partir de la relación entrada/salida, ignorando completamente el proceso de transformación intermedio y fundamentando la comparativa de éxito o fracaso simplemente en la definición de lo que el software está destinado a hacer. Por otra parte, en las pruebas de caja blanca es posible acceder al algoritmo que soporta la ejecución, facilitando la definición de estrategias de verificación en su estructura (p.ej., frente al uso de tipos de datos) conforme a la relación entrada/proceso/salida, haciendo evidente el proceso de transformación intermedio. Se esperaría entonces que las pruebas de caja blanca sean más efectivas que las pruebas de caja negra, sin embargo demandan de un mayor cuidado ya que es posible perder la objetividad durante su ejecución, ya que en ocasiones las pruebas pueden realizarse de una forma demasiado ajustada a la estructura de codificación, enfocándose simplemente en observar que el código haga lo que se supone debe hacer.