Mostrando entradas con la etiqueta fitnesse. Mostrar todas las entradas
Mostrando entradas con la etiqueta fitnesse. Mostrar todas las entradas

miércoles, 15 de abril de 2015

Arquitectura de la prueba automatizada



La automatización de la prueba de software es un tipo particular de sistema de software, a veces llamado framework de prueba o stack de prueba.
Como cualquier sistema de software, tiene una arquitectura que puede facilitar o no la extensión, reuso y modificación. 
Arquitectura del Sistema de pruebas automáticas

Una arquitectura compartida

Algunas familias de herramientas de prueba (como Mercury, Rational, Silk y TestComplete) están pensadas para resolver toda la problemática de la prueba y tienen la arquitectura definida por el fabricante.
En otros casos (como Selenium, FitNesse, Cucumber y RobotFramework) las herramientas resuelven sólo algunos de los aspectos de la prueba. Suelen usarse combinadas con otras herramientas.
Una discusión que dejo pendiente es la correlación entre los atributos de las arquitecturas (extensión, reuso y modificación) y el origen de las herramientas: propietarias o FLOSS.

Planteo entonces por mi cuenta una arquitectura que describe muchos de los stack de prueba que conozco. 
Esta arquitectura está en movimiento aún. Hace un año aproximadamente incorporé el componente Comparator por conversaciones con Nicolás Páez (él plantea un modelo ligeramente distinto parte 1/parte 2). En enero de este año, cuando estaba explicando la arquitectura, me surgió la necesidad de agregar el Editor.
Y aún tengo dudas sobre la utilidad de separar algunos componentes, como por ejemplo tener componentes separados para reflejar el mecanismo de extender el DSL, tanto del lado del Test Case como del Interpreter.

Para qué buscar una arquitectura compartida

Identificar una arquitectura común de las herramientas de prueba permitiría simplificar las interfaces y que cada producto tenga foco en su especialidad, sabiendo que otros productos se ocuparán de las restantes áreas. Esperaba que el programa de la Agile Alliance "Agile Alliance Functional Testing Tools" lograra este consenso. No encuentro que se haya publicado ese análisis, aunque hay otros temas interesantes (20082012).

Es también útil tener un lenguaje común para poder discutir stacks alternativos cuando se está diseñando una solución de prueba.  

Los componentes

