Cobertura de código con test funcionales para superhéroes
-
Upload
atsistemas -
Category
Software
-
view
358 -
download
0
Transcript of Cobertura de código con test funcionales para superhéroes
Cobertura de código con test funcionales para superhéroes de
hoy en día
Víctor Madrid Triviño
Agradecimientos
¿Quién soy yo?
Índice :• Introducción• Conceptos• Demo• Conclusiones
Introducción
Zona de confort …
Zona de trabajo …
Pregunta :
Golpe de realidad
Conceptos
Teoría de la Ventana Rota
Problema del Billón de Dólares
Mala Cultura• XXX
Desarrollo vs. Testing• XXX
Automatización• XXX
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
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.
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
Cobertura
Proyecto “envenenado”
Equipos multidisciplinares
Resolver el problema …
El elegido …
Miedito …
El perfil adecuado …
Demo
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
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
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
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
“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
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
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 : CoberturaCobertura
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
Estructura de Proyectosbat-architecture-testingbat-architecture-commons
bat-desk
bat-desk-common
bat-desk-web
bat-villain-core
Estados :• Libre (FREE)• Detenido(DETAINED)• Muerto(DEAD)
Servicio de “Detención”@Service("villainOperationService")public class VillainOperationServiceImpl implements VillainOperationService {
…@Overridepublic 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
Test : Servicio de “Detención”public class VillainOperationServiceTest {
private VillainOperationServiceImpl villainOperationService;private VillainMapper villainMapper;private Villain villainTest;
@Beforepublic 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);}
…
@Testpublic void shouldDetain() throws BatDeskException {
villainTest.setStatus(VillainStatusEnum.FREE);when(villainMapper.update(villainTest)).thenReturn(1);
villainOperationService.detain(VillainConstant.TEST_ID);
assertEquals(VillainStatusEnum.DETAINED,villainTest.getStatus());}
}
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
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;...@Beforepublic 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);
}
@Testpublic 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);
}
…
@Testpublic final void shouldBeDetain() throws BatDeskException {
final ModelAndView modelAndView = villainController.detain(VillainConstant.TEST_ID, request, redirectAttributes);
assertEquals(ViewConstant.REDIRECT_LIST_REQUEST_MAPPING,modelAndView.getViewName());}
}
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();
@Testpublic 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"));}
…
@Testpublic 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"));}
}
Despliege Bat-Desk• Arranque con Spring Boot• Despliegue automático en Tomcat Embebido
Reporting “Cobertura”• Informe de cobertura de código integrado en el
“site”• Opcional : Ejecución con diferentes perfiles
Conclusiones
Calidad de vida
Trabajar con principios
Yo soy ….QAtman !!!
Dudas