Archivo por meses: marzo 2006

¿Qué es el Testing de Software? Y ¿Por qué es esto tan difícil?

Todos los desarrolladores han sentido la frustración de recibir reportes de errores. ¿Por qué no descubrieron esos errores antes? ¿Qué paso con las pruebas unitarias y de integración? La respuesta a estas preguntas puede estar en  los siguientes aspectos:

 

  1. Los usuarios ejecutaron código que no fue probado.
  2. El orden de la secuencia de comandos ejecutados fue diferente a lo probado.
  3. El usuario ingreso una serie de datos no validados.
  4. El sistema está instalado en un ambiente donde no fue probado.

 

Plan de pruebas y Tester

 

Para crear un plan de pruebas efectivo se deben considerar múltiples aspectos relacionados con el sistema que se está probando. Por ejemplo funcionalidades, ingreso de de datos, ambiente de ejecución, etc.

 

Por eso, un Tester de profesional debe tener Skills en múltiples áreas para poder diseñar un plan de pruebas efectivo.

 

El proceso de pruebas, al igual que las fases de desarrollo de RUP, tiene 4 fases. Estas fases son:

 

  1. Modelar el ambiente del sistema.

La primera tarea del Tester es simular como el sistema se relaciona con su ambiente. Para esto se deben identificar todas las interfaces del sistema y los datos que pasan por las mismas.

 

Existen 4 tipos de interfaces en los sistemas:

 

·         Interfaces Humanas.

·         Interfaces de software (API)

·         Interfaces de Archivos (Datos)

·         Interfaces de comunicaciones (Redes y Device)

 

El siguiente paso que el Tester debe hacer es encontrar las acciones que el usuario puede realizar y que harían que el software deje de comportarse de manera consistente. Por ejemplo:

 

·         ¿Qué pasa si un cliente cambia los mismos registros que otro en el mismo tiempo? Esto es manejo de la concurrencia.

·         ¿Qué pasa si se baja el sistema en medio de una transacción?

·         ¿Qué pasa si dos sesiones del sistema acceden a la misma API?

·         Etc….

 

Dentro de las consideraciones que debe tener en mente un Tester se encuentran la forma en que se elogien los valores de las variables de entrada y la secuencia cómo ellas son ingresadas.

 

En la selección de las variables existe un método llamado BOUNDARY VALUE PARTITIONING. Básicamente es para buscar los valores esperados y las condiciones de borde de cada variable. Además de lo anterior se debe considerar la entrada concurrente de datos y la correcta aislamiento entre cada sesión que está manipulando los valores de las variables.

 

La segunda consideración tiene que ver con el orden en que se ingresan las variables. Tres técnicas son usadas para apoyar la definición del orden de ingreso de los datos. La más común es usar diagramas de estados (UML) para modelar el ingreso de datos y el estado en que el sistema debe quedar en cada movimiento.

 

Otra técnica es la que nos brindan las herramientas basadas en la teoría del lenguaje. Por ejemplo expresiones regulares y gramaticales. Un ejemplo de esto sería:

 

Filemenu.Open filename* (ClickOpen | ClickCancel)

 

Por último, las más desconocidas y complejas son las técnicas estocásticas y de algoritmos genéticos. Estos modelos combinan los valores a ingresar y su secuencia para producir palabras y oraciones Sintacticamente correctas.

 

  1. Elegir los escenarios de pruebas.

 

Muchos modelos de dominio y grupos de variables representan un infinito número de escenarios de pruebas, cada uno con sus costos y plazos de pruebas. Siendo realistas, en un proyecto de software los presupuestos son limitados por lo que debemos discriminar en que escenarios se probaran y cuales no.

 

¿Cuál es el criterio que se debe usar? Existen muchas respuestas para esta pregunta, siendo la más común entre los Tester el uso del criterio de Cobertura.  Este criterio dice que por lo menos una vez cada línea del código y las entradas del sistema han sido probadas. Esto es el criterio mínimo esperable para las pruebas de un sistema antes de ser entregado.

 