Por cada componente, describo brevemente el alcance del mismo y algunos ejemplos de su instanciación en herramientas existentes.
  • Editor: cómo creo y edito mis casos de prueba. En ocasiones una herramienta ad hoc, en otros casos una herramienta genérica.
    Selenium: Selenium IDE, permite grabar acciones desde la aplicación o editar los TC.
    WebDriver: editor/IDE del lenguaje de programación usado. 

    RobotFramework: RIDE, permite editar TC en forma 'inteligente', o editores de HTML. 
    FIT: Word, Excel y editores de HTML. 
    FitNesse: wiki. 
    Cucumber: cualquier editor de texto.
  • Test Case (TC): Repositorio de mis casos de prueba, con o sin estructura y con un lenguaje con el que escribo mis casos de prueba. Puede incorporar la posibilidad de definir términos nuevos que abstraigan complejidad.
    Selenium IDE: Los casos de prueba se escriben con Selenese, básicamente un HTML con una tabla de tres columnas. La estructura de casos está dada por las suite, que es una tabla HTML con los casos.
    WebDriver: No tiene forma nativa de representar los casos de prueba o la estructura. Debe usarse un Runner de otra herramienta y sintaxis de lenguaje de programación usado. Extensión usando el lenguaje de programación.
    RobotFramework: HTML (tablas), TSV, reST. Permiten extender el DSL de prueba con user keywords.

    FitNesse: wiki markup languaje (tablas). Permiten extender el DSL de prueba con Scenario.
    Cucumber: Gherkin y permiten extender el DSL de prueba con Scenario Outlines.
  • Interpreter: interprete del lenguaje de TC, incluyendo mecanismos para definir o extender un DSL.
    Selenium: plugin de Firefox, y se extiende con plugins adicionales.
    WebDriver: la parte correspondiente a WebDriver es interpretada o compilada por el lenguaje en el que se escribieron los TC (Java, Ruby, Python, etc), y resuelto con la API provista por la biblioteca de Selenium.
    RobotFramework: interprete del lenguaje propio (test data syntax), se extiende con  
    library keywords.
    FitNesse: interprete de HTML (modo compatibilidad con FIT) a través de una conversión interna entre wiki text y HTML, o directamente wiki text en el caso de SLIM. Extensión usando Fixture.
    Cucumber: interprete de Gherkin más los steps creados.
  • Adapter: cero, uno o más capas de adaptación entre los TC interpretados y el sistema bajo prueba (System Under Test o SUT). La forma de conexión depende del tipo de prueba que se quiere hacer, por ejemplo conexión con: interfaz usuario, API REST, u objetos de la aplicación.
    SUT Web: un ejemplo sería WebDriver usado para automatizar la aplicación a través de la interfaz web, más PhantonJs para simular un browser.
    SUT Desktop: un ejempo es Abbot para pruebas de Java Swing o White para pruebas GUI sobre Windows usando UIAutomation.
  • Comparator: el mecanismo de comparación entre resultados esperados y los resultados reales. El comparador puede tener conocimiento semántico y sintáctico sobre el resultado (Output) del SUT, para poder hacer comparaciones más significativas y concisas. En este sentido puede estar ligado a los adapters. En otras casos está implementado en el Interpreter. Y también puede ser extendido.
    Cucumber: la base de los comparadores corresponde a las expectations de RSpec, pero se enriquecer, por ejemplo con los Capybara Matchers.
    Fitnesse: una de las diferencias entre el Interprete FIT y SLIM es los comparadores adicionales (rangos, aproximados, mayor o menor). Adicionalmente, en SLIM se pueden enriquecer con Custom Comparators.
  • Runner: permite correr los TC. Pueden permitir ejecutar TC secuencialmente o en paralelo; todos los TC, o algún subconjunto (los que tienen cierta marca o tag, los que han fallado, etc), o uno solo TC; indicar dependencias entre TC.
    Los Runner también tiene mecanismos para indicar cuando un TC ejecutado ha sido exitoso o no y  mecanismos de preparación y limpieza de las pruebas. Invocan a la generación de reportes.
    Cucumber: se invoca por linea de comando y puede usarse el runner de JUnit (con cucumber-JVM)
    FitNesse: se puede ejecutar las pruebas desde la wiki, por linea de comandos, por servicios REST y puede usarse el runner de JUnit.
  • Reporter: crea los reportes de seguimiento de los resultados de la prueba. Distintos usuarios del sistema de pruebas requieren distintos reportes: resumen al momento, evolución temporal, detalle de casos ejecutados, etc. Es este área el formato XML de JUnit es el standard de facto. Aunque varias herramientas tienen alternativas: sólo texto o HMTL. Además, se incorporan a otras herramientas para brindar visibilidad, como Jenkins o SonarQube.

Conclusiones

Los equipos de desarrollo de software pueden diseñar su sistema de pruebas automáticas en forma iterativa e incremental, de igual manera a como se desarrolla el producto. Se empieza por la forma más sencilla y se mejora cuando es necesario.

Espero que este modelo te ayude a evaluar alternativas y decidir que cambiar en cada ciclo de mejora.

lunes, 12 de enero de 2015

Fitnesse con Python (waferslim)

En estos días tuve la necesidad, por primera vez, de usar Fitnesse para probar un sistema escrito con Python.
Gracias a SLiM es fácil, comparado con la implementación con FIT, relacionar Fitnesse con varios lenguajes.
Me llevó, de todas formas, más de un par de horas hacerlo funcionar. Espero que a otros les sirva.
Nota: Mis pruebas fueron con Ubuntu 14.04 y 11.04

Los pasos para ponerlo en marcha son:
  1. Instalar WaferSLiM, el servidor SLiM para Python.
  2. Instalar Fitnesse
  3. Configurar las pruebas para usar WaferSLiM y la versión correcta de protocolo

WaferSLiM

Obtener waferslim desde https://pypi.python.org/pypi/waferslim
wget https://pypi.python.org/packages/source/w/waferslim/waferslim-1.0.2-py2.6.zip#md5=acacf783444802677358b8b301ab23f9  --no-check-certificate
unzip waferslim-1.0.2-py2.6.zip

Instalar waferslim por https://bugs.launchpad.net/ubuntu/+source/distribute/+bug/958550

