Está en la página 1de 35

UNIDAD 10: CICLO DE VIDA DEL SOFTWARE

Etimología

Software (pronunciación AFI:[soft'ɣware]) es una palabra proveniente del inglés


(literalmente: partes blandas o suaves), que en español no posee una traducción
adecuada al contexto, por lo cual se la utiliza asiduamente sin traducir y así fue
admitida por la Real Academia Española (RAE).2 Aunque no es estrictamente lo
mismo, suele sustituirse por expresiones tales como programas (informáticos) o
aplicaciones (informáticas).3

Software es lo que se denomina producto en Ingeniería de Software.4

Definición de software

Existen varias definiciones similares aceptadas para software, pero probablemente la


más formal sea la siguiente:

Es el conjunto de los programas de cómputo, procedimientos, reglas, documentación


y datos asociados que forman parte de las operaciones de un sistema de computación.

Extraído del estándar 729 del IEEE5

Considerando esta definición, el concepto de software va más allá de los programas


de computación en sus distintos estados: código fuente, binario o ejecutable; también
su documentación, los datos a procesar e incluso la información de usuario forman
parte del software: es decir, abarca todo lo intangible, todo lo «no físico»
relacionado.

El término «software» fue usado por primera vez en este sentido por John W. Tukey
en 1957. En la ingeniería de software y las ciencias de la computación, el software es
toda la información procesada por los sistemas informáticos: programas y datos.

El concepto de leer diferentes secuencias de instrucciones (programa) desde la


memoria de un dispositivo para controlar los cálculos fue introducido por Charles
Babbage como parte de su máquina diferencial. La teoría que forma la base de la
mayor parte del software moderno fue propuesta por Alan Turing en su ensayo de
1936, «Los números computables», con una aplicación al problema de decisión.

Clasificación del software


Si bien esta distinción es, en cierto modo, arbitraria, y a veces confusa, a los fines
prácticos se puede clasificar al software en tres grandes tipos:

 Software de sistema: Su objetivo es desvincular adecuadamente al usuario y al


programador de los detalles del sistema informático en particular que se use,
aislándolo especialmente del procesamiento referido a las características
internas de: memoria, discos, puertos y dispositivos de comunicaciones,
impresoras, pantallas, teclados, etc. El software de sistema le procura al usuario
y programador adecuadas interfaces de alto nivel, controladores, herramientas
y utilidades de apoyo que permiten el mantenimiento del sistema global.
Incluye entre otros:

o Sistemas operativos

o Controladores de dispositivos

o Herramientas de diagnóstico

o Herramientas de Corrección y Optimización

o Servidores

o Utilidades

 Software de programación: Es el conjunto de herramientas que permiten al


programador desarrollar programas informáticos, usando diferentes alternativas
y lenguajes de programación, de una manera práctica. Incluyen básicamente:

o Editores de texto

o Compiladores

o Intérpretes

o Enlazadores

o Depuradores

o Entornos de Desarrollo Integrados (IDE): Agrupan las anteriores


herramientas, usualmente en un entorno visual, de forma tal que el
programador no necesite introducir múltiples comandos para compilar,
interpretar, depurar, etc. Habitualmente cuentan con una avanzada
interfaz gráfica de usuario (GUI).
 Software de aplicación: Es aquel que permite a los usuarios llevar a cabo una
o varias tareas específicas, en cualquier campo de actividad susceptible de ser
automatizado o asistido, con especial énfasis en los negocios. Incluye entre
muchos otros:

o Aplicaciones para Control de sistemas y automatización industrial

o Aplicaciones ofimáticas

o Software educativo

o Software empresarial

o Bases de datos

o Telecomunicaciones (por ejemplo Internet y toda su estructura lógica)

o Videojuegos

o Software médico

o Software de cálculo Numérico y simbólico.

o Software de diseño asistido (CAD)

o Software de control numérico (CAM)

Proceso de creación del software

El proceso de creación de software puede llegar a ser muy complejo, dependiendo de


su porte, características y criticidad del mismo. Por ejemplo la creación de un sistema
operativo es una tarea que requiere proyecto, gestión, numerosos recursos y todo un
equipo disciplinado de trabajo. En el otro extremo, si se trata de un sencillo programa
(por ejemplo, la resolución de una ecuación de segundo orden), éste puede ser
realizado por un solo programador (incluso aficionado) fácilmente. Es así que
normalmente se dividen en tres categorías según su tamaño (líneas de código) o costo:
de «pequeño», «mediano» y «gran porte». Existen varias metodologías para
estimarlo, una de las más populares es el sistema COCOMO que provee métodos y
un software (programa) que calcula y provee una aproximación de todos los costos de
producción en un «proyecto software» (relación horas/hombre, costo monetario,
cantidad de líneas fuente de acuerdo a lenguaje usado, etc.).
Considerando los de gran porte, es necesario realizar complejas tareas, tanto técnicas
como de gerencia, una fuerte gestión y análisis diversos (entre otras cosas), la
complejidad de ello ha llevado a que desarrolle una ingeniería específica para tratar su
estudio y realización: es conocida como Ingeniería de Software.

En tanto que en los de mediano porte, pequeños equipos de trabajo (incluso un


avezado analista-programador solitario) pueden realizar la tarea. Aunque, siempre en
casos de mediano y gran porte (y a veces también en algunos de pequeño porte, según
su complejidad), se deben seguir ciertas etapas que son necesarias para la
construcción del software. Tales etapas, si bien deben existir, son flexibles en su
forma de aplicación, de acuerdo a la metodología o proceso de desarrollo escogido y
utilizado por el equipo de desarrollo o por el analista-programador solitario (si fuere
el caso).

Los «procesos de desarrollo de software» poseen reglas preestablecidas, y deben ser


aplicados en la creación del software de mediano y gran porte, ya que en caso
contrario lo más seguro es que el proyecto o no logre concluir o termine sin cumplir
los objetivos previstos, y con variedad de fallos inaceptables (fracasan, en pocas
palabras). Entre tales «procesos» los hay ágiles o livianos (ejemplo XP), pesados y
lentos (ejemplo RUP), y variantes intermedias. Normalmente se aplican de acuerdo al
tipo y porte del software a desarrollar, a criterio del líder (si lo hay) del equipo de
desarrollo. Algunos de esos procesos son Programación Extrema (en inglés eXtreme
Programming o XP), Proceso Unificado de Rational (en inglés Rational Unified
Process o RUP), Feature Driven Development (FDD), etc.

Cualquiera sea el «proceso» utilizado y aplicado al desarrollo del software (RUP,


FDD, XP, etc), y casi independientemente de él, siempre se debe aplicar un «modelo
de ciclo de vida».6

Se estima que, del total de proyectos software grandes emprendidos, un 28% fracasan,
un 46% caen en severas modificaciones que lo retrasan y un 26% son totalmente
exitosos. 7

Cuando un proyecto fracasa, rara vez es debido a fallas técnicas, la principal causa de
fallos y fracasos es la falta de aplicación de una buena metodología o proceso de
desarrollo. Entre otras, una fuerte tendencia, desde hace pocas décadas, es mejorar las
metodologías o procesos de desarrollo, o crear nuevas y concientizar a los
profesionales de la informática a su utilización adecuada. Normalmente los
especialistas en el estudio y desarrollo de estas áreas (metodologías) y afines (tales
como modelos y hasta la gestión misma de los proyectos) son los ingenieros en
software, es su orientación. Los especialistas en cualquier otra área de desarrollo
informático (analista, programador, Lic. en informática, ingeniero en informática,
ingeniero de sistemas, etc.) normalmente aplican sus conocimientos especializados
pero utilizando modelos, paradigmas y procesos ya elaborados.

Es común para el desarrollo de software de mediano porte que los equipos humanos
involucrados apliquen «metodologías propias», normalmente un híbrido de los
procesos anteriores y a veces con criterios propios.

El proceso de desarrollo puede involucrar numerosas y variadas tareas 6 , desde lo


administrativo, pasando por lo técnico y hasta la gestión y el gerenciamiento. Pero,
casi rigurosamente, siempre se cumplen ciertas etapas mínimas; las que se pueden
resumir como sigue:

 Captura, elicitación8 , especificación y análisis de requisitos (ERS)

 Diseño

 Codificación

 Pruebas (unitarias y de integración)

 Instalación y paso a producción

 Mantenimiento

En las anteriores etapas pueden variar ligeramente sus nombres, o ser más globales, o
contrariamente, ser más refinadas; por ejemplo indicar como una única fase (a los
fines documentales e interpretativos) de «análisis y diseño»; o indicar como
«implementación» lo que está dicho como «codificación»; pero en rigor, todas existen
e incluyen, básicamente, las mismas tareas específicas.

En el apartado 4 del presente artículo se brindan mayores detalles de cada una de las
etapas indicadas.

Modelos de proceso o ciclo de vida

Para cada una de las fases o etapas listadas en el ítem anterior, existen sub-etapas (o
tareas). El modelo de proceso o modelo de ciclo de vida utilizado para el desarrollo
define el orden para las tareas o actividades involucradas6 también definen la
coordinación entre ellas, y su enlace y realimentación. Entre los más modelos
conocidos se puede mencionar: modelo en cascada o secuencial, modelo espiral,
modelo iterativo incremental. De los antedichos hay a su vez algunas variantes o
alternativas, más o menos atractivas según sea la aplicación requerida y sus
requisitos.7

Modelo cascada

Este, aunque es más comúnmente conocido como modelo en cascada es también


llamado «modelo clásico», «modelo tradicional» o «modelo lineal secuencial».

El modelo en cascada puro difícilmente se utiliza tal cual, pues esto implicaría un
previo y absoluto conocimiento de los requisitos, la no volatilidad de los mismos (o
rigidez) y etapas subsiguientes libres de errores; ello sólo podría ser aplicable a
escasos y pequeños sistemas a desarrollar. En estas circunstancias, el paso de una
etapa a otra de las mencionadas sería sin retorno, por ejemplo pasar del diseño a la
codificación implicaría un diseño exacto y sin errores ni probable modificación o
evolución: «codifique lo diseñado sin errores, no habrá en absoluto variantes futuras».
Esto es utópico; ya que intrínsecamente el software es de carácter evolutivo9 ,
cambiante y difícilmente libre de errores, tanto durante su desarrollo como durante su
vida operativa.6

Fig. 2 - Modelo cascada puro o secuencial para el ciclo de vida del software.

Algún cambio durante la ejecución de una cualquiera de las etapas en este modelo
secuencial implicaría reiniciar desde el principio todo el ciclo completo, lo cual
redundaría en altos costos de tiempo y desarrollo. La figura 2 muestra un posible
esquema del modelo en cuestión.6

Sin embargo, el modelo cascada en algunas de sus variantes es uno de los actualmente
más utilizados10 , por su eficacia y simplicidad, más que nada en software de pequeño
y algunos de mediano porte; pero nunca (o muy rara vez) se lo usa en su "forma
pura", como se dijo anteriormente. En lugar de ello, siempre se produce alguna
realimentación entre etapas, que no es completamente predecible ni rígida; esto da
oportunidad al desarrollo de productos software en los cuales hay ciertas incertezas,
cambios o evoluciones durante el ciclo de vida. Así por ejemplo, una vez capturados y
especificados los requisitos (primera etapa) se puede pasar al diseño del sistema, pero
durante esta última fase lo más probable es que se deban realizar ajustes en los
requisitos (aunque sean mínimos), ya sea por fallas detectadas, ambigüedades o bien
por que los propios requisitos han cambiado o evolucionado; con lo cual se debe
retornar a la primera o previa etapa, hacer los reajuste pertinentes y luego continuar
nuevamente con el diseño; esto último se conoce como realimentación. Lo normal en
el modelo cascada será entonces la aplicación del mismo con sus etapas
realimentadas de alguna forma, permitiendo retroceder de una a la anterior (e incluso
poder saltar a varias anteriores) si es requerido.

De esta manera se obtiene el «modelo cascada realimentado», que puede ser


esquematizado como lo ilustra la figura 3.

Fig. 3 - Modelo cascada realimentado para el ciclo de vida.

Lo dicho es, a grandes rasgos, la forma y utilización de este modelo, uno de los más
usados y populares.6 El modelo cascada realimentado resulta muy atractivo, hasta
ideal, si el proyecto presenta alta rigidez (pocos cambios, previsto no evolutivo), los
requisitos son muy claros y están correctamente especificados.10

Hay más variantes similares al modelo: refino de etapas (más etapas, menores y más
específicas) o incluso mostrar menos etapas de las indicadas, aunque en tal caso la
faltante estará dentro de alguna otra. El orden de esas fases indicadas en el ítem
previo es el lógico y adecuado, pero adviértase, como se dijo, que normalmente habrá
realimentación hacia atrás.

El modelo lineal o en cascada es el paradigma más antiguo y extensamente utilizado,


sin embargo las críticas a él (ver desventajas) han puesto en duda su eficacia. Pese a
todo, tiene un lugar muy importante en la Ingeniería de software y continúa siendo el
más utilizado; y siempre es mejor que un enfoque al azar.10

Desventajas del modelo cascada:6

 Los cambios introducidos durante el desarrollo pueden confundir al equipo


profesional en las etapas tempranas del proyecto. Si los cambios se producen en
etapa madura (codificación o prueba) pueden ser catastróficos para un proyecto
grande.

 No es frecuente que el cliente o usuario final explicite clara y completamente


los requisitos (etapa de inicio); y el modelo lineal lo requiere. La incertidumbre
natural en los comienzos es luego difícil de acomodar.10

 El cliente debe tener paciencia ya que el software no estará disponible hasta


muy avanzado el proyecto. Un error detectado por el cliente (en fase de
operación) puede ser desastroso, implicando reinicio del proyecto, con altos
costos.

Modelos evolutivos

El software evoluciona con el tiempo.11 9 Los requisitos del usuario y del producto
suelen cambiar conforme se desarrolla el mismo. Las fechas de mercado y la
competencia hacen que no sea posible esperar a poner en el mercado un producto
absolutamente completo, por lo que se aconsejable introducir una versión funcional
limitada de alguna forma para aliviar las presiones competitivas.

En esas u otras situaciones similares los desarrolladores necesitan modelos de


progreso que estén diseñados para acomodarse a una evolución temporal o progresiva,
donde los requisitos centrales son conocidos de antemano, aunque no estén bien
definidos a nivel detalle.

En el modelo cascada y cascada realimentado no se tiene demasiado en cuenta la


naturaleza evolutiva del software11 , se plantea como estático, con requisitos bien
conocidos y definidos desde el inicio.6
Los evolutivos son modelos iterativos, permiten desarrollar versiones cada vez más
completas y complejas, hasta llegar al objetivo final deseado; incluso evolucionar más
allá, durante la fase de operación.

Los modelos «iterativo incremental» y «espiral» (entre otros) son dos de los más
conocidos y utilizados del tipo evolutivo.10

Modelo iterativo incremental

En términos generales, se puede distinguir, en la figura 4, los pasos generales que


sigue el proceso de desarrollo de un producto software. En el modelo de ciclo de vida
seleccionado, se identifican claramente dichos pasos. La descripción del sistema es
esencial para especificar y confeccionar los distintos incrementos hasta llegar al
producto global y final. Las actividades concurrentes (especificación, desarrollo y
validación) sintetizan el desarrollo pormenorizado de los incrementos, que se hará
posteriormente.

Fig. 4 - Diagrama genérico del desarrollo evolutivo incremental.

El diagrama 4 muestra en forma muy esquemática, el funcionamiento de un ciclo


iterativo incremental, el cual permite la entrega de versiones parciales a medida que
se va construyendo el producto final.6 Es decir, a medida que cada incremento
definido llega a su etapa de operación y mantenimiento. Cada versión emitida
incorpora a los anteriores incrementos las funcionalidades y requisitos que fueron
analizados como necesarios.

El incremental es un modelo de tipo evolutivo que está basado en varios ciclos


Cascada Realimentados aplicados repetidamente, con una filosofía iterativa.10 En la
figura 5 se muestra un refino del diagrama previo, bajo un esquema temporal, para
obtener finalmente el esquema del modelo de ciclo de vida Iterativo Incremental, con
sus actividades genéricas asociadas. Aquí se observa claramente cada ciclo cascada
que es aplicado para la obtención de un incremento; estos últimos se van integrando
para obtener el producto final completo. Cada incremento es un ciclo Cascada
Realimentado, aunque, por simplicidad, en la figura 5 se muestra como secuencial
puro.

Fig. 5 - Modelo iterativo incremental para el ciclo de vida del software,.

Se observa que existen actividades de desarrollo (para cada incremento) que son
realizadas en paralelo o concurrentemente, así por ejemplo, en la figura, mientras se
realiza el diseño detalle del primer incremento ya se está realizando en análisis del
segundo. La figura 5 es sólo esquemática, un incremento no necesariamente se
iniciará durante la fase de diseño del anterior, puede ser posterior (incluso antes), en
cualquier tiempo de la etapa previa. Cada incremento concluye con la actividad de
«operación y mantenimiento» (indicada como «Operación» en la figura), que es
donde se produce la entrega del producto parcial al cliente. El momento de inicio de
cada incremento es dependiente de varios factores: tipo de sistema; independencia o
dependencia entre incrementos (dos de ellos totalmente independientes pueden ser
fácilmente iniciados al mismo tiempo si se dispone de personal suficiente); capacidad
y cantidad de profesionales involucrados en el desarrollo; etc.

Bajo este modelo se entrega software «por partes funcionales más pequeñas», pero
reutilizables, llamadas incrementos. En general cada incremento se construye sobre
aquel que ya fue entregado.6

Como se muestra en la figura 5, se aplican secuencias Cascada en forma escalonada,


mientras progresa el tiempo calendario. Cada secuencia lineal o Cascada produce un
incremento y a menudo el primer incremento es un sistema básico, con muchas
funciones suplementarias (conocidas o no) sin entregar.

El cliente utiliza inicialmente ese sistema básico, intertanto, el resultado de su uso y


evaluación puede aportar al plan para el desarrollo del/los siguientes incrementos (o
versiones). Además también aportan a ese plan otros factores, como lo es la
priorización (mayor o menor urgencia en la necesidad de cada incremento en
particular) y la dependencia entre incrementos (o independencia).

Luego de cada integración se entrega un producto con mayor funcionalidad que el


previo. El proceso se repite hasta alcanzar el software final completo.

Siendo iterativo, con el modelo incremental se entrega un producto parcial pero


completamente operacional en cada incremento, y no una parte que sea usada para
reajustar los requerimientos (como si ocurre en el modelo de construcción de
prototipos).10

El enfoque incremental resulta muy útil cuando se dispone de baja dotación de


personal para el desarrollo; también si no hay disponible fecha límite del proyecto por
lo que se entregan versiones incompletas pero que proporcionan al usuario
funcionalidad básica (y cada vez mayor). También es un modelo útil a los fines de
versiones de evaluación.

Nota: Puede ser considerado y útil, en cualquier momento o incremento incorporar


temporalmente el paradigma MCP como complemento, teniendo así una mixtura de
modelos que mejoran el esquema y desarrollo general.

Ejemplo:

Un procesador de texto que sea desarrollado bajo el paradigma Incremental podría


aportar, en principio, funciones básicas de edición de archivos y producción de
documentos (algo como un editor simple). En un segundo incremento se le podría
agregar edición más sofisticada, y de generación y mezcla de documentos. En un
tercer incremento podría considerarse el agregado de funciones de corrección
ortográfica, esquemas de paginado y plantillas; en un cuarto capacidades de dibujo
propias y ecuaciones matemáticas. Así sucesivamente hasta llegar al procesador final
requerido. Así, el producto va creciendo, acercándose a su meta final, pero desde la
entrega del primer incremento ya es útil y funcional para el cliente, el cual observa
una respuesta rápida en cuanto a entrega temprana; sin notar que la fecha límite del
proyecto puede no estar acotada ni tan definida, lo que da margen de operación y
alivia presiones al equipo de desarrollo.

Como se dijo, el Iterativo Incremental es un modelo del tipo evolutivo, es decir donde
se permiten y esperan probables cambios en los requisitos en tiempo de desarrollo; se
admite cierto margen para que el software pueda evolucionar9 . Aplicable cuando los
requisitos son medianamente bien conocidos pero no son completamente estáticos y
definidos, cuestión esa que si es indispensable para poder utilizar un modelo Cascada.

El modelo es aconsejable para el desarrollo de software en el cual se observe, en su


etapa inicial de análisis, que posee áreas bastante bien definidas a cubrir, con
suficiente independencia como para ser desarrolladas en etapas sucesivas. Tales áreas
a cubrir suelen tener distintos grados de apremio por lo cual las mismas se deben
priorizar en un análisis previo, es decir, definir cual será la primera, la segunda, y así
sucesivamente; esto se conoce como «definición de los incrementos» con base en la
priorización. Pueden no existir prioridades funcionales por parte del cliente, pero el
desarrollador debe fijarlas de todos modos y con algún criterio, ya que basándose en
ellas se desarrollarán y entregarán los distintos incrementos.

El hecho de que existan incrementos funcionales del software lleva inmediatamente a


pensar en un esquema de desarrollo modular, por tanto este modelo facilita tal
paradigma de diseño.

En resumen, un modelo incremental lleva a pensar en un desarrollo modular, con


entregas parciales del producto software denominados «incrementos» del sistema, que
son escogidos según prioridades predefinidas de algún modo. El modelo permite una
implementación con refinamientos sucesivos (ampliación o mejora). Con cada
incremento se agrega nueva funcionalidad o se cubren nuevos requisitos o bien se
mejora la versión previamente implementada del producto software.

Este modelo brinda cierta flexibilidad para que durante el desarrollo se incluyan
cambios en los requisitos por parte del usuario, un cambio de requisitos propuesto y
aprobado puede analizarse e implementarse como un nuevo incremento o,
eventualmente, podrá constituir una mejora/adecuación de uno ya planeado. Aunque
si se produce un cambio de requisitos por parte del cliente que afecte incrementos
previos ya terminados (detección/incorporación tardía) se debe evaluar la factibilidad
y realizar un acuerdo con el cliente, ya que puede impactar fuertemente en los costos.
La selección de este modelo permite realizar entregas funcionales tempranas al
cliente (lo cual es beneficioso tanto para él como para el grupo de desarrollo). Se
priorizan las entregas de aquellos módulos o incrementos en que surja la necesidad
operativa de hacerlo, por ejemplo para cargas previas de información, indispensable
para los incrementos siguientes.10

El modelo iterativo incremental no obliga a especificar con precisión y detalle


absolutamente todo lo que el sistema debe hacer, (y cómo), antes de ser construido
(como el caso del cascada, con requisitos congelados). Sólo se hace en el incremento
en desarrollo. Esto torna más manejable el proceso y reduce el impacto en los costos.
Esto es así, porque en caso de alterar o rehacer los requisitos, solo afecta una parte del
sistema. Aunque, lógicamente, esta situación se agrava si se presenta en estado
avanzado, es decir en los últimos incrementos. En definitiva, el modelo facilita la
incorporación de nuevos requisitos durante el desarrollo.

Con un paradigma incremental se reduce el tiempo de desarrollo inicial, ya que se


implementa funcionalidad parcial. También provee un impacto ventajoso frente al
cliente, que es la entrega temprana de partes operativas del software.

El modelo proporciona todas las ventajas del modelo en cascada realimentado,


reduciendo sus desventajas sólo al ámbito de cada incremento.

El modelo incremental no es recomendable para casos de sistemas de tiempo real, de


alto nivel de seguridad, de procesamiento distribuido, o de alto índice de riesgos.

Modelo espiral

El modelo espiral fue propuesto inicialmente por Barry Boehm. Es un modelo


evolutivo que conjuga la naturaleza iterativa del modelo MCP con los aspectos
controlados y sistemáticos del Modelo Cascada. Proporciona potencial para desarrollo
rápido de versiones incrementales. En el modelo Espiral el software se construye en
una serie de versiones incrementales. En las primeras iteraciones la versión
incremental podría ser un modelo en papel o bien un prototipo. En las últimas
iteraciones se producen versiones cada vez más completas del sistema diseñado.6 10

El modelo se divide en un número de Actividades de marco de trabajo, llamadas


«regiones de tareas». En general existen entre tres y seis regiones de tareas (hay
variantes del modelo). En la figura 6 se muestra el esquema de un Modelo Espiral con
6 regiones. En este caso se explica una variante del modelo original de Boehm,
expuesto en su tratado de 1988; en 1998 expuso un tratado más reciente.
Fig. 6 - Modelo espiral para el ciclo de vida del software.

Las regiones definidas en el modelo de la figura son:

 Región 1 - Tareas requeridas para establecer la comunicación entre el cliente y


el desarrollador.

 Región 2 - Tareas inherentes a la definición de los recursos, tiempo y otra


información relacionada con el proyecto.

 Región 3 - Tareas necesarias para evaluar los riesgos técnicos y de gestión del
proyecto.

 Región 4 - Tareas para construir una o más representaciones de la aplicación


software.

 Región 5 - Tareas para construir la aplicación, instalarla, probarla y


proporcionar soporte al usuario o cliente (Ej. documentación y práctica).

 Región 6 - Tareas para obtener la reacción del cliente, según la evaluación de lo


creado e instalado en los ciclos anteriores.

Las actividades enunciadas para el marco de trabajo son generales y se aplican a


cualquier proyecto, grande, mediano o pequeño, complejo o no. Las regiones que
definen esas actividades comprenden un «conjunto de tareas» del trabajo: ese
conjunto sí se debe adaptar a las características del proyecto en particular a
emprender. Nótese que lo listado en los ítems de 1 a 6 son conjuntos de tareas,
algunas de las ellas normalmente dependen del proyecto o desarrollo en si.

Proyectos pequeños requieren baja cantidad de tareas y también de formalidad. En


proyectos mayores o críticos cada región de tareas contiene labores de más alto nivel
de formalidad. En cualquier caso se aplican actividades de protección (por ejemplo,
gestión de configuración del software, garantía de calidad, etc.).

Al inicio del ciclo, o proceso evolutivo, el equipo de ingeniería gira alrededor del
espiral (metafóricamente hablando) comenzando por el centro (marcado con ๑ en la
figura 6) y en el sentido indicado; el primer circuito de la espiral puede producir el
desarrollo de una especificación del producto; los pasos siguientes podrían generar un
prototipo y progresivamente versiones más sofisticadas del software.

Cada paso por la región de planificación provoca ajustes en el plan del proyecto; el
coste y planificación se realimentan en función de la evaluación del cliente. El gestor
de proyectos debe ajustar el número de iteraciones requeridas para completar el
desarrollo.

El modelo espiral puede ir adaptándose y aplicarse a lo largo de todo el Ciclo de vida


del software (en el modelo clásico, o cascada, el proceso termina a la entrega del
software).

Una visión alternativa del modelo puede observarse examinando el «eje de punto de
entrada de proyectos». Cada uno de los circulitos (๏) fijados a lo largo del eje
representan puntos de arranque de los distintos proyectos (relacionados); a saber:

 Un proyecto de «desarrollo de conceptos» comienza al inicio de la espiral, hace


múltiples iteraciones hasta que se completa, es la zona marcada con verde.

 Si lo anterior se va a desarrollar como producto real, se inicia otro proyecto:


«Desarrollo de nuevo Producto». Que evolucionará con iteraciones hasta
culminar; es la zona marcada en color azul.

 Eventual y análogamente se generarán proyectos de «mejoras de productos» y


de «mantenimiento de productos», con las iteraciones necesarias en cada área
(zonas roja y gris, respectivamente).

Cuando la espiral se caracteriza de esta forma, está operativa hasta que el software
se retira, eventualmente puede estar inactiva (el proceso), pero cuando se produce un
cambio el proceso arranca nuevamente en el punto de entrada apropiado (por ejemplo,
en «mejora del producto»).

El modelo espiral da un enfoque realista, que evoluciona igual que el software 11 ; se


adapta muy bien para desarrollos a gran escala.

El Espiral utiliza el MCP para reducir riesgos y permite aplicarlo en cualquier etapa
de la evolución. Mantiene el enfoque clásico (cascada) pero incorpora un marco de
trabajo iterativo que refleja mejor la realidad.

Este modelo requiere considerar riesgos técnicos en todas las etapas del proyecto;
aplicado adecuadamente debe reducirlos antes de que sean un verdadero problema.

El Modelo evolutivo como el Espiral es particularmente apto para el desarrollo de


Sistemas Operativos (complejos); también en sistemas de altos riesgos o críticos (Ej.
navegadores y controladores aeronáuticos) y en todos aquellos en que sea necesaria
una fuerte gestión del proyecto y sus riesgos, técnicos o de gestión.

Desventajas importantes:

 Requiere mucha experiencia y habilidad para la evaluación de los riesgos, lo


cual es requisito para el éxito del proyecto.

 Es difícil convencer a los grandes clientes que se podrá controlar este enfoque
evolutivo.

Este modelo no se ha usado tanto, como el Cascada (Incremental) o MCP, por lo que
no se tiene bien medida su eficacia, es un paradigma relativamente nuevo y difícil de
implementar y controlar.

Depuracion de programas

1. Introducción

Una de las últimas fases del ciclo de vida antes de entregar un programa para su
explotación, es la fase de pruebas.

Una de las sorpresas con las que suelen encontrar los nuevos programadores es la
enorme cantidad de tiempo y esfuerzo que requiere esta fase. Se estima que la mitad
del esfuerzo de desarrollo de un programa (tanto en tiempo como en gastos) se va en
esta fase. Si hablamos de programas que involucran vidas humanas (medicina,
equipos nucleares, etc) el costo de la fase de pruebas puede fácilmente superar el
80%.

Pese a su enorme impacto en el coste de desarrollo, es una fase que muchos


programadores aún consideran clasificable como un arte y, por tanto, como
difícilmente conceptualizable. Es muy difícil entrenar a los nuevos programadores,
que aprenderán mucho más de su experiencia que de lo que les cuenten en los cursos
de programación.

Aún siendo una tarea abocada al fracaso, voy a intentarlo.

1.1. ¿Qué es probar?

Probar un programa es ejercitarlo con la peor intención a fin de encontrarle fallos.

1.2. Organización

Hay multitud de conceptos (y palabras clave) asociadas a las tareas de prueba.


Clasificarlas es difícil, pues no son mutuamente disjuntas, sino muy entrelazadas. En
lo que sigue intentaremos la siguiente estructura para la presentación:

Fases de prueba:

 UNIDADES
Planteamientos:

o CAJA BLANCA
Cobertura:

 de segmentos

 de ramas

 de condición/decisión

 de bucles

o CAJA NEGRA
Cobertura de requisitos

 INTEGRACIÓN

 ACEPTACIÓN
La prueba de unidades se plantea a pequeña escala, y consiste en ir probando uno a
uno los diferentes módulos que constituyen una aplicación.

Las pruebas de integración y de aceptación son pruebas a mayor escala, que puede
llegar a dimensiones industriales cuando el número de módulos es muy elevado, o la
funcionalidad que se espera del programa es muy compleja.

Las pruebas de integración se centran en probar la coherencia semántica entre los


diferentes módulos, tanto de semántica estática (se importan los módulos adecuados;
se llama correctamente a los procedimientos proporcionados por cada módulo), como
de semántica dinámica (un módulo recibe de otro lo que esperaba). Normalmente
estas pruebas se van realizando por etapas, englobando progresivamente más y más
módulos en cada prueba.

Las pruebas de integración se pueden empezar en cuanto tenemos unos pocos


módulos, aunque no terminarán hasta disponer de la totalidad. En un diseño
descendente (top-down) se empieza a probar por los módulos más generales; mientras
que en un diseño ascendente se empieza a probar por los módulos de base.

El planteamiento descendente tiene la ventaja de estar siempre pensando en términos


de la funcionalidad global; pero también tiene el inconveniente de que para cada
prueba hay que "inventarse" algo sencillito (pero fiable) que simule el papel de los
módulos inferiores, que aún no están disponibles.

El planteamiento ascendente evita tener que escribirse módulos ficticios, pues vamos
construyendo pirámides más y más altas con lo que vamos teniendo. Su desventaja es
que se centra más en el desarrollo que en las espectativas finales del cliente.

Estas clasificaciones no son las únicas posibles. Por ejemplo, en sistemas con mucha
interacción con el usuario es frecuente codificar sólo las partes de cada módulo que
hacen falta para una cierta funcionalidad. Una vez probada, se añade otra
funcionalidad y así hasta el final. Esto da lugar a un planteamiento más "vertical" de
las pruebas. A veces se conoce como "codificación incremental".

Por último, las pruebas de aceptación son las que se plantea el cliente final, que
decide qué pruebas va a aplicarle al producto antes de darlo por bueno y pagarlo. De
nuevo, el objetivo del que prueba es encontrar los fallos lo antes posible, en todo caso
antes de pagarlo y antes de poner el programa en producción.

2. Prueba de Unidades
¿Cómo se prueban módulos sueltos?

Normalmente cabe distinguir una fase informal antes de entrar en la fase de pruebas
propiamente dicha. La fase informal la lleva a cabo el propio codificador en su
despacho, y consiste en ir ejecutando el código para convencerse de que
"básicamente, funciona". Esta fase suele consistir en pequeños ejemplos que se
intentan ejecutar. Si el módulo falla, se suele utilizar un depurador para observar la
evolución dinámica del sistema, localizar el fallo, y repararlo.

En lenguajes antiguos, poco rigurosos en la sintaxis y/o en la semantica de los


programas, esta fase informal llega a ser muy dura, laboriosa, y susceptible de dejar
pasar grandes errores sin que se note. En lenguajes modernos, con reglas estrictas, hay
herramientas que permiten análisis exhaustivos de los aspectos estáticos de la
semántica de los programas: tipado de las variables, ámbitos de visibilidad,
parámetros de llamada a procedimientos, etc etc

Hay asimismo herramientas más sofisticadas capaces de emitir "opiniones" sobre un


programa y alertar de construcciones arriesgadas, de expresiones muy complicadas
(que se prestan a equivocaciones), etc. etc. A veces pueden prevenir sobre variables
que pueden usarse antes de tomar algún valor (no inicializadas), variables que se
cargan pero luego no se usan, y otras posibilidades que, sin ser necesariamente errores
en sí mismas, sí suelen apuntar a errores de verdad.

Más adelante, cuando el módulo parece presentable, se entra en una fase de prueba
sistemática. En esta etapa se empieza a buscar fallos siguiendo algún criterio para que
"no se escape nada". Los criterios más habituales son los denominados de caja negra y
de caja blanca.

Se dice que una prueba es de caja negra cuando prescinde de los detalles del código y
se limita a lo que se ve desde el exterior. Intenta descubrir casos y circunstancias en
los que el módulo no hace lo que se espera de él.

Por oposición al término "caja negra" se suele denominar "caja blanca" al caso
contrario, es decir, cuando lo que se mira con lupa es el código que está ahí escrito y
se intenta que falle. Quizás sea más propio la denominación de "pruebas de caja
transparente".

2.1. Caja blanca

Sinónimos:
 pruebas estructurales

 pruebas de caja transparente

En estas pruebas estamos siempre observando el código, que las pruebas se dedican a
ejecutar con ánimo de "probarlo todo". Esta noción de prueba total se formaliza en lo
que se llama "cobertura" y no es sino una medida porcentual de ¿cuánto código hemos
cubierto?

Hay diferentes posibilidades de definir la cobertura. Todas ellas intentan sobrevivir al


hecho de que el número posible de ejecuciones de cualquier programa no trivial es (a
todos los efectos prácticos) infinito. Pero si el 100% de cobertura es infinito, ningún
conjunto real de pruebas pasaría de un infinitésimo de cobertura. Esto puede ser muy
interesante para los matemáticos; pero no sirve para nada.

Cobertura de segmentos

A veces también denominada "cobertura de sentencias". Por segmento se entiende una


secuencia de sentencias sin puntos de decisión. Como el ordenador está obligado a
ejecutarlas una tras otra, es lo mismo decir que se han ejecutado todas las sentencias o
todos los segmentos.

El número de sentencias de un programa es finito. Basta coger el código fuente e ir


contando. Se puede diseñar un plan de pruebas que vaya ejercitando más y más
sentencias, hasta que hayamos pasado por todas, o por una inmensa mayoría.

En la práctica, el proceso de pruebas termina antes de llegar al 100%, pues puede ser
excesivamente laborioso y costoso provocar el paso por todas y cada una de las
sentencias.

A la hora de decidir el punto de corte antes de llegar al 100% de cobertura hay que ser
precavido y tomar en consideración algo más que el índice conseguido. En efecto,
ocurre con harta frecuencia que los programas contienen código muerto o
inalcanzable. Puede ser que este trozo del programa, simplemente "sobre" y se pueda
prescindir de él; pero a veces significa que una cierta funcionalidad, necesaria, es
inalcanzable: esto es un error y hay que corregirlo.

Cobertura de ramas

La cobertura de segmentos es engañosa en presencia de segmentos opcionales. Por


ejemplo:
IF Condicion THEN EjecutaEsto; END;

Desde el punto de vista de cobertura de segmentos, basta ejecutar una vez, con éxito
en la condición, para cubrir todas las sentencias posibles. Sin embargo, desde el punto
de vista de la lógica del programa, también debe ser importante el caso de que la
condición falle (si no lo fuera, sobra el IF). Sin embargo, como en la rama ELSE no
hay sentencias, con 0 ejecuciones tenemos el 100%.

Para afrontar estos casos, se plantea un refinamiento de la cobertura de segmentos


consistente en recorrer todas las posibles salidas de los puntos de decisión. Para el
ejemplo de arriba, para conseguir una cobertura de ramas del 100% hay que ejecutar
(al menos) 2 veces, una satisfaciendo la condición, y otra no.

Estos criterios se extienden a las construcciones que suponen elegir 1 de entre varias
ramas. Por ejemplo, el CASE.

Nótese que si lograramos una cobertura de ramas del 100%, esto llevaría implícita
una cobertura del 100% de los segmentos, pues todo segmento está en alguna rama.
Esto es cierto salvo en programas triviales que carecen de condiciones (a cambio,
basta 1 sóla prueba para cubrirlo desde todos los puntos de vista). El criterio también
debe refinarse en lenguajes que admiten excepciones (por ejemplo, Ada). En estos
casos, hay que añadir pruebas para provocar la ejecución de todas y cada una de las
excepciones que pueden dispararse.

Cobertura de condición/decisión

La cobertura de ramas resulta a su vez engañosa cuando las expresiones booleanas


que usamos para decidir por qué rama tirar son complejas. Por ejemplo:

IF Condicion1 OR Condicion2 THEN HazEsto; END;

Las condiciones 1 y 2 pueden tomar 2 valores cada una, dando lugar a 4 posibles
combinaciones. No obstante sólo hay dos posibles ramas y bastan 2 pruebas para
cubrirlas. Pero con este criterio podemos estar cerrando los ojos a otras
combinaciones de las condiciones.

Consideremos sobre el caso anterior las siguientes pruebas:

Prueba 1: Condicion1 = TRUE y Condicion2 = FALSE

Prueba 2: Condicion1 = FALSE y Condicion2 = TRUE


Prueba 3: Condicion1 = FALSE y Condicion2 = FALSE

Prueba 4: Condicion1 = TRUE y Condicion2 = TRUE

Bastan las pruebas 2 y 3 para tener cubiertas todas las ramas. Pero con ellos sólo
hemos probado una posibilidad para la Condición1.

Para afrontar esta problemática se define un criterio de cobertura de


condición/decisión que trocea las expresiones booleanas complejas en sus
componentes e intenta cubrir todos los posibles valores de cada uno de ellos.

Nótese que no basta con cubrir cada una de las condciones componentes, si no que
además hay que cuidar de sus posibles combinaciones de forma que se logre siempre
probar todas y cada una de las ramas. Así, en el ejemplo anterior no basta con ejecutar
las pruebas 1 y 2, pues aun cuando cubrimos perfectamente cada posibilidad de cada
condición por separado, lo que no hemos logrado es recorrer las dos posibles ramas de
la decisión combinada. Para ello es necesario añadir la prueba 3.

El conjunto mínimo de pruebas para cubrir todos los aspectos es el formado por las
pruebas 3 y 4. Aún así, nótese que no hemos probado todo lo posible. Por ejemplo, si
en el programa nos colamos y ponemos AND donde queríamos poner OR (o
viceversa), este conjunto de pruebas no lo detecta. Sólo queremos decir que la
cobertura es un criterio útil y práctico; pero no es prueba exhaustiva.

Cobertura de bucles

Los bucles no son más que segmentos controlados por decisiones. Así, la cobertura de
ramas cubre plenamente la esencia de los bucles. Pero eso es simplemente la teoría,
pues la práctica descubre que los bucles son una fuente inagotable de errores, todos
triviales, algunos mortales. Un bucle se ejecuta un cierto número de veces; pero ese
número de veces debe ser muy preciso, y lo más normal es que ejecutarlo una vez de
menos o una vez de más tenga consecuencias indeseables. Y, sin embargo, es
extremadamente fácil equivocarse y redactar un bucle que se ejecuta 1 vez de más o
de menos.

Para un bucle de tipo WHILE hay que pasar 3 pruebas

1. 0 ejecuciones

2. 1 ejecución
3. más de 1 ejecución

Para un bucle de tipo REPEAT hay que pasar 2 pruebas

1. 1 ejecución

2. más de 1 ejecución

Los bucles FOR, en cambio, son muy seguros, pues en su cabecera está definido el
número de veces que se va a ejecutar. Ni una más, ni una menos, y el compilador se
encarga de garantizarlo. Basta pues con ejecutarlos 1 vez.

No obstante, conviene no engañarse con los bucles FOR y examinar su contenido. Si


dentro del bucle se altera la variable de control, o el valor de alguna variable que se
utilice en el cálculo del incremento o del límite de iteración, entonces eso es un bucle
FOR con trampa.

También tiene "trampa" si contiene sentencias del tipo EXIT (que algunos lenguajes
denominan BREAK) o del tipo RETURN. Todas ellas provocan terminaciones
anticipadas del bucle.

Estos últimos párrafos hay que precisarlos para cada lenguaje de programación. Lo
peor son aquellos lenguajes que permiten el uso de sentencias GOTO. Tampoco
conviene confiarse de lo que prometen lenguajes como MODULA-2, que se supone
que prohiben ciertas construcciones arriesgadas. Los compiladores reales suelen ser
más tolerantes que lo que anuncian los libros.

Si el programa contiene bucles LOOP, o simplemente bucles con trampa, la única


cobertura aplicable es la de ramas. El riesgo de error es muy alto; pero no se conocen
técnicas sistemáticas de abordarlo, salvo reescribir el código.

2.2. Caja negra

Sinónimos:

 pruebas de caja opaca

 pruebas funcionales

 pruebas de entrada/salida

 pruebas inducidas por los datos


Las pruebas de caja negra se centran en lo que se espera de un módulo, es decir,
intentan encontrar casos en que el módulo no se atiene a su especificación. Por ello se
denominan pruebas funcionales, y el probador se limita a suministrarle datos como
entrada y estudiar la salida, sin preocuparse de lo que pueda estar haciendo el módulo
por dentro.

Las pruebas de caja negra están especialmente indicadas en aquellos módulos que van
a ser interfaz con el usuario (en sentido general: teclado, pantalla, ficheros, canales de
comunicaciones, etc etc) Este comentario no obsta para que sean útiles en cualquier
módulo del sistema.

Las pruebas de caja negra se apoyan en la especificación de requisitos del módulo. De


hecho, se habla de "cobertura de especificación" para dar una medida del número de
requisitos que se han probado. Es fácil obtener coberturas del 100% en módulos
internos, aunque puede ser más laborioso en módulos con interfaz al exterior. En
cualquier caso, es muy recomendable conseguir una alta cobertura en esta línea.

El problema con las pruebas de caja negra no suele estar en el número de funciones
proporcionadas por el módulo (que siempre es un número muy limitado en diseños
razonables); sino en los datos que se le pasan a estas funciones. El conjunto de datos
posibles suele ser muy amplio (por ejemplo, un entero).

A la vista de los requisitos de un módulo, se sigue una técnica algebráica conocida


como "clases de equivalencia". Esta técnica trata cada parámetro como un modelo
algebráico donde unos datos son equivalentes a otros. Si logramos partir un rango
excesivamente amplio de posibles valores reales a un conjunto reducido de clases de
equivalencia, entonces es suficiente probar un caso de cada clase, pues los demás
datos de la misma clase son equivalentes.

El problema está pues en identificar clases de equivalencia, tarea para la que no existe
una regla de aplicación universal; pero hay recetas para la mayor parte de los casos
prácticos:

 si un parámetro de entrada debe estar comprendido en un cierto rango, aparecen


3 clases de equivalencia: por debajo, en y por encima del rango.

 si una entrada requiere un valor concreto, aparecen 3 clases de equivalencia:


por debajo, en y por encima del rango.
 si una entrada requiere un valor de entre los de un conjunto, aparecen 2 clases
de equivalencia: en el conjunto o fuera de él.

 si una entrada es booleana, hay 2 clases: si o no.

 los mismos criterios se aplican a las salidas esperadas: hay que intentar generar
resultados en todas y cada una de las clases.

Ejemplo: utilizamos un entero para identificar el día del mes. Los valores posibles
están en el rango [1..31]. Así, hay 3 clases:

1. números menores que 1

2. números entre 1 y 31

3. números mayores que 31

Durante la lectura de los requisitos del sistema, nos encontraremos con una serie de
valores singulares, que marcan diferencias de comportamiento. Estos valores son
claros candidatos a marcar clases de equivalencia: por abajo y por arriba.

Una vez identificadas las clases de equivalencia significativas en nuestro módulo, se


procede a coger un valor de cada clase, que no esté justamente al límite de la clase.
Este valor aleatorio, hará las veces de cualquier valor normal que se le pueda pasar en
la ejecución real.

La experiencia muestra que un buen número de errores aparecen en torno a los puntos
de cambio de clase de equivalencia. Hay una serie de valores denominados "frontera"
(o valores límite) que conviene probar, además de los elegidos en el párrafo anterior.
Usualmente se necesitan 2 valores por frontera, uno justo abajo y otro justo encima.

3. Pruebas de Integración

Las pruebas de integración se llevan a cabo durante la construcción del sistema,


involucran a un número creciente de módulos y terminan probando el sistema como
conjunto.

Estas pruebas se pueden plantear desde un punto de vista estructural o funcional.

Las pruebas estructurales de integración son similares a las pruebas de caja blanca;
pero trabajan a un nivel conceptual superior. En lugar de referirnos a sentencias del
lenguaje, nos referiremos a llamadas entre módulos. Se trata pues de identificar todos
los posibles esquemas de llamadas y ejercitarlos para lograr una buena cobertura de
segmentos o de ramas.

Las pruebas funcionales de integración son similares a las pruebas de caja negra. Aquí
trataremos de encontrar fallos en la respuesta de un módulo cuando su operación
depende de los servicios prestados por otro(s) módulo(s). Según nos vamos acercando
al sistema total, estas pruebas se van basando más y más en la especificación de
requisitos del usuario.

Las pruebas finales de integración cubren todo el sistema y pretenden cubrir


plenamente la especificación de requisitos del usuario. Además, a estas alturas ya
suele estar disponible el manual de usuario, que también se utiliza para realizar
pruebas hasta lograr una cobertura aceptable.

En todas estas pruebas funcionales se siguen utilizando las técnicas de partición en


clases de equivalencia y análisis de casos límite (fronteras).

4. Pruebas de Aceptación

Estas pruebas las realiza el cliente. Son básicamente pruebas funcionales, sobre el
sistema completo, y buscan una cobertura de la especificación de requisitos y del
manual del usuario. Estas pruebas no se realizan durante el desarrollo, pues sería
impresentable de cara al cliente; sino una vez pasada todas las pruebas de integración
por parte del desarrollador.

La experiencia muestra que aún después del más cuidadoso proceso de pruebas por
parte del desarrollador, quedan una serie de errores que sólo aparecen cuando el
cliente se pone a usarlo. Los desarrolladores se suelen llevar las manos a la cabeza:

"Pero, ¿a quién se le ocurre usar así mi programa?"

Sea como sea, el cliente siempre tiene razón. Decir que los requisitos no estaban
claros, o que el manual es ambiguo puede salvar la cara; pero ciertamente no deja
satisfecho al cliente. Alegar que el cliente es un inútil es otra tentación muy fuerte,
que conviene reprimir.

Por estas razones, muchos desarrolladores ejercitan unas técnicas denominadas


"pruebas alfa" y "pruebas beta". Las pruebas alfa consisten en invitar al cliente a que
venga al entorno de desarrollo a probar el sistema. Se trabaja en un entorno
controlado y el cliente siempre tiene un experto a mano para ayudarle a usar el
sistema y para analizar los resultados.

Las pruebas beta vienen despues de las pruebas alfa, y se desarrollan en el entorno del
cliente, un entorno que está fuera de control. Aquí el cliente se queda a solas con el
producto y trata de encontrarle fallos (reales o imaginarios) de los que informa al
desarrollador.

Las pruebas alfa y beta son habituales en productos que se van a vender a muchos
clientes. Algunos de los potenciales compradores se prestan a estas pruebas bien por
ir entrenando a su personal con tiempo, bien a cambio de alguna ventaja económica
(mejor precio sobre el producto final, derecho a mantenimiento gratuito, a nuevas
versiones, etc etc). La experiencia muestra que estas prácticas son muy eficaces.

5. Otros tipos de pruebas

Recorridos (walkthroughs)

Quizás es una técnica más aplicada en control de calidad que en pruebas. Consiste en
sentar alrededor de una mesa a los desarrolladores y a una serie de críticos, bajo las
órdenes de un moderador que impida un recalentamiento de los ánimos. El método
consiste en que los revisores se leen el programa línea a línea y piden explicaciones
de todo lo que no está meridianamente claro. Puede que simplemente falte un
comentario explicativo, o que detecten un error auténtico o que simplemente el código
sea tan complejo de entender/explicar que más vale que se rehaga de forma más
simple. Para un sistema complejo pueden hacer falta muchas sesiones.

Esta técnica es muy eficaz localizando errores de naturaleza local; pero falla
estrepitosamente cuando el error deriva de la interacción entre dos partes alejadas del
programa. Nótese que no se está ejecutando el programa, sólo mirándolo con lupa, y
de esta forma sólo se ve en cada instante un trocito del listado.

Aleatorias (random testing)

Ciertos autores consideran injustificada una aproximación sistemática a las pruebas.


Alegan que la probabilidad de descubrir un error es prácticamente la misma si se
hacen una serie de pruebas aleatoriamente elegidas, que si se hacen siguiendo las
instrucciones dictadas por criterios de cobertura (caja negra o blanca).
Como esto es muy cierto, probablemente sea muy razonable comenzar la fase de
pruebas con una serie de casos elegidos al azar. Esto pondrá de manifiesto los errores
más patentes. No obstante, pueden permanecer ocultos errores más sibilinos que sólo
se muestran ante entradas muy precisas.

Si el programa es poco crítico (una aplicación personal, un juego, ...) puede que esto
sea suficiente. Pero si se trata de una aplicación militar o con riesgo para vidas
humanas, es de todo punto insuficiente.

Solidez (robustness testing)

Se prueba la capacidad del sistema para salir de situaciones embarazosas provocadas


por errores en el suministro de datos. Estas pruebas son importantes en sistemas con
una interfaz al exterior, en particular cuando la interfaz es humana.

Por ejemplo, en un sistema que admite una serie de órdenes (commands) se deben
probar los siguientes extremos:

 órdenes correctas, todas y cada una

 órdenes con defectos de sintaxis, tanto pequeñas desviaciones como errores de


bulto

 órdenes correctas, pero en orden incorrecto, o fuera de lugar

 la orden nula (línea vacia, una o más)

 órdenes correctas, pero con datos de más

 provocar una interrupción (BREAK, ^C, o lo que corresponda al sistema


soporte) justo después de introducir una orden.

 órdenes con delimitadores inapropiados (comas, puntos, ...)

 órdenes con delimitadores incongruentes consigo mismos (por ejemplo, esto]

Aguante (stress testing)

En ciertos sistemas es conveniente saber hasta dónde aguantan, bien por razones
internas (¿hasta cuantos datos podrá procesar?), bien externas (¿es capaz de trabajar
con un disco al 90%?, ¿aguanta una carga de la CPU del 90?, etc etc)

Prestaciones (performance testing)


A veces es importante el tiempo de respuesta, u otros parámetros de gasto.
Típicamente nos puede preocupar cuánto tiempo le lleva al sistema procesar tantos
datos, o cuánta memoria consume, o cuánto espacio en disco utiliza, o cuántos datos
transfiere por un canal de comunicaciones, o ... Para todos estos parámetros suele ser
importante conocer cómo evolucionan al variar la dimensión del problema (por
ejemplo, al duplicarse el volumen de datos de entrada).

Conformidad u Homologación (conformance testing)

En programas de comunicaciones es muy frecuente que, además de los requisitos


específicos del programa que estamos construyendo, aparezca alguna norma más
amplia a la que el programa deba atenerse. Es frecuente que organismos
internacionales como ISO y el CCITT elaboren especificaciones de referencia a las
que los diversos fabricantes deben atenerse para que sus ordenadores sean capaces de
entenderse entre sí.

Las pruebas, de caja negra, que se le pasan a un producto para detectar discrepancias
respecto a una norma de las descritas en el párrafo anterior se denominan de
conformidad u homologación. Suelen realizarse en un centro especialmente
acreditado al efecto y, si se pasan satisfactoriamente, el producto recibe un sello
oficial que dice: "homologado".

Interoperabilidad (interoperability tesing)

En el mismo escenario del punto anterior, programas de comunicaciones que deden


permitir que dos ordenadores se entiendan, aparte de las pruebas de conformidad se
suelen correr una serie de pruebas, también de caja negra, que involucran 2 o más
productos, y buscan problemas de comunicación entre ellos.

Regresión (regression testing)

Todos los sistemas sufren una evolución a lo largo de su vida activa. En cada nueva
versión se supone que o bien se corrigen defectos, o se añaden nuevas funciones, o
ambas cosas. En cualquier caso, una nueva versión exige una nueva pasada por las
pruebas. Si éstas se han sistematizado en una fase anterior, ahora pueden volver a
pasarse automáticamente, simplemente para comprobar que las modificaciones no
provocan errores donde antes no los había.

El mínimo necesario para usar unas pruebas en una futura revisión del programa es
una documentación muy muy clara.
Las pruebas de regresión son particularmente espectaculares cuando se trata de probar
la interacción con un agente externo. Existen empresas que viven de comercializar
productos que "graban" la ejecución de una prueba con operadores humanos para
luego repetirla cuantas veces haga falta "reproduciendo la grabación". Y, obviamente,
deben monitorizar la respuesta del sistema en ambos casos, compararla, y avisar de
cualquier discrepancia significativa.

Mutación (mutation testing)

Es una técnica curiosa consistente en alterar ligeramente el sistema bajo pruebas


(introduciendo errores) para averiguar si nuestra batería de pruebas es capaz de
detectarlo. Si no, más vale introducir nuevas pruebas. Todo esto es muy laborioso y
francamente artesano.

6. Depuración (debugging)

Casi todos los compiladores suelen llevar asociada la posibilidad de ejecutar un


programa paso a paso, permitiéndole al operador conocer dónde está en cada
momento, y cuánto valen las variables.

Los depuradores pueden usarse para realizar inspecciones rigurosas sobre el


comportamiento dinámico de los programas. La práctica demuestra, no obstante, que
su uso es tedioso y que sólo son eficaces si se persigue un objetivo muy claro. El
objetivo habitual es utilizarlo como consecuencia de la detección de un error. Si el
programa se comporta mal en un cierto punto, hay que averiguar la causa precisa para
poder repararlo. La causa a veces es inmediata (por ejemplo, un operador booleano
equivocado); pero a veces depende del valor concreto de los datos en un cierto punto
y hay que buscar la causa en otra zona del programa.

En general es mala idea "correr al depurador", tanto por el tiempo que se pierde
buceando sin una meta clara, como por el riesgo de corregir defectos intermedios sin
llegar a la raiz del problema. Antes de entrar en el depurador hay que delimitar el
error y sus posibles causas. Ante una prueba que falla, hay que identificar el dominio
del fallo, averiguar las características de los datos que provoca el fallo (y comprobar
experimentalmente que todos los datos con esas características provocan ese fallo, y
los que no las tienen no lo provocan).

El depurador es el último paso para convencernos de nuestro análisis y afrontar la


reparación con conocimiento de causa.
7. Plan de Pruebas

Un plan de pruebas está constituido por un conjunto de pruebas. Cada prueba debe

 dejar claro qué tipo de propiedades se quieren probar (corrección, robustez,


fiabilidad, amigabilidad, ...)

 dejar claro cómo se mide el resultado

 especificar en qué consiste la prueba (hasta el último detalle de cómo se


ejecuta)

 definir cual es el resultado que se espera (identificación, tolerancia, ...) ¿Cómo


se decide que el resultado es acorde con lo esperado?

Las pruebas angelicales carecen de utilidad, tanto si no se sabe exactamente lo que se


quiere probar, o si no está claro cómo se prueba, o si el análisis del resultado se hace
"a ojo".

Estas mismas ideas se suelen agrupar diciendo que un caso de prueba consta de 3
bloques de información:

1. El propósito de la prueba

2. Los pasos de ejecución de la prueba

3. El resultado que se espera

Y todos y cada uno de esos puntos debe quedar perfectamente documentado. Las
pruebas de usar y tirar más vale que se tiren directamente, aún antes de usarlas.

Cubrir estos puntos es muy laborioso y, con frecuencia, tedioso, lo que hace
desagradable (o al menos muy aburrida) la fase de pruebas. Es mucho mas divertido
codificar que probar. Tremendo error en el que, no obstante, es fácil incurrir.

Respecto al orden de pruebas, una práctica frecuente es la siguiente:

1. Pasar pruebas de caja negra analizando valores límite. Recuerde que hay que
analizar condiciones límite de entrada y de salida.

2. Identificar clases de equivalencia de datos (entrada y salida) y añadir más


pruebas de caja negra para contemplar valores normales (en las clases de
equivalencia en que estos sean diferentes de los valores límite; es decir, en
rangos amplios de valores).

3. Añadir pruebas basadas en "presunción de error". A partir de la experiencia y el


sentido común, se aventuran situaciones que parecen proclives a padecer
defectos, y se buscan errores en esos puntos. Son pruebas del tipo "¡Me lo
temía!"

4. Medir la cobertura de caja blanca que se ha logrado con las fases previas y
añadir más pruebas de caja blanca hasta lograr la cobertura deseada.
Normalmente se busca una buena cobertura de ramas (revise los comentarios
expuestos al hablar de caja blanca).

Documentación del programa

Consiste en describir por escrito a nivel técnico los procedimientos relacionados con
el programa y su modo de uso.

Tipos de documentación

La documentación que se entrega al cliente se divide claramente en dos categorías,


interna y externa:

 Interna: Es aquella que se crea en el mismo código, ya puede ser en forma de


comentarios o de archivos de información dentro de la aplicación.

 Externa: Es aquella que se escribe en cuadernos o libros, totalmente ajena a la


aplicación en si. Dentro de esta categoría también se encuentra la ayuda
electrónica.

La guía técnica

En la guía técnica o manual técnico se reflejan el diseño del proyecto, la codificación


de la aplicación y las pruebas realizadas para su correcto funcionamiento.
Generalmente este documento esta diseñado para personas con conocimientos de
informática, generalmente programadores.

El principal objetivo es el de facilitar el desarrollo, corrección y futuro mantenimiento


de la aplicación de una forma rápida y fácil.
Esta guía esta compuesta por tres apartados claramente diferenciados:

 Cuaderno de carga: Es donde queda reflejada la solución o diseño de la


aplicación.
Esta parte de la guía es únicamente destinada a los programadores. Debe estar
realizado de tal forma que permita la división del trabajo

 Programa fuente: Es donde se incluye la codificación realizada por los


programadores. Este documento puede tener, a su vez, otra documentación para
su mejor comprensión y puede ser de gran ayuda para el mantenimiento o
desarrollo mejorado de la aplicación. Este documento debe tener una gran
claridad en su escritura para su fácil comprensión.

 Pruebas: es el documento donde se especifican el tipo de pruebas realizadas a lo


largo de todo el proyecto y los resultados obtenidos.

La guía de uso

Es lo que comúnmente llamamos el manual del usuario. Contiene la información


necesaria para que los usuarios utilicen correctamente la aplicación.

Este documento se hace desde la guía técnica pero se suprimen los tecnicismos y se
presenta de forma que sea entendible para el usuario que no sea experto en
informática.

Un punto a tener en cuenta en su creación es que no debe hacer referencia a ningún


apartado de la guía técnica y en el caso de que se haga uso de algún tecnicismo debe
ir acompañado de un glosario al final de la misma para su fácil comprensión.

La guía de instalación

Es la guía que contiene la información necesaria para implementar dicha aplicación.


Dentro de este documento se encuentran las instrucciones para la puesta en marcha
del sistema y las normas de utilización del mismo.

Dentro de las normas de utilización se incluyen también las normas de seguridad,


tanto las físicas como las referentes al acceso a la información.
Mantenimiento de programas

Contexto

La fase de mantenimiento de software involucra cambios al software para corregir


defectos encontrados durante su uso o la adición de nueva funcionalidad mejorando la
usabilidad y aplicabilidad del software.

El mantenimiento del software involucra diferentes técnicas específicas. Una técnica


es el rebanamiento estático, la cual es usada para identificar todo el código de
programa que puede modificar alguna variable. Es generalmente útil en la
refabricación del código del programa y fue específicamente útil en asegurar
conformidad para el problema del año 2000.

La fase de mantenimiento de software es una parte explícita del modelo en cascada


del proceso de desarrollo de software el cual fue desarrollado durante el movimiento
de programación estructurada en computadores. El otro gran modelo, el Desarrollo en
espiral desarrollado durante el movimiento de ingeniería de software orientada a
objeto no hace una mención explícita de la fase de mantenimiento. Sin embargo, esta
actividad es notable, considerando el hecho de que dos tercios del coste del tiempo de
vida de un sistema de software involucran mantenimiento (Page-Jones pg 31).

En un ambiente formal de desarrollo de software, la organización o equipo de


desarrollo tendrán algún mecanismo para documentar y rastrear defectos y
deficiencias. El Software tan igual como la mayoría de otros productos, es
típicamente lanzado con un conjunto conocido de defectos y deficiencias. El software
es lanzado con esos defectos conocidos porque la organización de desarrollo en las
utilidades y el valor del software en un determinado nivel de calidad compensa el
impacto de los defectos y deficiencias conocidas.

Las deficiencias conocidas son normalmente documentadas en una carta de


consideraciones operacionales o notas de lanzamiento (release notes) es así que los
usuarios del software serán capaces de trabajar evitando las deficiencias conocidas y
conocerán cuándo el uso del software sería inadecuado para tareas específicas.

Con el lanzamiento del software (software release), otros defectos y deficiencias no


documentados serán descubiertas por los usuarios del software. Tan pronto como
estos defectos sean reportados a la organización de desarrollo, serán ingresados en el
sistema de rastreo de defectos.
Las personas involucradas en la fase de mantenimiento de software esperan trabajar
en estos defectos conocidos, ubicarlos y preparar un nuevo lanzamiento del software,
conocido como un lanzamiento de mantenimiento, el cual resolverá los temas
pendientes.

Tipos de mantenimiento

A continuación se señalan los tipos de mantenimientos existentes:

 Perfectivo: son las acciones llevadas a cabo para mejorar la calidad interna de
los sistemas en cualquiera de sus aspectos: reestructuración del código,
definición más clara del sistema y optimización del rendimiento y eficiencia.

 Evolutivo: son las incorporaciones, modificaciones y eliminaciones necesarias


en un producto software para cubrir la expansión o cambio en las necesidades
del usuario.

 Adaptativo: son las modificaciones que afectan a los entornos en los que el
sistema opera, por ejemplo, cambios de configuración del hardware, software
de base, gestores de base de datos, comunicaciones, etc.

 Correctivo: son aquellos cambios precisos para corregir errores del producto
software.

También podría gustarte