Si desarrollamos la idea de la cobertura del código, nos damos cuenta que esto es muy complejo porque además de considerar que todo el código haya sido ejecutado al menos una vez, caemos en el tema de las rutas de ejecución. Una ruta de ejecución es la secuencia en que las líneas de código son ejecutadas. Las rutas de ejecución para el mismo código pueden llegara a ser infinitas.

 

Para acotar estas infinitas posibilidades el Tester busca los escenarios más comunes de ejecución, lo típico que un usuario ejecutaría.

 

Existen tres criterios reconocidos para elegir las rutas de ejecución a ser probadas. El primer criterio es el que pone foco en la cobertura de las estructuras de control. La idea es que las pruebas hagan que la ruta de ejecución pase por todas las opciones que abren las estructuras de control. Por ejemplo IF, que en ambos casos se prueben.

 

El segundo criterio es DATAFLOW, esto es que todas las estructuras de datos sean iniciadas y usadas.

 

Por último, el criterio menos usado, en mi opinión, es FAULT SEEDING. La idea de está técnica es sembrar errores intencionalmente en el código los cuales son encontrados con los casos de pruebas, idealmente se encuentran también los errores originales.

 

Respecto a los criterios de selección de casos de pruebas para las entradas de datos son simplemente la cobertura de todas las entradas de datos del sistema y la técnica llamada DISCRIMINATION CRITERION, que consiste en probar aleatoriamente hasta llegar a cubrir las entradas posibles del sistema.

 

  1. Ejecutar y evaluar los escenarios de pruebas.

 

Después de tener definidos los escenarios de pruebas viene la labor de ejecutar lo que se definió. Hacer esto de manera manual es un trabajo arduo y puede generar nuevos errores.  Existen herramientas que ayudan al Tester a obtener información interna  del sistema para mejorar sus pruebas.

 

La evaluación del escenario de pruebas basado en las salidas del sistema no es fácil de automatizar. La evaluación pasa por el Tester que compara los valores esperados versus lo que realmente obtuvo. Para está sencilla tarea se asume que las especificaciones, valores esperados, son correctos. Actualmente esta evaluación es hecha por el Oráculo Humana, llamado Tester.

 

Dos aproximaciones para evaluar las pruebas

 

Existen dos opciones para ejecutar las pruebas:

 

·         Formalismo: poco usado en la práctica porque requiere especificaciones formales de buena calidad. En las especificaciones pueden haber errores, que serían traspasados al sistema.

 

·         Código embebido: Es software incrustado en el sistema que toma medidas de estados de objetos, valores de variables y datos internos. Esto ayuda al Tester porque entrega información desde el interior del sistema y no sólo de las salidas de pantalla.

 

Existen otro tipo de pruebas basadas en código embebido. Está segundo tipo de pruebas es mucho más sofisticado y son código de pruebas del código del sistema. Está técnica se llama SELF-TESTING PROGRAMS.

 

Pruebas de Regresión

 

En un equipo serio de desarrollo, una vez que el Tester ha reportado errores encontrados en el sistema, estos son asignados a los desarrolladores que correspondan para que los reparen. Los desarrolladores después de esto generan una nueva versión del sistema.

 

En ese momento se debe resolver la pregunta ¿Qué es lo que debo volver a probar? La respuesta a la pregunta no es trivial porque cada reparación de un error puede tener cuatro consecuencias posibles:

 

·         Sólo repara el problema reportado.

·         Falla en resolver el problema reportado.

·         Resuelve el problema, pero genera otras fallas.

·         No resuelve el problema y además genera otras fallas.

 

Lo razonable sería volver a probar todo para así hacerse cargo de todas las opciones que el FIX (código reparado) puede provocar. Esto es, cómo siempre, económicamente inviable para el proyecto de software.

 

Estas pruebas realizadas sucesivamente en cada versión del sistema se llaman pruebas de Regresión.

 

Además de tener que resolver que probar para validar el FIX, ocurre frecuentemente que en cada nueva versión del sistema se incorpora funcionalidades nuevas que deben ser probadas también. Muchas veces el Tester privilegia las pruebas sobre las nuevas funcionalidades en vez de hacer pruebas de regresión, esto es malo pero consistente con el criterio de cobertura expuesto en la fase de diseño de las pruebas.

 

  1. Medir el progreso de las pruebas.

 