sudo easy_install ez_setup
cd waferslim-1.0.2
sudo python setup.py install

Para probar que todo anda bien (reemplazar el syspath donde lo hayas instalado)

python -m waferslim.server --syspath /home/kleer/waferslim-1.0.2 8002

Fitnesse

Obtener Fitnesse de http://www.fitnesse.org/FitNesseDownload el release 20140901
fitnesse-standalone.jar (*)

Para probar, ejecutar
java -jar fitnesse.jar -p 8000

Abrir en un browser localhost:8000

Configuración

Crear una página de Test y poner este texto, reemplazando el path por el correcto en tu caso (*)

!define TEST_SYSTEM {slim}
!define SLIM_VERSION {0.1}
!path /home/kleer/waferslim-1.0.2
!define COMMAND_PATTERN {python -m waferslim.server -v --syspath %p }

Luego poner el test, tomado del docstring de algunos de los ejemplos que se encuentran en la distribución de WaferSlim. Por ejemplo

|import|
|waferslim.examples.decision_table|

|should I buy milk|
|cash in wallet|credit card|pints of milk remaining|go to store?|
|      0       |    no     |      0                |    no      |
|      10      |    no     |      0                |    yes     |
|      0       |    yes    |      0                |    yes     |
|      10      |    yes    |      0                |    yes     |
|      0       |    no     |      1                |    no      |
|      10      |    no     |      1                |    no      |
|      0       |    yes    |      1                |    no      |
|      10      |    yes    |      1                |    nope    |

|should I buy milk alternative implementation|
|cash in wallet|credit card|pints of milk remaining|go to store?|
|      0       |    no     |      0                |    no      |
|      10      |    no     |      0                |    yes     |
|      0       |    yes    |      0                |    yes     |
|      10      |    yes    |      0                |    yes     |
|      0       |    no     |      1                |    no      |
|      10      |    no     |      1                |    no      |
|      0       |    yes    |      1                |    no      |
|      10      |    yes    |      1                |    nope    |


Apretar el botón de test y ¡listo!

(*) Versión del protocolo

La versión de WaferSlim que se encuentra en pypi fue probada con el fitnesse.jar release 20100103.
Si se usa esa versión de Fitnesse, no es necesario configurar SLIM_VERSION {0.1}

jueves, 7 de noviembre de 2013

¿Automatizamos las pruebas de regresión?

Trabajo en una aplicación en la que cada vez que mis usuarios piden un cambio, el costo de hacer el cambio es mucho menor que el costo de probar. La aplicación no tiene automatizada la prueba y no es modular.

Esta situación la he escuchado y vivido muchas veces en mi carrera. Ante esa situación, hay varias acciones posibles:

  1. Tenemos que hacer de nuevo el sistema, y esta vez lo haremos bien.
  2. Redefinamos el sistema para que sea más fácil probar.
  3. Automaticemos la prueba.
  4. Contratemos más testers.
  5. Sigamos sin cambiar durante algún tiempo.

No puedo en este post analizar todas las alternativas, pero voy a seguir una linea de razonamiento posible, asumiendo algunas creencias, básicamente que las prácticas de desarrollo ágil me pueden ayudar en este caso (Más información sobre estrategias en Working Effectively with Legacy Code). 

Entonces, la secuencia de pensamientos sería:
  • Me gustaría hacerlo bien, pero no puedo tirarlo y empezar de nuevo, el negocio debe seguir operando y la aplicación se debe seguir adaptando. Entonces, trabajaremos en hacerlo bien, pero en forma incremental
  • Para redefinir el sistema sin romperlo, y dado que trabajaremos incrementalmente (muchos pequeños cambios) necesitamos pruebas automatizadas, al menos de nivel funcional. Esto permitirá que probemos a bajo costo cada versión del sistema con un nivel aceptable de calidad. No reemplazamos las pruebas manuales, pero al menos distribuimos las pruebas entre dos ciclos: uno automatizado, rápido y frecuente, otro manual, costoso y que corremos antes de liberar versiones.
  • Hacer bien la aplicación implica modularizar, tener pruebas automatizadas unitarias y calidad interna. Estas importantes ideas y prácticas no las estoy tratando en este post, pero suelen tener como precondición las pruebas funcionales automatizadas a las que me refierno en este post.
