cobertura de código con test funcionales para superhéroes

Post on 15-Apr-2017

358 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

top related