Cuando uno juega el rol de Tester, el Project Manager siempre debe estarnos preguntando ¿Cómo vamos con las pruebas? No siempre tenemos una respuesta fundamentada para entregarle. Básicamente tenemos el número de veces que hemos probado, los casos, las fallas, etc. Es decir contamos eventos, pero esto no nos asegura que estemos avanzando hacia el objetivo del proyecto.

 

Para tener una idea del avance de las pruebas debemos preocuparnos de verificar la completitud estructural y funcional. Para poder comprobar los aspectos estructurales podemos formularnos las siguientes preguntas:

 

·         ¿Hemos probado los errores de programación comunes?

·         ¿Hemos ejercitado todo el código fuente?

·         ¿Hemos forzado todos los datos internos para que sean inicializados y usados?

·         ¿Hemos encontrado todos los errores sembrados?

 

Para la completitud estructural por su parte son:

 

·         ¿Hemos pasado a través de las formas en que el sistema puede fallar? ¿Hemos realizado todos los Test que lo validan?

·         ¿Hemos aplicado todas las entradas de datos?

·         ¿Hemos explorado todos los estados del sistema?

·         ¿Hemos ejecutado todos los escenarios que espero que el usuario use?

 

Estas preguntas ayudan a los Tester para poder estimar el número de fallas que quedan en el sistema por ser descubiertos y la probabilidad que estas ocurran en el ambiente productivo del sistema. Está es la medida cuantitativa que todos buscamos.

Crash que gran película

Fui a ver esta película al pésimo cine del alto Las Condes. Aunque estaba en esa pésima sala, la  película fue notable.

 

La película muestra la peor cara de los Angeles, dónde todos viven con miedo y encerrados. Los americanos “clásicos” temen  de los Negros, árabes, Chinos, etc.

 

Cuando vi la película me recordó a “Perros de la calle”, un mosaico de historias entrelazadas por eventos accidentales. También a perros de la calle.

 

Para ser una película americana, con armas y explosiones es buenísima. El guión esta muy bien escrito, porque le da su espacio a una docena de personajes profundamente bien desarrollados.

 

En el transcurso de la película la primera opinión que uno se hace de un personaje, va cambiando según pasan los cuadros, llegando muchas veces a la conclusión que nada es como lo vemos tras el prisma de nuestros prejuicios.

 

Altamente recomendable verla.

Siempre son 3 respuestas, las que abren miles de nuevas preguntas.

Pensando en cómo introducir el tema de WSE (Web Service mejorados) pensé en una analogía con el mundo de la física de principios del siglo XX. Los Web Services resolvieron 3 problemas fundamentales de la integración de aplicaciones: llamada a procedimientos remotos, descripción de los servicios, interoperabilidad basada en XML.

 

Con la solución de Web Services, si bien se solucionaron esos tres aspectos,  se crearon nuevas necesidades o problemas. Por ejemplo seguridad de las comunicaciones, manejo de contexto transaccional, etc.

 

Hacia fines del 1900 habían algunos puzzles no resueltos a los que Einstein aporta notables explicaciones. En particular 3 muy interesantes, que abren infinitas nuevas preguntas no resueltas.

 

1. La teoría electromagnética de Maxwell no era consistente con la relatividad de Galileo. Ello se traduce en que un sistema electromagnético no se comporta de igual forma en tierra o arriba de un tren en movimiento. Si bien Lorentz aporta una solución matemática a ese problema, Einstein introduce la Teoría de la Relatividad, en la  que el tiempo obedece las mismas leyes de transformación que las coordenadas espaciales.

 

Luego de la Relatividad Especial Einstein desarrolla la Relatividad general y con ella la Cosmología. Esta última es un problema abierto y sumamente complicado, con una serie de ‘patologías’ o inconsistencias. El tema actual de la materia oscura es uno de los     ingredientes en esta discusión. Conclusiones tales como ‘más de la mitad de la materia del universo es materia oscura’ surge de interpretaciones observacionales a la luz de la Relatividad General de Einstein. Materia oscura es aquella que no detectamos desde  tierra pero que si es necesaria suponerla para explicar la dinámica observada del universo.

 