Vemos muchas equipos y empresas que siguen esta linea de pensamiento. Entonces el problema se puede reeplantear.

Quiero automatizar las pruebas de regresión, ¿cómo lo hago?

Quiero automatizar las pruebas de regresión, ¿cómo lo hago? ¿Qué herramientas uso? ¿lo hago con personas del equipo o contrato fuera? ¿cómo cambian los roles, que tenemos que aprender? ¿qué y cuánto tengo que probar para lograr confianza?

La respuesta a estas preguntas: Depende. 
Bien, pero ¿de qué depende?

El equipo ¿tiene cultura de calidad?

Algunos miembros del equipo se ocupan de la calidad, dedican tiempo a pensar como probar y manejar los riesgos. Ya se hacen pruebas, y el problema es como automatizarlas.

¿Cómo es la estructura y cuales son los conocimiento del equipo?

La prueba manual la realiza un grupo externo, o los testers del equipo (hay personas cuyo puesto o posición es Tester), o las pruebas las realizan varias personas que cubren el rol de tester, pero también otros roles (analistas, programadores, diseñadores gráficos).
Los que cumplen rol de tester, ¿tienen conocimientos sobre programación o les interesa adquirirlos?
Funciona el equipo como un Whole Team en el que todos toman responsabilidad sobre el resultado conjunto y no sobre un subconjunto ("yo pruebo, no programo" o viceversa)

¿Cómo es la tecnológicamente la aplicación?

Para disminuir la curva de aprendizaje y mejorar la aceptación de las nuevas herramientas, es conveniente que la tecnología de la prueba automática sea cercana a la utilizada para la aplicación. ¿El equipo trabaja con .Net, Java, Ruby, Python?

Nuestra respuesta

Cada equipo debe explorar qué organización, herramientas y conocimientos les servirán para su caso.
Lo que encontramos desde Kleer (en este caso Nicolás Páez, Juan Gabardini, Carlos Peix), es que solemos repetir, al inicio de las consultoría de estos temas, una mínima nivelación de conocimientos y del abanico de posibilidades en cuanto a prácticas y herramientas, para facilitar a los equipos tomar decisiones. Pensamos que sería útil extraer esto en una serie de talleres cortos (medio día cada uno):


martes, 23 de octubre de 2012

Ágiles 2012: Requerimientos por medio de ejemplos con Fitnesse

Mañana empieza Ágiles 2012 en Córdoba.
Voy a facilitar una sesión sobre el uso de Especificación por medio de Ejemplos en el contexto de software existente.
Usaremos Fitnesse como herramienta para los ejemplos (SLIM). 
Pueden ver una video de presentación del tema y demo que realicé sobre la herramienta (son 45 min, a partir del min 16 está la demo de Fitnesse). 

Cree un evento Google+, para que compartamos las experiencias.

Instalación Fitnesse
Los asistentes trabajarán con Fitnesse, y sería conveniente que lo tengan instalado en sus equipos, para agilizar el desarrollo del taller. Alternativamente, puedo ofrecer máquinas virtuales (virtualbox) con Fitnesse instalado (requiere JDK).
Para validar si la instalación está bien, puende correr en una terminal
  java -jar fitnesse.jar -p 8080

Y luego probar con un browser accediendo a http://localhost:8080

lunes, 26 de septiembre de 2011

Pruebas con fechas


En los últimos días me encontré dos veces conversando sobre pruebas que incluyen fechas: en el curso sobre Fitnesse, con pruebas funcionales, y con mis compañeros en UTI SIS con TDD.
Ambas conversaciones me hicieron repensar este tema, y aprovecho para volcar mi visión actual.

¿Por qué es especial la prueba que involucra a fechas?

Hay otras razones, pero en este post me centraré en la dependencia con la fecha del sistema. Esta dependencia puede darse en dos situaciones:
  • crear la entidad o realizar una acción sobre la misma (emitir un documento legal, como un cheque o una factura), que asigna la fecha del sistema a algún campo de una entidad u objeto de la aplicación.
  • consultar a la entidad u objeto, siendo el resultado de la consulta dependiente de la fecha (posiblemente la fecha actual).
Ejemplos:
  • Un cheque se emite al día de hoy (fecha de emisión), y vence en 30 días.
  • Quiero consultar si puedo depositar el cheque (no debe estar vencido).
