Gestión SQA y de Proyectos en Procesos de Construcción de Software

Temas relacionado a la obtención de calidad de los procesos de construcción del software. Conocimiento de las herramientas necesarias y las existentes en el mercado global. Procesos, métricas, metodologías, testing, Gestión de Proyectos y otras gestiones. Un mercado tan dinámico merece la pena ser pensado por todos los involucrados.

Orden y limpieza

La encargada de limpieza en las oficinas de una Software Factory puso carteles en el baño y en la cocina, llamando a la colaboración para mantener el orden y la limpieza. El resultado directo fue el enojo y reacción de las personas, los comentarios por lo bajo y los email masivos mostrando el descontento de quienes se vieron afectados por las notas.

Ayudemos a definir un mejor modelo SCRUM

Suena a una cruzada para salvar el mundo o para generar conciencia. Tal vez se ambas cosas.

Lo cierto es que alguien se tomó el trabajo de hacer un buen número de definiciones en base a SCRUM y graficó el modelo con la mayoría de los elementos que son partícipes.

Pronto los invitaré a colaborar con mi propia metodología, modelo o proceso, según a lo que llegue.

Si estas interesado, puedes hacer ingresando en Metodología basada en SCRUM

¿"Pay per Defect" o elevar la conciencia creativa del acto verificador?

Un amigo posteó en XING:

¿Como creéis que se debe incentivar a los equipos de calidad? ¿Es posible incentivar por defecto encontrado o es contraproducente?

Imaginemos una compañía que decide incentivar por objetivos, e incentiva el equipo de desarrollo por funcionalidad implementada en el menor tiempo. ¿Como incentivamos al Tester? o dicho de otra manera como sabemos quién realiza mejor labor de verificación?

Una respuesta de otro amigo:

Pues yo creo que incentivar la localización de errores está bien.
Eso sí, debe haber un "juez" que decida si cada caso es digno de contabilizar o no, es decir, que tome la decisión en los casos dudosos ("es que no estaba en los requisitos escritos" ... "sí, pero es algo 'obvio'" ... y cosas de ese estilo).
Ah! Y añado otra cosa : igual que se incentiva la localización de errores en el testeo creo que se debería "desincentivar" cuando un Cliente localiza el error una vez lanzada la versión.
Luego se hacen las cuentas de la vieja y se saca balance.
Ya verías la de errores que se encuentran !!! jejeje

El aporte de uno más:

¿Qué tal un esquema intermedio?

1) Incentivar a los desarrolladores con relación inversa al número de defectos que "les" encuentren los Testers.
2) Incentivar a los Testers con relación al número de objetos/unidades/loquesea que pasen a producción validados
3) E incentivarlos a todos con la "velocidad" del proceso completo, y con relación inversa a los defectos encontrados en producción.

La "fórmula" no sería sencilla, pero puede que se lograse un resultado adecuado. ¡O que se lograse confundirles a todos!

¡¡¡¡me encanta este aporte!!!!

Pero mi opinión fue la siguiente:

Me resulta un poco extraña la cuestión de tener que incentivar a un Tester o grupos de Testers por tener que hacer su trabajo bien. Igual para los desarrolladores o cualquier otra persona que cumpla un rol específico dentro de la cadena productiva de software.
En primer lugar considero que un plan de verificaciones y/o validaciones no puede quedar librado a la buena de un Tester, sino que debe guiar a cada uno de los involucrados en el proceso hacia los resultados esperados, también sus controles y gestiones.

Si se considera la existencia de Testers en un equipo de producción de software, no debe ser sin la existencia de un responsable QA y la carga de gestión recae sobre los hombros de tal o tales personas. Lo que se puede hacer es "elevar la conciencia creativa del acto verificador" agregando capacitaciones especializadas en herramientas, conceptos avanzados, UML, patrones de calidad y demás elementos que existen para sumir al equipo de calidad en un estándar que desde el llano y como punto de partida sea muy alto.
Podría agregar que es bueno tener métricas de cantidades de defectos detectados por Tester pero no podríamos dejar de mirar que tipo de defectos detecta cada uno de ellos y como es el impacto de cada uno de esos defectos detectados en los proyectos. Esto serviría para que en caso de necesitar dividir el equipo de Testing, al "mejor Tester" se le asignaría una especie de "jefatura" sobre otros que no tienen el mismo rendimiento.

Dependiendo del entorno de trabajo, la suspicacia de las personas, los objetivos personales de cada integrante del equipo de Testers entre otros elementos RRHH, es bueno aplicar criterios de competencias operativas. Pero en otros casos no. Equipos enteros podrían desintegrarse por generarse ciertas incomodidades. No quiero ni hablar de como impactaría sobre nuestros productos.

Entonces, pagar por defectos detectados vale para equipos de venta. Ya todos los defectos del producto fueron eliminados o se presumen eliminados y el producto de alta calidad. Ahora a generar retorno de la inversión.

Me gusta mucho la idea de incentivar, pero sin generar competencias sino ambientes colaborativos (inclusive para eso estamos aquí nosotros) y cada uno en su puesto de trabajo debe elevar al máximo su conciencia de responsabilidad.
Los Testers suelen encontrar fallos de severidad Invalidante o Graves a penas inician el Testing. No es mérito del Tester sino de la incompetencia de alguien en fases anteriores. No señalo a nadie, pero desde el Tester para atrás, hay muchas responsabilidades intrínsecas.

Es cierto también que al principio el Tester se haría de una muy buena suma de dinero extra por la cantidad de detección de fallos y conforme avanzan los ciclos, deberá devolver dinero y hasta poner de su bolsillo por que no encontrará fallos que se sabe por métricas, están allí.

Cierta oportunidad siendo Líder de Testing al finalizar uno de los proyectos postulados para certificación CMMI-4, mi equipo detectó grandes cantidades de fallos en los productos solo en los primeros ciclos, cuando había planificados 12 ciclos de pruebas. Conforme se comenzaron a resolver los fallos, previo a los ciclos subsiguientes, nuestra tasa de detección cayó significativamente para el resto de los ciclos (considerar PARETO).
Esto me llevo a analizar la situación, a modificar definitivamente el modo en que manejamos los ciclos. Finalizado el proyecto que menciono, invité a mi equipo a un almuerzo y por supuesto al equipo de Desarrollo. Allí pude explicar lo que habíamos conseguido trabajando en equipo y como unos sin otros no pueden evidenciar su actividad hecha con responsabilidad.
Gracias a esto pudimos mejorar nuestro proceso en forma significativa.
Creo en definitiva, que brindar las herramientas adecuadas, la capacitación necesaria y sostener un ambiente colaborativo y ameno, son los mejores incentivos.

La parte dificil de construir sofware es la especificación

La parte difícil de construir software es la
especificación, el diseño y la verificación de esa
trama conceptual, no la tarea de representarla y
testear la representación.

Esta elegante definición la pude ver en apuntes que suelo revisar sobre los distintos aspectos de la Ingeniería del Software y me dió que pensar.

En la mayoría de las oportunidades en que hemos tenido reuniones de equipo para hablar de nuestras fortalezas y debilidades en base al éxito o al fracaso de algún proyecto reciente, se nos hizo notar que nuestro equipo debe preponderar en la intensión de hacer ingeniería.
Son incontable las veces en que se describió mentalmente el proceso existente, en esas reuniones. Pero en cada una de las oportunidades en que se presenta

Quien estima mejor?

Tu estimación del proyecto por quien ha sido realizada?

Developer / Project Manager / Project Tracker / Testing Team / Quality Assurance team.

Según los roles serán las estimaciones. Pero esto no debería ser así o es lo más lógico?

Tengo la tarea de Capacitar a un equipo de Desarrollo completo, incluyendo a los Líderes de Proyectos, Analistas Funcionales, Analistas de Requerimientos, Desarrolladores y Arquitecto de Sistemas, Equipo de Pruebas y a la mismísima Gerencia.

Inicialmente me hago un pequeño plan con 3 o 4 ítems. Típicamente algo así:

  1. Definir herramientas necesarias y modos de Testing Unitario con ASP
  2. Buscar emails anteriores como referencias sobre PUI
  3. Buscar material vía Internet para enviar como referencia a JF
  4. Armar estrategias para aplicarse al proyecto vigente
  5. Armar metodología de capacitación al TEAM

No busco consenso con nadie en absoluto para establecer como útil esta mini estrategia, simplemente inicio las distintas actividades que comprenden cada una de las tareas de mi plan.