2. El efecto fotoeléctrico es el otro aporte: para explicarlo introduce la noción del ‘foton’ o quanto de energia. Una partícula sin masa que se mueve a la velocidad de la luz. Posteriormente el foton entra como ingrediente fundamental en la teoría cuantica de campos, las cuales adolecen de otra serie de problemas tales como  ‘renormalizacion’, que es una manera poco elegante de sacarse se encima algunos ‘infinitos’ que aparecen en las expresiones matemáticas.

 

Estas mismas teorías de campo se extienden a otras interacciones pero el problema es aun más complicado, llevándolas a límites casi  inmanejables: problemas abiertos.

 

3. El movimiento browniano. Se trata de ‘zig-zageo’ (observados con microscopio) en el polvo de polen en suspensión acuosa. Este movimiento interpreta Einstein como respuesta de los gránulos al impacto de corpusculos muy pequeños (átomos!).

 

Su descripción cuantitativa de las observaciones constituye el primer aporte serio (no especulativo) acerca de la naturaleza corpuscular de la materia. De ahí en adelante la identificación de estructuras y sub estructuras, dentro de las nuevas nociones de  espacio-tiempo, nos lleva a plantearnos nuevamente que es la materia: partículas? ondas? campos? o algo mas complejo? Como participa la energía en todo esto? Que es la materia? el espacio-tiempo? Como condicionan al universo? su origen, estado actual y lo que viene?

 

PD: Agradezco a Hugo Arellano, quien me explico estos problemas de manera que yo pudiera comprenderlos dentro de mis limitadas capacidades.

Introducción a conceptos de seguridad en Web Services, Autentificación

Conceptos importantes

 

  • Autentificación: Proceso de verificar la identidad de una persona.
  • Autorización: proceso de permitir el acceso de una persona a un sistema.
  • Credenciales: El juego de artefactos usados para proveer la identidad a un cliente.

Introducción

 

En la historia de la física Albert Einstein resolvió las 3 preguntas que hasta ese momento quitaban el sueño a los físicos de finales del siglo IX y principios del siglo XX, pero al hacerlo dejo al descubierto muchos nuevos desafíos para los físicos modernos.

 

De una manera análoga, los Web Services vinieron a solucionar los 3 problemas fundamentales de la interoperabilidad de sistemas. Esta tecnología resolvío los problemas de descripción de los contratos de los servicios (WSDL), la invocación de procedimientos (SOAP) y la interoperabilidad entre plataformas (Uso de XML). Al utilizar está solución, los desarrolladores se enfrentaron a nuevos problemas  generados por el uso de está tecnología. Algunos ejemplos de los esos problemas son seguridad, desempeño, manejo de contextos trasnacionales y confiabilidad, por nombrar algunos.

 

En esté articulo revisaremos los conceptos más importantes de seguridad en Web Services. Abordaremos los patrones de autentificación y protección de mensajes.

 

Patrones de autentificación

Los patrones de autentificación tienen como objetivo guiarnos en las decisiones de la aproximación con que nuestro sistema enfrentará el problema de la autentificación de los clientes que quieren usar los recursos expuestos por nuestros servicios.

 

En está sección veremos dos patrones fundamentales:

 

  • Autentificación directa
  • Autentificación indirecta (Brokered Authentication)

 

Ambas alternativas buscan solucionar el problema de identificación del cliente que hace uso de los recursos del Servicio Web. Cuando el cliente y servidor tienen una relación de confianza que les permite intercambiar y validar credenciales donde se incluye el password, el patrón de autentificación directa aplica perfectamente.

 

En un escenario en que el cliente y el servidor no tienen una relación de confianza, el camino es utilizar autentificación indirecta. En esta opción el cliente se autentifica contra un tercero (Broker) quien le entrega un Token al cliente para que lo entregue al Servicio cómo credencial. El Web Service toma el Token y lo valida con el Broker. Este tercero, llamado Broker, tiene relaciones de confianza con el cliente y el servidor.

 