Si hacemos pruebas que tengan dependencias sobre un elemento que no podemos controlar (en este caso el paso del tiempo), nuestras pruebas van a ser poco robustas (como pruebas unitarias).
Si quiero probar el sistema completo, me puede interesar explicitar el uso de las fechas de diferentes servidores. Estaría haciendo prueba de integración en vez de prueba unitaria.

¿Que alternativas tengo?

  1. Usar los datos de la prueba de manera que el estado o condición a probar sea válida al día de hoy.
    • En el caso del cheque, es fácil probar si el cheque puede depositarse (no está vencido), ya que si creamos un cheque hoy, seguro está dentro del período.
    • Probar el caso de cheque vencido es más complejo: quizás puedo modificar la fecha en la base de datos.
    • Se puede tener una generación automática de datos de prueba de manera que se cumplan las condiciones deseadas.
  1. Disponer de una manera de modificar la fecha que toma el sistema como fecha actual.
    • Se puede tener los datos con fechas fijas y lo que se cambia es la fecha simulada del sistema. Se requiere cambiar al sistema, para probarlo.
  1. Ampliar la funcionalidad de manera de recibir la fecha de referencia (en vez de tomarla del sistema)
    • Es conflictivo si el cliente no siente esto como necesario, parece ser complejidad adicional, sólo algo para facilitar las pruebas.
    • Generalmente sirve para el caso de las consultas, no de la generación.
  1. Realizar pruebas relativas.
    • No usar valores absolutos de fechas u horas. Esto tiene el inconveniente que (similar al caso 1), que no se puede acelerar el paso del tiempo: por ejemplo, si mi datos tiene precisión de días, y quiero probar el próximo día.

Ejemplos

Alternativa 1

TDD

El siguiente ejemplo, en PHP, muestra como se preparan los datos para las pruebas relacionadas con cursos, en la que hay 4 fechas (inicio y fin de la inscripción, inicio y fin del curso). Los primeros 4 números 'datos' son días relativos al día de hoy.
La función __creaCurso traduce entre valores relativos de las fechas y los valores absolutos con que se quiere probar.

function testCursosActuales() {
$pruebas = array(
array('datos' => array( 1, 1, 3, 3,false,false), 'actual' => False),
array('datos' => array( 1, 1, 3, 3,true,false), 'actual' => True),
array('datos' => array( 0, 0, 2, 2,true,false), 'actual' => True),
array('datos' => array(-1,-1, 1, 1,true,false), 'actual' => True),
array('datos' => array(-1,-1, 1, 1,true,true), 'actual' => True),
array('datos' => array(-2,-2, 0, 0,true,true), 'actual' => True),
array('datos' => array(-3,-3,-1,-1,true,true), 'actual' => False),
array('datos' => array(-5,-3,-1,-1,false,true), 'actual' => False)
);
foreach($pruebas as $prueba) {
$id = $this->__creaCurso($prueba['datos']);
$this->assertEqual($prueba['actual'], $resultado);
}

Fitnesse

Dentro de una página se puede usar !today, que se reemplaza por la fecha / hora actual. También se pueden poner fechas relativas (!today -1).

Alternativa 2

En este caso, la forma de cubrir esto es la Inversión de Control / Inyección de Dependencias. Se debe utilizar el servicio de obtención de día y hora actual a traves de una capa de abstración, de manera de poder reemplazarlo por una implementación controlada por las pruebas. Un ejemplo de esto se muestra en el capítulo 15 del libro FIT for Developing Software.
El SUT debe estar preparado (o debemos poder modificarlo para) para recibir la referencia al servicio.

Alternativa 3

En muchos casos el usuario pide funcionalidad que depende de la fecha actual, pero esa funcionalidad podría generalizarse para una fecha abitraria, con valor de negocio. Por ejemplo, cheque a depositar. Tiene sentido con fecha de hoy, pero si puedo poner fechas futuras, ayuda a la planificación.
Se puede evitar el aumento de la complejidad para el caso más usado con valores por defecto apropiados.

Alternativa 4

Puedo probar que un evento esté en intervalos temporales. Por ejemplo si quiero probar que un registro de auditoría es correcto, puedo almacenar el horario previo y posterior a la operación, y verificar que el registro esté dentro de ese intervalo.