Víctor Madrid, especialista en QA y Testing de la Oficina Técnica de atSistemas presentó su ponencia y demo sobre "Cobertura de código con test funcionales para superhéroes de hoy en día" durante el evento ExpoQA16, celebrado en Madrid.
16. Pruebas Unitarias
• Prueba la funcionalidad del código
• Realizado por los desarrolladores
• No pueden acceder a los recursos externos (no
acceden a la red, ni BD, etc.)
• Asociados al perfil de desarrollo “development”
• Automatización
• Muy mala práctica no realizarlos
• Enfoque “caja blanca”
• Pieza clave en la metodología TDD
17. Pruebas de Integración
• Pruebas relacionadas con el acceso a datos o con el
uso de otros componentes -> Relación con otras partes
• Realizado por los desarrolladores
• Asociados al perfil de desarrollo “integration”
• Automatización
• Muy mala práctica no realizarlos
• Enfoque “caja blanca”
• Requieren configuración
• Por ejemplo : Spring
• Técnicas :
Top down
Bottom up
Big bang
etc.
18. Pruebas Funcionales
• Pruebas relacionadas con el funcionamiento del
sistema, comprueba que se cumplan las funciones
específicas para las cuales han sido desarrollado
• Realizado por los analistas funcionales y clientes
• Enfoque “caja negra”
• Automatización
• El nivel de conocimiento de negocio del tester tiene
que ser similar al del cliente
27. Contexto
• Nuestro jefe : Bruce Wayne / Batman
• NO se puede desvelar ninguna identidad
• Existe una empresa : WAYNE ENTERPRISE
– Filial : WAYNE Consulting
• Cualquier desarrollo lo tiene que implementar en su mayoría un
proveedor externo
– No hay tiempo/dinero/recursos para realizar un desarrollo propio entero, pero sí
para realizar adaptaciones del código
– Ocultar al máximo el objetivo final : Por ejemplo solicitar un CRUD de
Funcionarios
28. Organización
• Batman tiene una organización global : Batman Inc.
• Sistema de Franquicias
• Problema : Cada país tiene su propias leyes
• Proyectos / librerías de arquitectura de Batman :
– bat-architecture-testing
– bat-architecture-common
29. Somos…Batman Madrid
• Nos regimos por las leyes Europeas y de España
• Regla básica : “NO matamos pero SÍ se detiene”
• Problema : En España NO dominamos mucho el inglés
30. Necesidad
• Batman Madrid necesita una nueva aplicación web
para la gestión de villanos, gestión de recursos ,
imputación de horas, etc.
– bat-desk
31. “Requerimientos”
• NO puede existir documentación o referencias a
metodologías de desarrollo / funcionalidad / testing / QA
• Utilización de una Arquitectura Open Source basada en
Java con tecnologías actuales
• Uso de buenas prácticas de desarrollo
• A realizar en : “2 semanas como máximo”
– Porque seguro que son 2 “if-then-else” y 4 pantallitas
32. Tecnologías Desarrollo
• Arquitectura basada en tecnología : Java
• Herramienta de construcción automática : Maven
• Trazas de la aplicación : Logback
• Framework de Spring y Spring MVC
• Spring Boot
• Persistencia de datos : Mybatis
• Servidores Embebidos (servidor ligero embebido) : Tomcat
• Bases de datos Embebidas (BD que se crea y se utiliza en memoria) : H2
33. Tecnologías Testing
• Frameworks de test unitarios y de integración : JUnit
• Mocking (creación de objetos simulados) : Mockito
• Framework de matchers (facilita comprobaciones) : Hamcrest
• Frameworks de test funcionales : Selenium
• Navegador (navegador sin interfaz grafica basado en Webkit): Phantom.js
• WebDriver para PhantomJS, : Ghost Driver
• Wrapper de Selenium : Fluentlenium
• Cobertura del código : Porcentaje de código accedido por test : Cobertura Cobertura
34. Ayuda Extra
• Somos muy amigos de “Batman Londres” y nos ha
facilitado el módulo de villanos que casi tiene
implementado porque es muy parecido a lo que
nosotros necesitamos
– bat-villain-core
Opciones :
Caso 1 : Uso Librería de Terceros
Caso 2 : Integración total en el desarrollo
Caso 3 : Módulo externo en el desarrollo
36. Estados :
• Libre (FREE)
• Detenido(DETAINED)
• Muerto(DEAD)
Servicio de “Detención”
@Service("villainOperationService")
public class VillainOperationServiceImpl implements VillainOperationService {
…
@Override
public void detain(long villainId) throws BatDeskException {
LOG.trace("Detaining Villain with id : {}", villainId);
final Villain villain = villainMapper.findByPK(villainId);
generateNotFoundException(villain);
if (VillainValidation.INSTANCE.isDead(villain)){
throw new BatDeskException(VillainTypeExceptionEnum.IS_DEAD.name());
}
if (VillainValidation.INSTANCE.isDetained(villain)){
throw new BatDeskException(VillainTypeExceptionEnum.IS_DETAINED.name());
}
villain.setStatus(VillainStatusEnum.DETAINED);
if (villainMapper.update(villain) == 0) {
throw new BatDeskException(VillainTypeExceptionEnum.DB_ERROR.name());
}
LOG.trace("Villain detained with id : {}", villainId);
}
…
}
Importante :
Este servicio se encuentra en el módulo : bat-villain-
core
37. Test : Servicio de “Detención”
public class VillainOperationServiceTest {
private VillainOperationServiceImpl villainOperationService;
private VillainMapper villainMapper;
private Villain villainTest;
@Before
public void initTests() {
...
}
@Test (expected=BatDeskException.class)
public void shouldDetainNotFoundException() throws BatDeskException {
when(villainMapper.findByPK(anyLong())).thenReturn(null);
villainOperationService.detain(VillainConstant.TEST_ID);
}
@Test (expected=BatDeskException.class)
public void shouldDetainWithDeadStatusException() throws BatDeskException {
villainTest.setStatus(VillainStatusEnum.DEAD);
villainOperationService.detain(VillainConstant.TEST_ID);
}
…
@Test
public void shouldDetain() throws BatDeskException {
villainTest.setStatus(VillainStatusEnum.FREE);
when(villainMapper.update(villainTest)).thenReturn(1);
villainOperationService.detain(VillainConstant.TEST_ID);
assertEquals(VillainStatusEnum.DETAINED,villainTest.getStatus());
}
}
38. Controlador de “Detención”
@Controller("villainController")
@RequestMapping(value = "/villain/*")
public class VillainController {
...
@RequestMapping(value = "/detain", method = RequestMethod.GET)
public ModelAndView detain(@RequestParam("id") Long villainId, HttpServletRequest request, final RedirectAttributes redirectAttributes) {
LOG.info("Detaining Villain with id : {}", villainId);
ModelAndView modelAndView = new ModelAndView(ViewConstant.REDIRECT_LIST_REQUEST_MAPPING);
Villain villainFound = villainService.findByPK(villainId);
if (!VillainValidation.INSTANCE.isValid(villainFound)) {
final String error = messageSource.getMessage("villain.validation.error.NOT_FOUND", new Object[] { villainId }, RequestContextUtils.getLocale(request) );
modelAndView.addObject(ParameterConstant.PARAM_EXCEPTION, error);
modelAndView.setViewName(ViewConstant.ERROR_VIEW);
} else {
try {
villainOperationService.detain(villainId);
final String success = messageSource.getMessage("villain.validation.IS_DETAINED", new Object[] { villainId }, RequestContextUtils.getLocale(request) );
redirectAttributes.addFlashAttribute(ParameterConstant.PARAM_MESSAGE, success);
LOG.info("Villain with id {} is detained", villainId);
} catch (BatDeskException e) {
LOG.error("BatDeskException : Villain with id {} not detained by '{}'", villainId, e.getMessage());
final String error = messageSource.getMessage("villain.validation.error."+e.getMessage(), new Object[] { villainId }, RequestContextUtils.getLocale(request) );
redirectAttributes.addFlashAttribute(ParameterConstant.PARAM_EXCEPTION, error );
}
}
return modelAndView;
}
...
}
Importante :
Este servicio se encuentra en el módulo : bat-desk-
web
39. Test : Controlador de “Detención”
public class VillainControllerTest {
private final String MESSAGE_SOURCE_VALUE = "TEST";
private VillainController villainController;
private VillainService villainService;
private VillainOperationService villainOperationService;
private RedirectAttributes redirectAttributes;
...
@Before
public final void setUp() throws Exception {
…
villainController = spy(new VillainController());
villainService = mock(VillainService.class);
villainOperationService = mock(VillainOperationService.class);
…
when(messageSource.getMessage(anyString(), any(Object[].class), anyObject())).thenReturn(MESSAGE_SOURCE_VALUE);
when(villainService.findByPK(anyLong())).thenReturn(villainTest);
}
@Test
public final void shouldBeDetainNoFound() {
when(villainService.findByPK(anyLong())).thenReturn(null);
final ModelAndView modelAndView = villainController.detain(VillainConstant.TEST_ID, request, redirectAttributes);
final String message = (String) modelAndView.getModel().get(ParameterConstant.PARAM_EXCEPTION);
assertEquals(ViewConstant.ERROR_VIEW,modelAndView.getViewName());
assertEquals(MESSAGE_SOURCE_VALUE,message);
}
…
@Test
public final void shouldBeDetain() throws BatDeskException {
final ModelAndView modelAndView = villainController.detain(VillainConstant.TEST_ID, request, redirectAttributes);
assertEquals(ViewConstant.REDIRECT_LIST_REQUEST_MAPPING,modelAndView.getViewName());
}
}
40. Test Funcional de “Detención”
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootWebApplication.class)
@WebAppConfiguration
@IntegrationTest("server.port:8080")
public class DetainSeleniumIT extends FluentTest {
private final String NAVIGATION_VILLAIN_LIST = "navigation_villain_list";
@Value("${local.server.port}")
private int serverPort;
private WebDriver webDriver = new PhantomJSDriver();
@Test
public void testBatDeskPage_Dead() {
goTo(getUrl());
// Villain List
find(By.id(NAVIGATION_VILLAIN_LIST)).click();
// Detain Dead Villain
find(By.id("detain_4")).click();
find(By.id("notificationException"));
}
…
@Test
public void testBatDeskPage_Free() {
goTo(getUrl());
// Villain List
find(By.id(NAVIGATION_VILLAIN_LIST)).click();
// Detain Free Villain
find(By.id("detain_1")).click();
find(By.id("notificationMessage"));
}
}
Quería empezar dando las gracias en primer lugar a la organización por permitir realizar un año mas este evento
Gracias a mi empresa por permitirme el poder estar aquí con vosotros danto esta charla
Me llamo Víctor y pertenezco a Oficina Técnica de AtSistemas en Barcelona… pero soy de Madrid
Lo normal en este punto es contar mi vida laboral, he estado en tal sitio y que cosas he hecho… pero creo que es mas interesante que conozcáis a la persona
Soy un tipo al que le gusta bastante viajar : siempre que puedo y la economía me lo permite salgo fuera
Me encanta comer : Solo no me gustan los caracoles, me pirra el sushi
Para compensar esto dire que intento ir al gimnasio aunque no tengo
Deboro series : Tengo predilección por las series de superherores robots , ninjas y explosiones
, soy un friki del baile, vme encanta la series
Me encantan los superhéroes, nunca digo que no a tomar se una cañas
Voy al gimnasio (de hecho hay semanas que hago tantas cosas que no me da tiempo a ir y es 24 horas)
Jugar a la PS4 (Juegos de ordenador donde puedo ganar donde no me gana un niño de 7 años)
¿Alguno comparte alguna de las aficiones? ¿últimamente se que el tema del baile esta pegando fuerte en el mudno de la informatica?
Las sesiones y las suelo preparar como me gustaría que me las contaran a mi
[ZONA DE CONFORT]
Bueno…aquí os presento MI OFICINA …es broma
¿A cuantos de vosotros os gustaría estar aquí?
Esta sería una PERFECTA zona de confor, donde aparentemente parece que no va a pasar nada (Por cierto el lugar se corresponde con las Maldivas para el que lo quiera saber)
¿Qué es una zona de conforT?
Este concepto es aplicable a cualquier aspecto de la vida de cada uno :
Aterrizandolo al sector de la informática se puede decir que la zona de confort va desde la empresa, el puesto de trabajo, el proyecto, la clase o las 3 líneas de código que te toca modificar
A todos nos gusta NO tener demasiadas complicaciones…o por lo menos NO ser el autor de la gran mayoría…jejeje
[ZONA DE TRABAJO]
¿Alguno reconoce esta ciudad?¿es europea?¿es americana?.....es Gotham
[PREGUNTA]
Todos los desarrolladores…al menos una vez en la vida… nos hemos hecho esta pregunta
Seguro que alguna vez os ha tocado mantener algún código heredado al que os gustaría tener delante al programador que lo ha realizado para decirle 4 cositas
Sabéis que es lo peor de todo esto…os hacéis una idea de lo que puede ser
Pues lo peor es descubrir que lo habéis hecho vosotros
[Golpe de Realidad]
Años de malas practicas, de no querer cambiar las cosas, de no quererse complicar la vida, etc es lo que ha llevado a que la calidad de mucho del código que hay actualmente no sea el adecuado
Y no os digo nada si esto lo dividimos por sector, seguro que mas de uno en la sala sería capaz de contarme alguna anectoda con alguna aplicación de cualquiera de lso sectores
[Teoría de la ventana rota]
¿Habéis oído hablar alguna vez de la teoría de las ventanas rotas?
Es una teoría que se basa en el contagio de las conductas incívicas (1969)
Estudio :
CASO 1
Abandonar un coche en las calles del Bronx de Nueva York, sin placas de matricula y con las puertas abiertas
Alos 10 minutos se empezaron a robar su componentes , a los 3 días no quedaba nada de valor y luego se empezaron a destroza
CASO2
Abandonar un coche en el barrio rico de Palo Alto (California) -> No paso nada
Al ver que no había respuesta , destrozo un poco algunas partes del coche a las pocas horas el coche estaba como el del Bronx
Se denomina de las ventanas porque suele ser el reflejo de lo que pasa con los edificios abandonados
Si en un edificio aparece una ventana rota y no se arregla pronto, inmediatamente el resto de ventanas acaban siendo rotas
Este mismo punto aplica de forma directa al código
¿Cuántas ventanas rompes y cuantas ventanas arreglas?
[Problema del Billón de Dólares]
¿Reconoceis a la persona de la izquierda? ¿os suena?
Una pista … al menos una vez en la vida habéis implementado un algoritmo que creado por él.
Se trata de
- Premio Turing 1980
Es el creador de algoritmo de ordenación : Quicksort (1960)
Para el considerado su primer y más afortunado logro ¿Qué mejor comienzo para una carrera academica, teniendo en cuenta que nunca se doctoro?
Habla de su vida en que también tuvo equivocaciones
[1º]
Fue desarrollador de un SO operativo que nunca salio a la luz por un problema de rendimiento con la paginación de memoria
[2º] él lo llama “mi error de 1000 millones de dolares”
¿Os haceis una idea de lo que puede ser? Pues es uno de los creadores de la referencia nula -> puntero a null
Esta bien para mostrar la ausencia de información -> pero es peligroso
Causante de millones de errores de programación, que llevan a ejecución de resultados impredecibles , errores difíciles de detectar
Este error dice que lleva 50 años en activo, por lo tanto un total de mil millones de dólares la realidad lo denomina el error del billón de dolares
[Mala Cultura]
El testing se considera aburrido
Tareas repetitivas
No existen grandes desafíos técnicos
Se “mal considera” un trabajo para el mal programador
Depende de la inspiración
En muchos casos se realiza en “horas extras”
Datos incoherentes -> No se puede hacer la prueba
Problemática a la hora de probar : “Date una vuelta por la aplicación”
En el mejor de los casos es guiada
El testing se considera poco necesario
En muchos casos NO se hace o bien se “miente”
Requiere invertir tiempo
Requiere invertir dinero (aprendizaje, etc)
No existe cultura en las empresas
No se incluye en las planificaciones
Si se puede hacer se hace al final del proyecto rápido y mal
[Desarrollo vs Testing]
Desarrollador :
Padre / Madre de la criatura (aplicación)
No suele ver los problemas de la aplicación
No suele hacer cosas que la molesten
Tester:
Amigo / Amiga de la criatura (aplicación)
Suele ver los problemas de la aplicación
Suele hacer cosas que la molesten (chinchar)
[Automatización]
Algunas de las tareas que engloba son : Compilación, Empaquetado ,Control de dependencias ,Testing, Versionado, Generación de documentación, Gestión de incidencias ,Etc
Automatización Ejecución de test (Test execution automation)
Conseguir que los casos de prueba sean ejecutados por una máquina (sin intervención humana)
Objetivos :
Detección temprana de errores
Reducir el time to market
Incrementar la calidad del SW
Motivación de equipo
Reducción de costes
Ejecución desatendida
Incremento de la motivación del equipo
Repetivle
Para ello se han uso de herramientas o frameworks : Junit, TestNG, Selenium , etc.
Requiere aprender a utilizar las herramientas
Uso de diferentes técnicas : Record & Playback, Data-Driven Testing
[Cobertura]
Ventajas:
Mejora la calidad del código
Evitan tener que hacer pruebas manuales
Facilitan el encontrar errores de forma sencilla
Proporciona seguridad a la hora de hacer cambios críticos
No discutas con los idiotas. Bajarás a su nivel y allí te aplastarán con su experiencia
Imposible probar un SW completamente
Probar todas las posibles entradas y observar todas las posibles salidas
Estos números por lo general son muy grandes
Probarlo todo o probarlo poco tiene unos costes muy elevados -> Equilibrio
Supone un ejercicio basado en el riesgo
No probar todas las entradas implica un riesgo
El riesgo se traduce en : perder dinero, etc
Este aspecto genera mucha presión para un tester
No se puede mostrar la ausencia de errores
Más bugs que encuentres, más bugs que hay
Se dice que los bugs se encuentran en grupo : malos días para el desarrollador, mismo error, etc
Paradoja pesticida : Cuando mas se prueba un SW mas inmune se vuelve a las casos de prueba
No todos los errores encontrados se corrigen
Falta de tiempo, No es un error, demasiado arriesgado, etc.
Es difícil reconocer cuando un bug es de hecho un bug
Sino de descubre el error ¿existe el error?
Parodia Caída Arbol ¿existe ruido?
Las especificaciones nunca son definitivas (el SW siempre se cambia)
No se le da la importancia necesaria a los tester
[Cobertura]
Ventajas:
Mejora la calidad del código
Evitan tener que hacer pruebas manuales
Facilitan el encontrar errores de forma sencilla
Proporciona seguridad a la hora de hacer cambios críticos
No discutas con los idiotas. Bajarás a su nivel y allí te aplastarán con su experiencia
Imposible probar un SW completamente
Probar todas las posibles entradas y observar todas las posibles salidas
Estos números por lo general son muy grandes
Probarlo todo o probarlo poco tiene unos costes muy elevados -> Equilibrio
Supone un ejercicio basado en el riesgo
No probar todas las entradas implica un riesgo
El riesgo se traduce en : perder dinero, etc
Este aspecto genera mucha presión para un tester
No se puede mostrar la ausencia de errores
Más bugs que encuentres, más bugs que hay
Se dice que los bugs se encuentran en grupo : malos días para el desarrollador, mismo error, etc
Paradoja pesticida : Cuando mas se prueba un SW mas inmune se vuelve a las casos de prueba
No todos los errores encontrados se corrigen
Falta de tiempo, No es un error, demasiado arriesgado, etc.
Es difícil reconocer cuando un bug es de hecho un bug
Sino de descubre el error ¿existe el error?
Parodia Caída Arbol ¿existe ruido?
Las especificaciones nunca son definitivas (el SW siempre se cambia)
No se le da la importancia necesaria a los tester
[Cobertura]
[Cobertura]
Porcentaje de código accedido por los test
Características
Probar todas las posibles entradas y observar todas las posibles salidas
Determina el código que esta probado
Permite calcular la complejidad ciclomática de las clases a probar
“A mayor cobertura mayor cantidad de código está siendo probado por nuestras pruebas unitarias”
Se aconseja una cobertura del 85% - 90%
En muchos casos este porcentaje (mínimo) se determina con el cliente, en otros casos es establecido por el equipo o la empresa desarrolladora
Genera “tranquilidad” a los desarrolladores, a la empresa y al cliente
Herramientas : Cobertura, Emma, Clover, etc
Una cobertura alta implica poder realizar cambios “sin miedo” y lo que es mucho mejor… permite dormir por las noches
[Proyecto Envenado]
No quTipico argumento de “esto no funcionará aqui” …Claro en el resto de las empresas si funciona
eda otra que entonces en ese proyecto o empresa existe una singularidad que sólo Stephen Hawkins es capaz de explicar
Error de atribución : Pensar que estar rodeado de “idiotas”
Entonces se piensa que hay que cambiar al resto de la gente
La gente se comporta en base al entorno que le toca vivir
“Yo soy yo y mis circunstancias” de Ortega y Gaset donde es muy difícil cambiar el YO pero mucho mas sencillo cambiar las circunstancias
[PERFIL ELEGIDO]
[MIEDITO]
[EL PERFIL ADECUADO]
Registro actualizado de villanos
Información compartida
Homogeneización en la forma de tratar cada caso
Etc.
¿Qué podemos hacer con Phantom.js?
Sólo podemos manejarlo con un terminal, o la consola MSDOS, usando un API Javascript.
Lanzar test unitarios con Jasmine, QUnit, Mocha, y muchos otros.
Simular acciones del usuario, antes de realizar la captura de pantalla: click, mouseover, keypress, etc...
Podemos manejar el DOM con librerías con jQuery, si lo necesitamos para testear nuestras páginas.
Automatizar la eficiencia de nuestras páginas con YSlow y Jenkins