Además de la razón de compartir relaciones de confianza entre el cliente y el servidor existen múltiples criterios que se deben tener en cuenta para la toma de decisión de cuál aproximación utilizar para la autentificación de los Web Services. Algunos de los criterios que se deben considerar al momento de evaluar son:

 

  • ¿Qué necesita el servicio para autentificar al cliente? ¿Password? ¿Certificados? ¿Algo más?
  • ¿El Web Services es capas de acceder al servicio de autentificación directamente?
  • ¿Cuál es la infraestructura existente?
  • ¿Es necesario soportar SSO?
  • ¿Necesita la aplicación un contexto de sesión?
  • ¿Estamos trabajando en el mundo Microsoft? ¿Tenemos Windows Impersonate?

 

Estas preguntas y más se deben tener en cuenta al momento de optar por un patrón de autentificación.

Autentificación directa

El escenario de la autentificación directa es que el Servidor Web espera del cliente las credenciales que él mismo validará con el servicio de autentificación (Identity Store).

 

El problema de está aproximación es el cómo el Servicio Web validará las credenciales presentadas por el cliente.

 

Las principales razones para utilizar autentificación directa son:

 

  • Las credenciales presentadas por el cliente al Servicio Web son basadas en un secreto compartido, por ejemplo un password.
  • El Servicio Web tiene capacidad para validar credenciales contra un servicio de autentificación.
  • El Servicio Web es simple y no necesita SSO o soporte para asegurar la no repudiación.
  • EL cliente y el Servicio Web confían uno en el otro en el manejo de seguro de credenciales.

 

Se puede utilizar autentificación directa cuando el Servicio Web actúa cómo un servicio de validación de las credenciales del cliente. Las  credenciales usadas, que se aceptan por el hecho de poseerlas (proof-of-possession), están basadas en un secreto compartido y son verificables contra un Identity Store.

 

Los participantes de este patrón de autentificación son los siguientes:

 

  • Cliente: Aplicación que accede al Servicio Web, entrega las credenciales para la autentificación.
  • Servicio Web: Web Services que requiere las credenciales para el uso de sus recursos.
  • Identity Store: Es la entidad que almacena las credenciales de los clientes.

  

El siguiente Diagrama muestra la secuencia de mensajes que se intercambian en entre los participantes de este patrón de autentificación.

 

 

 

Ilustración 1: Mensajes del patrón de autentificación directa.

 

Los beneficios del uso del patrón de autentificación directa son:

 

  • Simpleza.
  • Si existe un problema de seguridad en la relación de confianza del cliente y Servidor, sólo compromete a este cliente y servidor y no a todo un modelo.

 

Las consecuencias resultantes del uso de autentificación directa son:

 

  • Este modelo no permite el uso de SSO, lo que implica el paso de credenciales en cada llamada. Es posible sortear este asunto con cache de credenciales, pero si las credenciales son del tipo password está solución implica problemas de seguridad.
  • Si se comienzan ha crear muchos servicios Web, el sistema se vuelve muy complejo porque las relaciones de confianza entre clientes y servidores son punto a punto, dificultando las labores administrativas del sistema.
  • Si un cliente llama muchas veces al mismo servicio, el desempeño que obtiene es pobre porque cada vez el Servicio Web debe validar contra el Identity Store.
  • Las credenciales de un cliente deben estar replicadas en todos los Identity Store, lo que lleva a tener actividades de sincronización y actualización.

 

Algunas consideraciones de seguridad que se deben observar al momento de implementar autentificación directa son:

 

  • Es posible que ataques al canal entre el cliente y el Servidor Web puedan obtener las credenciales del cliente y atacar el servidor usando esas credenciales.
  • El Identity Store puede ser foco de un ataque, con el objetivo de obtener las credenciales, por ejemplos password guardados en texto plano.
  • El cliente puede implementar un Cache de Login y Password para entregarlos en cada requerimiento. Esto es un foco de riesgo de seguridad.

 

Autentificación indirecta

 

………………continuará……