Converso con uno de los Analistas de Requerimientos quien a su vez en Analista Funcional del proyecto vigente y obtengo información de la tecnología que podría utilizarse para dar las distintas soluciones técnicas. La respuesta genérica fue, ASP 3.0 por que es lo que el equipo de Desarrollo maneja con fluidez y no hay tiempo para aplicar innovaciones migrando a la plataforma de ASP.Net. Válido!!!

Abro mi navegador de Internet y averiguo sobre herramientas actuales que puedan aplicarse al Testing Unitario Automatizado con ASP 3.0

De paso me voy embebiendo en otras herramientas para Testing Automatizado como por ejemplo: JMeter, OpenSTA, Canno, Selenium, entre otras, cada una para algo en particular y con mejores o peores atributos, según con que se lo compare.

 

Algo de Testing Automatizado

Objetivos del Testing Automatizado

  • Tests should improve quality

  • Tests should help us understand the system under test

  • Tests should reduce risk

  • Tests should be easy to run

  • Tests should require minimal maintenance as the system evolves

  • Tests should be executed as part of the build process if possible

     

    Tipos básicos de Testing donde se puede automatizar

    • Unit tests

    • Customer Test

    • GUI Testing

     

    Que tener en cuenta para iniciarse en los Procesos Automatizados de Pruebas

    ¿Cuales son los objetivos de nuestro Testing Automatizado?  Estos deben estar alineados con todos los objetivos de Calidad. Algunos factores que afectarían a nuestros objetivos:

    Deseamos automatizar las pruebas regresivas?

    Deseamos realizar Integración Continua en nuestro Proceso de Desarrollo?

    Estamos buscando resolver un ítem específico de Aseguramiento de Calidad?

    Comencemos a Organizarnos

    Elegir una estrategia

    Escojamos un Tipo Básico de Testing al que nos queremos aproximar con la automatización. Debería ser una combinación de aproximaciones basadas en:

    • Objetivos
    • Tipo de sistema que queremos Testear:
      • Testabilidad. Definir la capacidad del sistema a soportar automatización de pruebas
      • Nivel de automatización actual. Si la base es cero podría ser el peor escenario
    • Podemos modificar el Proceso de Desarrollo y los Requisitos del Sistema para lograr una aproximación al nuevo Ciclo de Vida que exigen las pruebas automatizadas?
    • Con solo la automatización de pruebas resolvemos algún problema o debemos tener en cuenta otros ítmes?
    • Seleccionemos un tipo de Testing Automatizado: Unitario, Integración , Requerimientos, etc.

    Seleccionemos las herramientas basándonos en

    • Estrategia seleccionada
    • Orientación del Testing Automatizado (Aceptación del Cliente, de Componente, GUI)
    • Open Source o Comercial?
    • La aplicación seleccionada se adapta a nuestra aplicación o debemos hacer adaptaciones de la aplicación a la herramienta?
    • Definamos cuales son nuestros costos

    Dos puntos son necesarios para facilitar el mantenimiento

    • Definamos las expectativas de la Gestión de las pruebas
    • Definamos los estándares o patterns para implementar las pruebas automáticas.

    Por ejemplo usemos Palabra Clave como patrón que puede utilizarse para reducir los costos de mantenimiento al utilizar la interfaz de usuario basada en la automatización. Algunos de los enfoques de benefician directamente con el trabajo de control de calidad.

    El enfoque que realizamos afectan a las acciones 

    • Los desarrolladores serán responsables de crear y mantener las Unidades de Pruebas Automatizadas, aunque puede ayudar a QA y su criterio será fundamental
    • SQA puede crear los otros tres tipos de pruebas automatizadas, como siempre lo mejor es integrar directamente en el proceso de desarrollo
    • Debemos asegurarnos de que el Testing Automatizado es realmente parte del proceso y se complementa con el Testing No Automatizado

    Revisemos el Plan de Pruebas Automatizadas

    En todos los casos se debe hacer revisiones de la efectividad del proceso y buscar la forma de mejorarlo. Las siguientes cuestiones podrían ayudarnos:

    • Son lentas las automatizaciones?
    • Son difíciles de mantener?
    • Disminuyen la velocidad del Proceso de Desarrollo?
    • Son eficaces en el logro de nuestros objetivos de calidad?

    Estas pruebas tienen como principales objetivos comprobar que el código se comporta de la manera esperada y prevenir la aparición de errores inesperados al modificar o refactorizar el código. También, en algunos casos como en el desarrollo dirigido por pruebas, las pruebas unitarias ayudan a obtener el diseño del código. Las ventajas de realizar este tipo de pruebas son: código más depurado, mayor seguridad a la hora de refactorizar y mayor velocidad de desarrollo, aparte de obtener código de mayor calidad asegurando que nada se ha roto cuando cambiamos la lógica interna del software.

    Actualmente las pruebas unitarias se desarrollan con herramientas tipo jUnit [3] y objetos mock [4]. Las herramientas tipo jUnit se aplican para comprobar que el estado de un objeto, los valores retornados por los métodos, excepciones lanzadas, etc, son los esperados. Los objetos ficticios o mocks, en cambio, permiten aislar el código a probar de las dependencias con otras clases colaboradoras mediante el desarrollo de clases ficticias que simulan el comportamiento de estas clases colaboradoras. Esto permite centrar la prueba sólo en el código que se desea probar.

    Aquí introduzco conceptos de Testing Driven Development (TDD) tomados de un blog amigo WebTrun:

    SlideShare | View | Upload your own

    Las organizaciones son tan eficientes como sus procesos

    El diseño de un flujo de trabajo no es solamente un medio de documentar procesos, sino que es la guía de trabajo para el ambiente operativo.

    Es necesario diseñar con consenso de las partes, los flujos de trabajo para los procesos de negocio que acompañen el ciclo de vida completo de las interacciones Cliente-Organización-Usuarios.

    No se puede pretender eficiencia de los procesos cuando se ingresan cambios sin tener en cuenta el impacto global y particular en las distintas PA y las personas que interactúan en ellas. O solamente pensando en un resultado puntual que sirve a un propósito de corta data.

    Finalmente, hay que entender el por que de hecho de que la mayoría de las empresas tomaron conciencia de lo imprescindible de contar con una herramienta que coordine el trabajo y permita compartir actividades, información y recursos en un ambiente simple de operar y monitorear.

    Cronograma de proyectos, agendas de trabajo, dependiencias y criterios de completitud

    Resulta importante no hacer recortes en las actividades que tienen relación con el aseguramiento de calidad y las fechas de entrega no pueden poner en riesgo el aseguramiento de calidad, y los esfuerzos correctivos deben realizarse durante el proyecto y no al final cuando es prácticamente imposible.

    Esto implica que debe plantearse una estrategia que sea sostenida durante todo el ciclo de vida del proyecto y que se apliquen las tácticas adecuadas para evitar desplazamientos que pongan en riesgo tanto la fecha de entrega como la calidad en si.

    Es fácil observar que las agendas particulares que genera el cronograma del proyecto, tienen fuerte dependencias con otros integrantes del equipo y no es aceptable romper esas dependencias sin considerar los impactos de esas rupturas (básicamente cambio en la agenda de algún integrante). Este tipo de ruptura implica una falta al compromiso con el recurso dependiente y principalmente con la actividad planificada, lo cual quiere decir que alguien deberá realizar mayores esfuerzos para lograr cumplir con los elementos satisfactores que darán completitud a la actividad.

    Es cierto que la mayoría de las veces no son intencionales tales incumplimientos, sobre todo en algunas personas con roles específicos, que reciben pedidos en carácter de urgente. Sin embargo, si existe una planificación y se crea una agenda personal que incluye actividades con dependencias hacia otras actividades de otros recursos (agenda global), todo esto debería anteponerse a la fuerte necesidad de responder a las necesidades instantáneas de los superiores del organigrama.

    De no ser posible, debe consensuarse con los involucrados y modificar las condiciones de cumplimiento y aceptación (negociación), pero esto idealmente, no debería ser una constante hacia adentro, que implosione, sino que es mucho más saludable sostener un carácter de cumplimiento con las actividades con dependencias y el carácter negociador con los que generan nuevos pedidos en forma constante.

    En definitiva si permanecemos en la actitud de modificar nuestras actividades rompiendo dependencias sin negociar, vamos en contra de los principios de Completitud, que pertenecen a los axiomas del aseguramiento de calidad. ¿No es completitud la palabra más utilizada por los superiores de la organización a la hora de una refriega por incumplimiento?

    Ahora que estamos obligados a trabajar con agendas visibles para La Gerencia y el resto de los integrantes de la compañía, logremos que no se nos vuelva en contra y aprendamos a manejar todos los elementos que implica llevar una “agenda de compromisos”. Que cada ítem que ingresamos deje de ser un elemento de reclamo y acusaciones constantes.

    Mi rol, mi evolución, mis quedos, mi dejos

     

    Como Responsable SQA y QAC, sostengo mi crecimiento permanente basándome en la vida misma de los proyectos, en las interdependencias profesionales y en la relevancia de nuestras soluciones para el usuario. 

    La formación de un proceso particular y nuestra adaptación no ha sido fácil. Principalmente nos ha sido imposible automatizar las pruebas para un entorno de desarrollo basado en la API de Lotus Note, de IBM y todo lo que pueda existir para automatizar pruebas a cualquier nivel nos resultó bastante costoso, en lo que a tiempo y esfuerzo se refiere.

    Es así que me he puesto en la tarea de mejorar todos los procesos intrínsecos, previos a cualquier desarrollo.

    La estrategia global implicó lograr alertar fallos utilizando un símil  AMFE en los requerimientos, también en fases de Análisis y Diseño. Posteriormente establecer una cadena de revisiones para las soluciones técnicas propuestas, mas estudios de viabilidad y factibilidad de Despliegues de Productos, tratando de detectar y minimizar cualquier merma de la calidad que impacten en los eslabones finales de la cadena de producción.

    Como estrategia para pruebas, generamos la cantidad de ambientes de Testing que fueran necesarios, basándonos primariamente en los ciclos que dimos de alta en el cronograma del proyecto.

    Nuestra táctica implica la formación de una alta cantidad ciclos con una gran cantidad de Pruebas de Integración, que no son coincidentes con Versiones Candidatas del producto, sino que en cada integración nos vamos acercando a dichas versiones. Al lograr integrar todos los componentes que conforman un módulo testeable a nivel de pruebas funcionales, versionamos el producto y ejecutamos el ciclo funcional correspondiente.

    Esto esta lejos de ser una Integración Continua, pero es la aproximación mínima que he podido idear para que la mayoría de los defectos se detecten con bastante antelación.

    Conformamos un equipo chico de desarrollo, con un Responsable que hace las gestiones de seguimiento de avances de los componentes y determina en base al cronograma, el hito de integración de componentes, del mismo modo la conformación de los módulos y notificaciones al LP y SQA, de los módulos que están disponibles para pruebas de funcionales.

    La envergadura del equipo exige que los mismos desarrolladores ejecuten los ciclos de pruebas integradoras y lo hacen bajo un esquema de revisiones cruzadas. SQA asigna también un equipo de Testeadores que ejecutan pruebas integrales ayudando a acelerar el proceso.

    Previo a esto, sin duda alguna tuvimos que elevar el Skill de los Desarrolladores de esta tecnología tan particular, en lo que respecta a técnicas de pruebas y detección de fallos.

    Todos los fallos, defectos y errores son registrados y puestos a consideración de SQA y el LP quienes en un solo lote hacen las derivaciones para resoluciones, en base a la severidad y prioridad determinada en un análisis exhaustivo.

    Este accionar nos ha llevado rápidamente a una disminución significativa de fallos detectados en ciclos de pruebas funcionales de versiones candidatas y a una desaparición del 100% de fallos del tipo Invalidante y del 80% de fallos del tipo Grave.

    Las actividades de Despliegue en si mejoraron y garantizaron mejores funcionamiento cuando se comenzó a utilizar el elemento "Pruebas de Campo" y finalmente nuestro equipo de Soporte cuenta con una gran biblioteca de fallos detectados los cuales alimentan su base de conocimientos a cerca de como se podría presentar un fallo y como se orienta a su rápida resolución.

    Poco a poco trataré de mostrar los procesos aunque no pueda dar demasiados detalles, lo haré genérico.

    Hábitos que se consideran importantes para un desarrollador (y cualquiera)

    Anoche leyendo un foro de los que acostumbro, tuve la oportunidad de rescatar las siguientes líneas. Quiero comentarles que estos axiomas corresponden a apreciaciones que hacen Ingenieros, Programadores, Gerentes y Testers de varios países como España, Chile, Colombia, Perú, Argentina, y otros de habla hispana.

    • "Intenta siempre buscar la simplicidad". Busca siempre y por defecto la solución más simple, más natural, más clara frente a la optimizada, compleja y eficiente. Hoy en día, el rendimiento es algo que se ha superado con creces gracias a los potentes procesadores. Intentarlo inicialmente de la manera "fácil" y, si no da buen resultado, dar un paso hacia la optimización. Pero sólo uno. Si sigue dando problemas, da otro paso.
    • "Capacidad de organizar tu trabajo". Cada programador debe ser consciente de cuales son las horas más productivas del día para el y utilizarlas al máximo y dejar los momentos más flojos para tareas mecánicas. Es decir, evitar errores por cansancio que desembocan en un montón de correcciones a posteriori. (p. Ej.: para mi las primeras horas de la mañana son las más productivas y dejo para última hora de la tarde las tareas monótonas, reuniones, etc.)
    • "Analizar y organizar tu trabajo antes de hacerlo". El papel suele ser un buen medio. Es importante para tener claro en tu cabeza que vas a hacer.
    • "Intentar ser exigente en tu trabajo no sólo en las grandes tareas sino en todos los aspectos mínimos de un proyecto". A veces son los que marcan la diferencia. (Desde borrar variables en un fichero a optimizar los plazos de un proyecto. Todo es importante).
    • "Intentar ser pausado". Es una mala costumbre dar por buena al que más teclea. El programador debe acostumbrarse a ser reflexivo. A medio plazo compensa.
    • "Piensa en el test, tanto unitario como funcional". Una posibilidad es usar metodologías de desarrollo orientadas al test (Test Driven Development), pero no es la única. Lo cierto es que pensar en como vamos a verificar en el momento del desarrollo significa un ahorro importante durante fases de test y mantenimiento futuro.
    • "Deberías revisar las buenas prácticas de programación que se utilizan en extreme programming"
    • “Programación en pares - Reutilización -Uso de estándares”, entre otros
    • "La información debe fluir". Es decir, debe pasar de unas manos a otras, de una rutina a otra. De esta forma, evitaríamos (o al menos minimizaríamos) el uso de las tan peligrosas variables globales. Como caso práctico, nosotros solemos aplicar esta regla cuando pasamos de un formulario de la aplicación a otro. En lugar de que el formulario destino "tome" la información que necesita, se la pasamos a través de un método "Execute".
    • "Borrar la declaración de las variables que ya no se usan". Es práctica común ir copiando un programa de otro y comentar lo que no sirve para el nuevo pero no borrarlo, por ejemplo en lenguajes como C ó C siempre  se  tiende  a  dejar  las  variables que no se usan. Para ello las últimas versiones de los entornos de desarrollo ya incluyen ciertas herramientas de refactoring y una de ellas suele ser la de eliminar las variables no utilizadas.
    • "Usar una herramienta control de versiones".
    • Como dice el bueno de Steve (McConnell) en su "Code Complete 2", la clave del éxito de un producto software es la gestión contínua y correcta de la simplicidad.
    • "Haz que tu código se parezca lo más posible al lenguaje normal". El mismo que utilizarías si le quieres explicar a alguien ajeno qué hace ese programa. Identifica las palabras más importantes que utilizas en la explicación y haz que salgan en el código. Volviendo al ejemplo de antes, si dices "si el fichero está abierto, lo cierro", no escribas "if CurrentFile.fOpen<>NULL" !!!
    • Cuando implementes una solución, sigue el orden que cualquier ser humano esperaría. Prepara primero el trabajo y luego hazlo. Una buena idea (sugerida también por McConnell) consiste en escribir la solución en "cristiano" antes de comenzar a teclear código. Luego convierte esas frases en comentarios y, bajo él, introduce el código correspondiente.
    • Explica por qué no has podido hacerlo más simple. Cuando empezamos a trabajar en un código heredado, todos los programadores deseamos encontrarnos los comentarios adecuados en el sitio adecuado. Pues acuérdate de eso cuando seas tú el que has escrito el código.
    • La frase de "divide y vencerás" está tan manida que la llegamos a olvidar injustamente. ¿A que te joroba encontrar una rutina de 3 pantallas en el código ajeno? Muchas veces merece la pena hacer una rutina de una línea (del estilo "bool File.IsOpen( )") en aras de la legibilidad.
    • "Fortalecer la comunicación con sus compañeros"
    • "planificar su día antes de empezar a trabajar"

    Aplicación de La programación orientada a aspectos en el diseño e implementación de pruebas funcionales

    Creadores del documento: Javier J. Gutiérrez1, Darío Villadiego1, María J. Escalona2, Manuel Mejías2

    Departamento de Lenguajes y Sistemas Informáticos - Universidad de Sevilla dariovifer@yahoo.es, risoto@lsi.us.es

    RESUMEN

    En este trabajo exponemos cómo aplicar la programación orientada a aspectos para desarrollar pruebas unitarias. Primero presentamos la estrategia más utilizada para diseñar y escribir estas pruebas y, a continuación, identificamos y describimos un conjunto de escenarios donde esta estrategia no es adecuada. Para cada uno de estos escenarios se propone la aplicación de técnicas de programación orientada a aspectos para la realización de pruebas funcionales adecuadas. Por último desarrollamos un ejemplo práctico donde aparecen varios de estos escenarios e implementamos la solución que la programación orientada a aspectos aporta.

    PALABRAS CLAVES

    Programación orientada a aspectos, AOP, pruebas unitarias, AspectJ.

    1. INTRODUCCION

    1.1. Introducción a la programación orientada a aspectos.

    La programación orientada a aspectos [1] (a partir de ahora llamada AOP), fue presentada en público por Gregor Kickzales y su equipo de investigación de Palo Alto Research Center en 1996. Desde entonces ha tenido una gran presencia en entornos académicos y, actualmente, comienza a estar cada vez más presente en entornos de desarrollo reales.

    La AOP es un nuevo paradigma cuyo objetivo es aumentar la modularización de los sistemas software. En concreto, propone extraer y centralizar en un solo punto los "crosscutting concepts", los cuales son fragmentos de código que deben repetirse en todos o la mayoría de módulos del sistema, como el registro de trazas o la persistencia de objetos. La AOP no rompe con las técnicas de programación orientadas a objetos sino que las complementa y extiende.

    En la ilustración 1 se muestra la idea fundamental de este paradigma, consistente en centralizar en un solo punto todos los aspectos comunes a las clases que forman el sistema software.

    clip_image002[6]

    Ilustración 1. Evolución de un sistema orientado a objetos a un sistema orientado a aspectos.

    A continuación, en los siguientes puntos, se enuncian brevemente los conceptos básicos de la AOP y se explica la manera más común de desarrollar pruebas unitarias sin aplicar AOP.

    1.2. Conceptos básicos de AOP.

    En la tabla 1 se describen brevemente algunos conceptos básicos sobre los que se asienta la AOP y que serán necesarios a lo largo de este trabajo:

    Punto de unión(Join Point)

    Una posición bien definida dentro del código orientado a objetos, por ejemplo, la declaración de un método.

    Punto de ejecución (Advice)

    Fragmento de código que se ejecuta cuando se activa un punto de corte.

    Punto de corte(Pointcut):

    Un conjunto de condiciones aplicadas a un punto de unión que, al cumplirse, activarán el punto de corte y se ejecutará el punto de ejecución asignado a dicho punto de corte.

    Aspecto (Aspect)

    La combinación de puntos de corte, puntos de unión y puntos de ejecución.

    Tabla 1. Conceptos básicos de AOP.

    1.3. Cómo se diseña una prueba unitaria.

    Actualmente, en el proceso de desarrollo del software se ha aceptado como una técnica imprescindible el diseño e implementación de pruebas unitarias del código en desarrollo. Estas pruebas tienen como principales objetivos comprobar que el código se comporta de la manera esperada y prevenir la aparición de errores inesperados al modificar o refactorizar el código. También, en algunos casos como en el desarrollo dirigido por pruebas, las pruebas unitarias ayudan a obtener el diseño del código. Las ventajas de realizar este tipo de pruebas son: código más depurado, mayor seguridad a la hora de refactorizar y mayor velocidad de desarrollo, aparte de obtener código de mayor calidad.

    Actualmente las pruebas unitarias se desarrollan con herramientas tipo jUnit [3] y objetos mock [4]. Las herramientas tipo jUnit se aplican para comprobar que el estado de un objeto, los valores retornados por los métodos, excepciones lanzadas, etc, son los esperados. Los objetos ficticios o mocks, en cambio, permiten aislar el código a probar de las dependencias con otras clases colaboradoras mediante el desarrollo de clases ficticias que simulan el comportamiento de estas clases colaboradoras. Esto permite centrar la prueba sólo en el código que se desea probar.

    La estrategia para desarrollar este tipo de pruebas [4] consiste en extraer el comportamiento del colaborador a una interfaz y realizar dos implementaciones de la misma, una real que se utilizará en producción y una implementación ficticia, mediante un mock, que se utilizará en las pruebas unitarias. Para decidir la clase colaboradora a utilizar se puede añadir como parámetro del método.

    Los elementos que intervienen en este tipo de pruebas y su relación se muestran en la ilustración 2.

    clip_image004

    Ilustración 2. Relación entre la clase bajo prueba y un colaborador.

    Pero las pruebas unitarias no son siempre fáciles de desarrollar, por ejemplo cuando el comportamiento de un módulo depende en gran medida del estado del sistema en que es ejecutado. En el siguiente apartado comprobaremos como existen escenarios en los que esta estrategia presenta problemas a la hora de desarrollar pruebas unitarias y como podemos solventar estos problemas aplicando la AOP.

    2. AOP Y PRUEBAS UNITARIAS.

    2.1. AOP como complemento a pruebas unitarias con jUnit y Mocks.

    Hemos visto una estrategia muy común para diseñar pruebas unitarias mediante el binomio de herramientas jUnit o similares y objetos mocks. Sin embargo, existen una serie de escenarios, por ejemplo en arquitecturas cliente/servidor o al trabajar con clases y métodos sin estado o estáticos, donde esta estrategia es insuficiente para diseñar e implementar pruebas unitarias. Este tipo de escenarios podemos solventarlos de una forma práctica y elegante aplicando la AOP al diseño e implementación de pruebas unitarias.

    Estos escenarios se describen en los siguientes puntos junto con la aplicación de técnicas de AOP para solventarlos.

    2.2.1. Dependencia de datos.

    Un problema muy común al desarrollar pruebas unitarias es la dependencia de estas con los datos de prueba. Es necesario hacer las mínimas suposiciones posibles sobre los datos en los que basar las pruebas, por ejemplo un número de clientes determinado en una base de datos o la existencia de un cliente en concreto. Si no, algunas veces las pruebas se ejecutarán correctamente (cuando los datos reales coincidan con los esperados) y otras no, estando la causa del fallo no en el código bajo prueba sino en los datos que la prueba presupone.

    Una primera solución, muy común, es que la propia prueba gestione los datos necesarios, por ejemplo insertando y borrando clientes determinados, pero esto no es siempre posible, por ejemplo por compartir un mismo almacén de datos para todo el equipo de desarrollo, porque podría aumentaría tanto la complejidad y el tiempo de ejecución de la prueba que la haría inviable o porque los datos dependen del sistema sobre el que se ejecuta el software.

    Un ejemplo de esto último sería una aplicación que lance eventos según la hora del sistema. Si se quiere probar que el evento de las 6am se lanza adecuadamente sería necesario una manipulación manual del reloj del sistema, cosa que va en contra de la filosofía de la automatización de pruebas y no siempre será posible, por ejemplo si el código se ejecuta en un servidor en el que existen otras aplicaciones que también dependen del reloj del sistema para trabajar.

    Para solucionar este escenario con técnicas de AOP, la estrategia a seguir será desarrollar aspectos cuya misión sea interceptar las llamadas a los métodos que soliciten los datos. En concreto, aplicado al ejemplo anterior, será necesario escribir un aspecto que intercepte las llamadas al método que devuelve la hora del sistema, si dicho método es invocado desde la clase encargada de probar que el evento de las 6am se lanza adecuadamente, el aspecto devuelve como hora del sistema 6am, independientemente de la hora real.

    2.2.2. Pérdidas de rendimiento.

    Hemos visto en el apartado 1.3 como la estrategia a la hora de desarrollar pruebas con jUnit y mocks es extraer las clases colaboradoras como parámetros de los métodos bajo prueba para poder alternar entre la clase real y la clase ficticia sin necesidad de modificar el código. Sin embargo, esto puede tener un impacto inaceptable sobre el rendimiento del sistema.

    En arquitecturas cliente-servidor, como por ejemplo J2EE [5], aplicar esta estrategia a un método que será invocado remotamente implica tener realizar la serialización y posterior deserialización del objeto colaborador cada vez que se invoque al método, lo que, en la práctica, supone un coste tan alto que impide aplicar esta estrategia.

    Una solución aplicando técnicas de AOP consistirá en interceptar la creación del objeto colaborador para devolver un objeto mock siempre que el método se invoque dentro del ámbito de una prueba.

    2.2.3. Interacción entre métodos.

    Las herramientas estilo jUnit necesita la existencia de un estado comprobable, por ejemplo cambios en los atributos de un objeto, un resultado devuelto por un método, una excepción lanzada, etc. Por esto es difícil elaborar pruebas unitarias con esta herramienta cuando no hay ningún estado comprobable.

    Un ejemplo donde no existe un estado comprobable es en el caso en que existe un método encargado de almacenar los atributos de una clase en una base de datos y se impone como requisito la necesidad de reutilizar conexiones ya abiertas con la base de datos en vez de crear una nueva cada en cada llamada al método.

    En este ejemplo es difícil elaborar una prueba que verifique que efectivamente se reutiliza una conexión ya existente en vez de crear una nueva, ya que, en ambos casos, el comportamiento del método, almacenar los valores de los atributos, es igualmente válido.

    Aplicando técnicas de AOP es posible escribir una prueba que intercepte las llamadas y compruebe que se ha pedido una conexión ya existente en vez de crear una nueva.

    2.2.4. Clases sin estado y métodos estáticos.

    La estrategia para desarrollar pruebas con jUnit y mocks es extraer los métodos del colaborador a una interfaz y realizar dos implementaciones de ella: una real, utilizada en producción, y una ficticia mediante un objeto mock utilizada en el proceso de pruebas.

    Sin embargo, en el caso de la plataforma Java, no es posible aplicar esta estrategia cuando las dependencias entre la clase bajo prueba y la clase colaboradora están basadas en llamadas a métodos estáticos o cuando la clase colaboradora es una clase sin estados, ya que la plataforma Java no permite interfaces con métodos estáticos.

    La estrategia a seguir aplicando técnicas de AOP, será desarrollar un objeto mock basado en AOP que intercepte las llamadas a los métodos estáticos y simule el comportamiento de la clase real.

    3. Un ejemplo práctico.

    En este apartado se va desarrollar un ejemplo práctico que ilustra una situación en la que las pruebas unitarias basadas en jUnit y mocks no son suficientes para desarrollar correctamente las pruebas del código. A continuación se aplicarán técnicas de AOP para desarrollar una prueba unitaria adecuada.

    3.1. Descripción del ejemplo.

    Se desea desarrollar una prueba unitaria para verificar el correcto funcionamiento de un método llamado alerta() perteneciente a una clase llamada Control. Este método obtiene la temperatura de un sensor y devuelve verdadero o falso según la temperatura obtenida esté por encima o por debajo de un umbral especificado. Para obtener la temperatura del sensor el fabricante del mismo proporciona una clase sin estado (estática) llamada Sensor que dispone de un método llamado LeerTemperatura() que devuelve la temperatura actual del sensor.

    En la ilustración 3 se detallan los detalles de ambas clases relevantes para este supuesto práctico.

    clip_image006

    Ilustración 3. Clases del ejemplo.

    Todo el código, tanto las clases, las pruebas, como la clase suministrada por el fabricante, está implementado en lenguaje Java.

    3.2. Por qué el binomio jUnit + mock falla.

    Uno de los principios básicos para el diseño de pruebas unitarias es aislar el código que se desea probar de todas sus dependencias. Sin embargo, en este ejemplo aparecen dos de los escenarios que hemos identificado como problemáticos para el binomio jUnit+mocks que van a dificultar aplicar este principio. En primer lugar existe una dependencia de datos, ya que es imposible predecir que valor de temperatura va a devolver el sensor y por tanto, es imposible predecir cual va a ser el resultado del método alerta(), si debe activarse o no, por lo que no es posible comprobar si está trabajando correcta o incorrectamente.

    Una solución es reemplazar la clase Sensor por un mock que devuelva una temperatura conocida a priori. Con este valor es posible determinar el comportamiento esperado del método alerta() y escribir una prueba que verifique si lo cumple o no. La estrategia a aplicar para trabajar con mocks, comentada en el apartado 1.3, consiste en evitar esta dependencia extrayendo los métodos que la clase Control necesita de Sensor a una interfaz y escribiendo dos implementaciones de la misma, una implementación real que será la propia clase Sensor y una implementación ficticia que devolverá valores conocidos de antemano, llamada SensorMock.

    Aquí aparece otro de los escenarios identificados en el apartado anterior, ya que esta solución no puede ser aplicada si implementamos las clases en Java dado que este lenguaje no permite interfaces con métodos estáticos. Sin embargo aplicando técnicas de AOP sí va a ser posible diseñar una prueba con un mock de la clase Sensor creado con AOP, como se describe en el siguiente punto.

    3.3. Aplicación de técnicas de AOP para el desarrollo de una prueba unitaria.

    La estrategia a seguir en este caso, será crear una clase mock basada en AOP llamada AspectoSensorMock con un pointcut que enlace con las llamadas a la clase Sensor. El comportamiento asociado a este punto de corte consistirá en comprobar desde donde se lanzó la llamada y, si esta se lanzó desde el código de prueba, devolverá un valor establecido de antemano, o si se lanzó desde otro lugar, la llamada se ejecutará normalmente. Las clases y aspectos se muestran en la figura 4, utilizando la notación [6].

    clip_image008

    Ilustración 4. Clases y aspectos de la prueba.

    Una posible implementación de esta estrategia, cuyo objetivo es interceptar la llamada si viene de la clase de prueba y devolver el valor 42, escrita para la herramienta AspectJ se muestra en tabla 2.

    public aspect AspectoSensorMock {

    pointcut inTest() : execution(public void ControlTest.*());

    int around() :

    call( public static int LeerTemperatura() )

    && cflow(inTest())

    {

    return 42;

    }

    }

    Tabla 2. Mock de prueba del sensor escrito para AspectJ.

    4. CONCLUSIONES y futuros trabajos.

    4.1. Conclusiones.

    En este trabajo se ha presentado una guía para aplicar las técnicas de AOP como complemento al desarrollo de pruebas unitarias y suplir las carencias de las herramientas más habitualmente empleadas en el desarrollo de este tipo de pruebas. Los escenarios identificados y las soluciones propuestas basadas en AOP demuestran que estas técnicas puede aplicarse satisfactoriamente para encontrar nuevas soluciones más sencillas y eficientes a la hora de desarrollar pruebas unitarias.

    Aunque el hecho de tener que aprender un nuevo paradigma de programación y una nueva herramienta con su propio lenguaje de programación puede ser una barrera importante para los programadores a la hora de incorporar estas técnicas a un entorno de desarrollo real, las técnicas empleadas en este artículo son muy sencillas de aplicar y requieren adquirir muy pocos conocimientos nuevos. Su aplicación a entornos de desarrollo reales no requiere de grandes inversiones en tiempo, formación y recursos.

    4.2. Futuros trabajos.

    En este trabajo se han utilizado técnicas de AOP pero no lo esencial de AOP, que es el aumentar la modularización de los sistemas software extrayendo y centralizando en un sólo punto los fragmentos de código que deben repetirse en todos o la mayoría de módulos del sistema. Para corregir esto, se pretende continuar desarrollar otros enfoques de la AOP aplicada al proceso de prueba, como utilizar la AOP para reducir el número de pruebas unitarias necesarias incluyendo en el código aspectos que controlen elementos como precondiciones, invariantes, postcondiciones y restricciones, como, por ejemplo, que no se pueda acceder directamente a ningún atributo de ninguna clase, sino solo a través de una pareja de métodos set() y get().

    REFERENCias

    [1] D. Gradecki, Joseph; Lesiecki, Nicholas, 2.003. Mastering AspectJ Aspect-Oriented Programming in Java. Wiley & Sons, USA.

    [2] Isberg, Wes, 2.002. Get Test-Inoculated!. Software Development Magazine, No. Mayo.

    [3] jUnit: http://www.junit.org.

    [4] Mackinnon, Tim; Freeman, Steve; Craig, Philip. Endo-Testing: Unit Testing with Mock Objects. eXtreme Programming and Flexible Processes in Software Engineering - XP2000.

    [5] Alur, Deepak; Crupi, John; Malks, Dan. 2.003.J2EE PATTERNS core. Prentice Hall PTR, Upper Saddle River, NJ 07458, USA.

    [6] Renaud Pawlak, et al. 2002. A UML Notation for Aspect-Oriented Software Design. Project ObjectWeb. http://www.objectweb.org/

    Fallos y defectos. Su persistencia y estrategia para mitigarlos.

    Al respecto de los defectos reportados, su persistencia y/o mutación, sucede que los defectos reportados por Testing en algunos casos fueron persistentes hasta tres (3) ciclos y en algunas situaciones más. El defecto es tratado normalmente según el  proceso normal establecido. Lo cierto es que nunca tienen el mismo comportamiento ni los mismos síntomas y por lo general no se reportan como nuevos defectos sino que se le aumenta la persistencia y en el mismo defecto se reportan las No Conformidades. Así hasta que definitivamente se obtiene un resultado satisfactorio.

    Inicialmente puede decirse que el fallo es reportado por Testing con un bajo análisis de los defectos que lo provocan, lo cual dentro de un ciclo de pruebas de sistemas es correcto, inclusive sin ningún análisis sino la mera detección y reporte.

    El o los defectos puede/n escalar a otros niveles, como por ejemplo a las prueba de campo (producción), sin ser necesariamente un fallo y ser detectado/s allí con cierto grado de mutación.
    Se asume que el fallo persiste y se lo reconoce como si se tratara del mismo que alguna vez se detectó en fases anteriores de las pruebas, pero en realidad puede tener otra naturaleza. Un ejemplo son las fechas con formato invertido en algunos campos, las cuales no representan necesariamente un fallo salvo una incomodidad visual, pero que en algunas situaciones especiales pueden llevar a la toma de una decisión incorrecta si no se percibe por parte del usuario. 

    Es importante notar que esta mutación de defectos se va dando a medida que se hicieron correcciones sobre los fallos reportados y es aquí mismo donde uno se detiene a pensar en el por que de la persistencia.

    Me interesa analizar el contexto para reconocer esos motivos:

    Motivo 1

    Otro motivo importante es la falta de definición de los requerimientos, la esencia del concepto que da vida a los mismos para lograr tangibilizarlo como una funcionalidad.
    Este motivo podría tener muchos frentes para atacar, sin embargo yo me centro en que se debería aumentar el análisis que nos permita conocer la mecánica de uso involucrada y como se puede agregar valor funcional.

    Esto puede implicar pactar una interacción fluida con el Cliente o un Usuario Líder de modo tal que se involucre en el proyecto y valide la evolución de los requerimientos. Este mismo usuario en un esquema de validaciones y verificaciones, puede ayudarnos en gran medida a la detección temprana de cambios y principalmente al planteamiento de las estrategias para afrontarlos y establecer un criterio de fin en base a la completitud necesaria del requisito.

    Motivo 2

    También la falta de Estimación de Riesgos posibles para cada uno de los requisitos impacta en la aparición (formación diría yo) de los fallos y defectos, puesto a que es posible iniciar las fases de construcción sin tener conciencia de los problemas que debemos evitar para generar un componente completo. Este aspecto también favorece a la mutación de los defectos una vez que los fallos fueron detectado, reportados y "solucionados", debido que las resoluciones son encaradas del mismo modo en que se construyeron los componentes.

    Se puede considerar como muy valioso reconocer en que fase impactarían los riesgos detectados y de ese modo establecer la estrategia que mejor se ajuste a la mitigación, eliminación o aceptación del riesgo.
    Teniendo en cuenta que se debe evitar un impacto desfavorable, se pone en conocimiento de los riesgos detectados a los responsables de cada fase y luego al equipo involucrado en la fase. Para esto es posible que se deba tener un nombre claro del riesgo, una descripción de que lo produce y como se manifiesta (síntomas), los antecedentes de ocurrencia en caso de que los tuviera, y la cantidad de veces que se podría presentar según los antecedentes. Es importante que cada uno pueda realizar una lectura inteligente, simple y tienda a la estrategia correcta.

    Motivo 3

    Una vez que los fallos y/o defectos escalaron a un Ambiente de Producción, los mismos son reportados por los Usuarios Finales bajo algún esquema de "Mesa de Entrada al Soporte del Sistema". Allí mismo se incurre en una grave falencia al dar de alta los fallos y/o defectos, pues se lo hace sin un Subjet claro que exprese en forma simplificada del problema ("sujeto2+"situación no deseada"). Así mismo el cuerpo del reporte no es más que una descripción del incidentes sufrido por  usuario y por ende no involucra ningún análisis ni apunta a ninguna alternativa de solución.
    En este sentido es prácticamente imposible diagramar próximos pasos y coordinar acciones efectivas y eficientes (teniendo en cuenta un Calendario)

    Motivo 4

    Un importante motivo a considerar es la falta de análisis del fallo y los defectos que lo provocan. Así mismo no reconocer su impacto en el requerimiento del cliente y en su entorno de trabajo a nivel del negocio y no solo del sistema.

    El análisis de los fallos y defectos, tiene que tener al menos un responsable. Típicamente se imputa la responsabilidad a SQA, el cual debe reconocer los factores mencionados a nivel de impacto y riesgos, detectar la naturaleza de los fallos y posteriormente emitir el reporte de los mismos. Esto podría implicar también que el mismo equipo SQA es responsable de categorizar no solo el carácter de Severidad, sino también la Prioridad con la que se debe dar resolución a cada uno de los fallos.

    Una estrategia de este tipo puede denotar ciertas demoras en el proceso relentizandolo por no hacerse el reporte de fallos y/o defectos en forma instantánea, una vez que son detectados en los ciclos de pruebas. Sin embargo, puede por otro lado acelerar la calidad de las resoluciones disminuyendo la persistencia de los defectos y su mutación, como también garantiza en mayor grado que se resuelven en un orden que agrega valor al proceso en general.

     

    Es necesario crear una conciencia general para todos los involucrados en la Gestión de Problemas para propender a la generación de Soluciones Técnicas definidas correctamente según el propósito de cada resolución y que tomen como entrada los Subjetcs y Descripciones correctamente formados.
    Esta formación impactará directamente en la mejora del trabajo proactivo evitando el trabajo en solitario y aumentando las capacidades de seguimientos anticipados. Aunque parezca contradictorio, su vez genera un buen grado de independencia de trabajo para el resto del equipo de trabajo en base a la mejora sustancial que implica en nuestra base de conocimientos.
    También estimula la limpieza del proceso de la organización y facilita el conocimiento individual para fortalecer el conocimiento grupal.

    Modificar el "paradigma de pruebas" por el "paradigma de evidencias".

    En nuestra organización me he visto obligado a modificar el "paradigma de pruebas" por mi "paradigma de evidencias".
    Movilizado por la creciente demanda de agilidad que nos impone el ritmo de obtención de proyectos, ya sean para modificaciones de productos existentes o para construcción de nuevos, SQA trabaja fuertemente al principio de los proyectos, desde las minutas de requerimientos, pasando por la ERS, ERP, documentos de análisis, diseño y arquitectura (si es que los hubiere) para localizar los puntos débiles y fortalecerlos en base a observaciones de No Conformidad que obliguen a un enriquecimiento del proyecto antes de pasar a otras fases del mismo. Eso como primera medida.
    A partir de esos documentos, SQA comienza un análisis profundo con una alta tasa en la solicitud de consultas técnicas, investigaciones paralelas (documentación técnica, de negocios, de sistemas, modelos de datos, arquitecturas, patrones de diseño, etc.) entre otras actividades y empieza a formar el documento de calidad "Especificaciones SQA" si le quieres dar algún nombre. Este documento lo vamos formando y generando su trazabilidad en base a la descomposición de trabajo (WBS) que hicieron los analistas y líderes de proyectos en una fase de iniciación.
    Nuestros criterios de calidad son tanto para el proyecto, para el producto o sistema, como también para los servicios aledaños y son puestos "a mano" del área de desarrollo, como documento matriz para la guía de la calidad esperada.
    No generamos casos de pruebas, puestos que solicitamos EVIDENCIAS y este concepto introducido por mi, obliga al equipo de desarrollo a la generación de todos los elementos tangibles que garantizan la completitud y correctitud del componente entregable o no entregable que se construyó. Ya sea que se trate de un componente de interfase, una clase, objetos, esquemas de bases de datos, parámetros de conectividad o cualquier tipo de instanciación, debe quedar una evidencia que puede ser sujeta a una auditoría de calidad.
    De este modo, SQA no forma un equipo de testing que espera un producto final desplegado en un ambiente controlado para pruebas, ni forma juegos de pruebas de funcionalidad que puedan ser ejecutados en una fase determinada, pero si ejecuta el uso del sistema bajo un fuerte y concienzudo conocimiento del mismo, debido a que pudo generar los factores de evidencias mínimas requeridas y durante toda la fase de construcción, interactúa con el área de desarrollo haciendo un seguimiento de la formación de las evidencias a satisfacerse.
    Es posible que este proceder no reduzca el tamaño ni el esfuerzo de las pruebas, sino todo lo contrario, pero lo que si provoca, es una mejor distribución del esfuerzo para la obtención de calidad.
    Quizás no es fácil entender mi modelo, pero es una idea que puse en práctica y esta dando buenos resultados en nuestro ambiente de trabajo. Tenemos mejor adaptación a cambios hasta el punto tal de que SQA puede inclusive no enterarse de las modificaciones introducidas a ciertos requerimientos de algún proyecto en particular, pero minutos antes de recibir el entregable consolida el conocimiento con las EVIDENCIAS y reorganiza los criterios de calidad para aceptar o rechazar el componente. Puede parecer extremista, pero lo es también la exigencia de nuestras necesidades de facturación y los mercados y el volumen de pruebas sería imposible de manejar para un equipo como el nuestro, al menos.
    Así que tiro la punta del ovillo para que comencemos a debatir este modelo, si es que les parece debatible.

    Diseño de Interfaces de Usuario - Principios, Prototipos y Heurísticas para Evaluación

     

    1. Conceptos Generales

    2. Principios para el Diseño de Interfaces de Usuario

    3. Utilización de Prototipos en la Implementación de IU

    4. Heurísticas para la Evaluación de IU

    5. Caso Práctico

    6. Conclusiones

    7. Bibliografía

    Abstract

    El diseño de interfaces de usuario es una tarea que ha adquirido relevancia en el desarrollo de un sistema. La calidad de la interfaz de usuario puede ser uno de los motivos que conduzca a un sistema al éxito o al fracaso. Los principios que se presentan son de utilidad para creación de interfaces funcionales y de fácil operación. A pesar de no ser capaces de resolver todos los aspectos propios del contexto con el que se esté trabajando, pueden ser combinados con la prototipación y la aplicación de heurísticas de evaluación para facilitar el proceso de diseño. El presente artículo se centra en los componentes de software de las interfaces de usuario, quedando fuera del alcance de mismo otros aspectos, como hardware y documentación. Lo anteriormente expuesto se complementa con un caso práctico de diseño de interfaces de usuario, producto de realizar la actividad de “Definición de Interfaces de Usuario” (EFS 4) de la metodología Métrica Versión 2.

    Key words: evaluation, heuristics, principles, prototypes, user interfaces.

    1. Conceptos Generales

    La Interfaz de Usuario, en adelante IU, de un programa es un conjunto de elementos hardware y software de una computadora que presentan información al usuario y le permiten interactuar con la información y con el computadora. También se puede considerar parte de la IU la documentación (manuales, ayuda, referencia, tutoriales) que acompaña al hardware y al software.

    Si la IU está bien diseñada, el usuario encontrará la respuesta que espera a su acción. Si no es así puede ser frustrante su operación, ya que el usuario habitualmente tiende a culparse a sí mismo por no saber usar el objeto.

    Los programas son usados por usuarios con distintos niveles de conocimientos, desde principiantes hasta expertos. Es por ello que no existe una interfaz válida para todos los usuarios y todas las tareas. Debe permitirse libertad al usuario para que elija el modo de interacción que más se adecúe a sus objetivos en cada momento. La mayoría de los programas y sistemas operativos ofrecen varias formas de interacción al usuario.

    Existen tres puntos de vista distintos en una IU: el del usuario, el del programador y el del diseñador (analogía de la construcción de una casa). Cada uno tiene un modelo mental propio de la interfaz, que contiene los conceptos y expectativas acerca de la misma, desarrollados a través de su experiencia.

    El modelo permite explicar o predecir comportamientos del sistema y tomar las decisiones adecuadas para modificar el mismo. Los modelos subyacen en la interacción con las computadoras, de ahí su importancia.

    Modelo del usuario: El usuario tiene su visión personal del sistema, y espera que éste se comporte de una cierta forma. Se puede conocer el modelo del usuario estudiándolo, ya sea realizando tests de usabilidad, entrevistas, o a través de una realimentación. Una interfaz debe facilitar el proceso de crear un modelo mental efectivo.

    Para ello son de gran utilidad las metáforas, que asocian un dominio nuevo a uno ya conocido por el usuario. Un ejemplo típico es la metáfora del escritorio, común a la mayoría de las interfaces gráficas actuales.

    Modelo del diseñador: El diseñador mezcla las necesidades, ideas, deseos del usuario y los materiales de que dispone el programador para diseñar un producto de software. Es un intermediario entre ambos.

    El modelo del diseñador describe los objetos que utiliza el usuario, su presentación al mismo y las técnicas de interacción para su manipulación. Consta de tres partes: presentación, interacción y relaciones entre los objetos (Figura 1).

    La presentación es lo que primero capta la atención del usuario, pero más tarde pasa a un segundo plano, y adquiere más importancia la interacción con el producto para poder satisfacer sus expectativas. La presentación no es lo más relevante y un abuso en la misma (por ejemplo, en el color) puede ser contraproducente, distrayendo al usuario.

    La segunda parte del modelo define las técnicas de interacción del usuario, a través de diversos dispositivos.

    La tercera es la más importante, y es donde el diseñador determina la metáfora adecuada que encaja con el modelo mental del usuario. El modelo debe comenzar por esta parte e ir hacia arriba. Una vez definida la metáfora y los objetos del interfaz, los aspectos visuales saldrán de una manera lógica y fácil.

    clip_image001

    clip_image002

    Figura 1. Representación del modelo del diseñador: el look-and-feel iceberg, de IBM (1992)

    Estos modelos deben estar claros para los participantes en el desarrollo de un producto, de forma que se consiga una interfaz atractiva y a la vez efectiva para el trabajo con el programa.

    Una interfaz no es simplemente una cara bonita; esto puede impresionar a primera vista pero decepcionar a la larga. Lo importante es que el programa se adapte bien al modelo del usuario, cosa que se puede comprobar utilizando el programa más allá de la primera impresión.

    Modelo del programador: Es el más fácil de visualizar, al poderse especificar formalmente. Está constituido por los objetos que manipula el programador, distintos de los que trata el usuario (ejemplo: el programador llama base de datos a lo que el usuario podría llamar agenda). Estos objetos deben esconderse del usuario.

    Los conocimientos del programador incluyen la plataforma de desarrollo, el sistema operativo, las herramientas de desarrollo y especificaciones. Sin embargo, esto no significa necesariamente que tenga la habilidad de proporcionar al usuario los modelos y metáforas más adecuadas. Muchos no consideran el modelo del usuario del programa, y sí sus propias expectativas acerca de cómo trabajar con la computadora.

    2. Principios para el Diseño de Interfaces de Usuario

    Existen principios relevantes para el diseño e implementación de IU, ya sea para las IU gráficas, como para la Web.

    Anticipación

    Las aplicaciones deberían intentar anticiparse a las necesidades del usuario y no esperar a que el usuario tenga que buscar la información, recopilarla o invocar las herramientas que va a utilizar.

    clip_image004

    En la Figura 2 se ilustra como el procesador de texto se anticipa a las necesidades del usuario, proporcionando las características del texto seleccionado -fuente, tamaño, alineación, etc.- permitiendo que el usuario pueda modificarlas ágilmente.

    Autonomía

    La computadora, la IU y el entorno de trabajo deben estar a disposición del usuario. Se debe dar al usuario el ambiente flexible para que pueda aprender rápidamente a usar la aplicación. Sin embargo, está comprobado que el entorno de trabajo debe tener ciertas cotas, es decir, ser explorable pero no azaroso.

    clip_image006

    En la Figura 3 se visualiza un diseño incorrecto de interfaz de usuario. La cantidad de opciones propuestas propone un grado de complejidad que no permite que el usuario pueda aprender a utilizar el sistema en forma progresiva.

    Es importante utilizar mecanismos indicadores de estado del sistema que mantengan a los usuarios alertas e informados. No puede existir autonomía en ausencia de control, y el control no puede ser ejercido sin información suficiente. Además, se debe mantener información del estado del sistema en ubicaciones fáciles de visualizar.

    clip_image008

    En la Figura 4 se ejemplifica una incorrecta disposición de componentes en la IU. El reloj no debe ser incorporado en el menú del sistema ya que aporta confusión al usuario. Para mantenerlo informado sería mas adecuado colocarlo en la barra de estado del sistema.

    Percepción del Color

    clip_image010Aunque se utilicen convenciones de color en la IU, se deberían usar otros mecanismos secundarios para proveer la información a aquellos usuarios con problemas en la visualización de colores

    En la Figura 5 se representa un mecanismo secundario muy utilizado para ejecución de comandos: los comandos abreviados (shortcut-keys). Sin embargo la aplicación presenta un problema de inconsistencia ya que define combinaciones de teclas que difieren a lo esperado por el usuario, por ejemplo Alt+< en lugar de Alt+B.

    Valores por Defecto

    No se debe utilizar la palabra “Defecto” en una aplicación o servicio. Puede ser reemplazada por “Estándar” o “Definida por el Usuario”, “Restaurar Valores Iniciales” o algún otro término especifico que describa lo que está sucediendo. Los valores por defecto deberían ser opciones inteligentes y sensatas. Además, los mismos tienen que ser fáciles de modificar.

    Consistencia

    Para lograr una mayor consistencia en la IU se requiere profundizar en diferentes aspectos que están catalogados en niveles. Se realiza un ordenamiento de mayor a menor consistencia:

    1. Interpretación del comportamiento del usuario: la IU debe comprender el significado que le atribuye un usuario a cada requerimiento. Ejemplo: mantener el significado de las los comandos abreviados (shortcut-keys) definidos por el usuario.

    2. Estructuras invisibles: se requiere una definición clara de las mismas, ya que sino el usuario nunca podría llegar a descubrir su uso. Ejemplo: la ampliación de ventanas mediante la extensión de sus bordes.

    3. Pequeñas estructuras visibles: se puede establecer un conjunto de objetos visibles capaces de ser controlados por el usuario, que permitan ahorrar tiempo en la ejecución de tareas específicas. Ejemplo: ícono y/o botón para impresión.

    4. Una sola aplicación o servicio: la IU permite visualizar a la aplicación o servicio utilizado como un componente único. Ejemplo: La IU despliega un único menú, pudiendo además acceder al mismo mediante comandos abreviados.

    5. Un conjunto de aplicaciones o servicios: la IU visualiza a la aplicación o servicio utilizado como un conjunto de componentes. Ejemplo: La IU se presenta como un conjunto de barras de comandos desplegadas en diferentes lugares de la pantalla, pudiendo ser desactivadas en forma independiente.

    6. Consistencia del ambiente: la IU se mantiene en concordancia con el ambiente de trabajo. Ejemplo: La IU utiliza objetos de control como menúes, botones de comandos de manera análoga a otras IU que se usen en el ambiente de trabajo.

    7. Consistencia de la plataforma: La IU es concordante con la plataforma. Ejemplo: La IU tiene un esquema basado en ventanas, el cual es acorde al manejo del sistema operativo Windows.

    clip_image013

    En la Figura 6 puede observarse la mejora en la consistencia de las pequeñas estructuras visibles (3.) para los sistemas gráficos basados en ventanas. La inclusión de la opción X para cerrar la ventana –operación comunmente utilizada en estas aplicaciones- simplifica la operatividad del mismo.

    La inconsistencia en el comportamiento de componentes de la IU debe ser fácil de visualizar. Se debe evitar la uniformidad en los componentes de la IU. Los objetos deben ser consistentes con su comportamiento. Si dos objetos actúan en forma diferente, deben lucir diferentes. La única forma de verificar si la IU satisface las expectativas del usuario es mediante testeo.

    Eficiencia del Usuario

    Se debe considerar la productividad del usuario antes que la productividad de la máquina. Si el usuario debe esperar la respuesta del sistema por un período prolongado, estas pérdidas de tiempo se pueden convertir en pérdidas económicas para la organización. Los mensajes de ayuda deben ser sencillos y proveer respuestas a los problemas. Los menúes y etiquetas de botones deberían tener las palabras claves del proceso.

    clip_image015

    En la Figura 7 se demuestra como una incorrecta definición de las palabras clave de las etiquetas de los botones de comando puede confundir al usuario. Los botones OK y Apply aparentan realizar el mismo proceso. Esto puede solucionarse suprimiendo uno de ellos si realizan la misma tarea o etiquetándolos con los nombres de los procesos específicos que ejecutan.

    Ley de Fitt

    El tiempo para alcanzar un objetivo es una función de la distancia y tamaño del objetivo. Es por ello, que es conveniente usar objetos grandes para las funciones importantes.

    clip_image017

    En la Figura 8 se puede apreciar la relación entre los elementos de diseño de pantalla y su percepción visual. El número de elementos visuales que perciben son: en el caso a) 1 (el fondo); en b) 3 (la línea, lo que está encima y lo que está debajo); en c) son 5 (el espacio fuera del recuadro, el recuadro, la línea y el espacio encima y debajo de ésta); finalmente, en d) el número se eleva a 35, siguiendo el mismo criterio. Conclusión: cada elemento nuevo que se añade influye más de lo que se piensa en el usuario.

    Interfaces Explorables

    Siempre que sea posible se debe permitir que el usuario pueda salir ágilmente de la IU, dejando una marca del estado de avance de su trabajo, para que pueda continuarlo en otra oportunidad.

    Para aquellos usuarios que sean noveles en el uso de la aplicación, se deberá proveer de guías para realizar tareas que no sean habituales.

    Es conveniente que el usuario pueda incorporar elementos visuales estables que permitan, no solamente un desplazamiento rápido a ciertos puntos del trabajo que esté realizando, sino también un sentido de “casa” o punto de partida.

    La IU debe poder realizar la inversa de cualquier acción que pueda llegar a ser de riesgo, de esta forma se apoya al usuario a explorar el sistema sin temores.

    Siempre se debe contar con un comando “Deshacer”. Este suprimirá la necesidad de tener que contar con diálogos de confirmación para cada acción que realice en sistema.

    El usuario debe sentirse seguro de poder salir del sistema cuando lo desee. Es por ello que la IU debe tener un objeto fácil de accionar con el cual poder finalizar la aplicación.

    Objetos de Interfaz Humana

    Los objetos de interfaz humana no son necesariamente los objetos que se encuentran en los sistemas orientados a objetos. Estos pueden ser vistos, escuchados, tocados o percibidos de alguna forma. Además, estos objetos deberían ser entendibles, consistentes y estables.

    clip_image020

    En la Figura 9 se presentan barras de controles que simplifican la operación de un sistema. A través de las ilustraciones que poseen los mismos, el usuario puede aprender fácilmente su uso. Si se mantienen para estos botones las mismas asignaciones de procesos en diferentes sistemas, la comprensión del funcionamiento de los mismos se hace mas sencilla.

    Uso de Metáforas

    Las buenas metáforas crean figuras mentales fáciles de recordar. La IU puede contener objetos asociados al modelo conceptual en forma visual, con sonido u otra característica perceptible por el usuario que ayude a simplificar el uso del sistema.

    clip_image023

    En la Figura 10 se compara la aplicación de metáforas en el desarrollo de una IU. En el primer caso, se utiliza incorrectamente la metáfora de una cámara de video para representar el procesamiento de un documento por una impresora. Se puede observar que el botón << carece de sentido, ya que no se puede volver atrás un trabajo que ya ha sido impreso. En el segundo caso, la metáfora de la agenda es utilizada correctamente para la implementación de una agenda electrónica.

    Curva de Aprendizaje

    El aprendizaje de un producto y su usabilidad no son mutuamente excluyentes. El ideal es que la curva de aprendizaje sea nula, y que el usuario principiante pueda alcanzar el dominio total de la aplicación sin esfuerzo.

    Reducción de Latencia

    Siempre que sea posible, el uso de tramas (multi-threading) permite colocar la latencia en segundo plano (background). Las técnicas de trabajo multitarea posibilitan el trabajo ininterrumpido del usuario, realizando las tareas de transmisión y computación de datos en segundo plano.

    Protección del Trabajo

    Se debe poder asegurar que el usuario nunca pierda su trabajo, ya sea por error de su parte, problemas de transmisión de datos, de energía, o alguna otra razón inevitable.

    Auditoría del Sistema

    La mayoría de los navegadores de Internet (browsers), no mantienen información acerca de la situación del usuario en el entorno, pero para cualquier aplicación es conveniente conocer un conjunto de características tales como: hora de acceso al sistema, ubicación del usuario en el sistema y lugares a los que ha accedido, entre otros. Además, e