jueves, 15 de abril de 2010

Excluir clases al obtener la cobertura de tests

Considero que saber en todo momento el porcentaje (cobertura) de nuestro código que está siendo testeado por nuestras pruebas unitarias es algo muy util para saber la calidad con la que ha sido desarrollado.

También me parece que hay que intentar mantener que este porcentaje nunca baje de un límite (por ejemplo 80%).

Si se utiliza Scrum creo que es un elemento que hay que comprobar en las finalizaciones de todos los Sprints y tenerlo muy en cuenta en las retrospectivas para saber si vamos por buen camino o no.

También se puede llegar a indicarle a Maven que si el porcentaje de tests está por debajo de un limite ni siquiera llegue a compilarse el paquete y genere un error.

Recientemente nos hemos encontrado con un problema en el trabajo, y es que nuestra cobertura estaba bajando debido a unas clases que no pueden ser testeadas o en las que no se debería perder tiempo haciendo tests tontos:

- Entidades: ¿Realmente es necesario que todos los getter/setters de un bean se testeen si lo unico que hacen es devolver y settear un valor? ¿Acaso no confiamos en la generación automática de get/set del Eclipse? Yo personalmente no perdería el tiempo testeando estas clases.

- Stubs de Web services: Supongamos que tenemos unos stubs generados a partir de un WSDL. Me niego rotundamente a tener que testear estas clases.

Seguramente que habrá mil casos mas de clases que no es necesario testear y que mucha gente se planteará hacerlo para que la cobertura del proyecto no baje.

En vez de hacer malabarismos, hay una solución muy sencilla que es indicarle al plugin maven de cobertura que excluya las clases que queramos:



org.codehaus.mojo
cobertura-maven-plugin



com/company/models/*.class






clean







Con esta configuración le estamos indicando a cobertura que pase de todos mis modelos, que son los que tienen los get/set :-)

Testear daos con testng

Al hilo del anterior post, ahora voy a comentar como realizar tests de los daos mediante otro popular framework de testing: TestNG.

En esta ocasión la clase de Spring de la que va a extender nuestro test es AbstractTransactionalTestNGSpringContextTests.

Para incorporar esta clase a nuestro classpath podemos hacerlo fácilmente añadiendo la dependencia maven del proyecto spring-test:


org.springframework
spring-test
2.5.6
test



Tenemos que utilizar una anotación @ContextConfiguration para indicarle donde se encuentra el fichero xml con la configuración de spring para cargar la configuración de conexión a la base de datos de testing.

Si queremos que las tablas de nuestra base de datos se encuentren en un estado determinado, podemos realizar las inserts que consideremos oportunas en el método que tenga la anotación @BeforeMethod

A continuación pongo el ejemplo:


@ContextConfiguration(locations={"classpath:/testng-spring-core-services-config.xml"})
public class RoleDaoTest extends AbstractTransactionalTestNGSpringContextTests {

@Autowired
private IRoleDao roleDao;

@Test
public void testFindByRoleNameExists (){
Role role = roleDao.findByRoleName(RoleName.Admin);
assertNotNull (role);
}

@Test
public void testFindByRoleNameNotExists (){
Role role = roleDao.findByRoleName(RoleName.AdvancedCrmUser);
assertNull (role);
}

@BeforeMethod
protected void onSetUpInTransaction() throws Exception {
Role roleAdmin = new Role();
roleAdmin.setName(RoleName.Admin);
roleDao.create(roleAdmin);
}

}


Como se ha comentado antes, el fichero testng-spring-core-services-config.xml es el que tiene la configuración de spring necesaria para que funcione el test. En mi caso tiene la siguiente pinta:



xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">



class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">











class="org.springframework.jdbc.datasource.DriverManagerDataSource">












Testear daos con JUnit

Para testear daos con junit me voy a basar en una clase que proporciona Spring: org.springframework.test.jpa.AbstractJpaTests.

Para incorporar esta clase a nuestro classpath podemos hacerlo fácilmente añadiendo la dependencia maven del proyecto spring-test:


org.springframework
spring-test
2.5.6
test



Para poder crear el test hacemos que nuestra clase extienda de AbstractJpaTests. Dentro de ella tenemos dos métodos importantes:

- onSetUpInTransaction: Es el método en el que vamos a realizar los inserts en nuestras tablas de la base de datos de tests para que se quede en el estado que necesitamos. Podemos estar tranquilos porque una vez terminado el test, estos cambios son desechados automáticamente.

- getConfigLocations: En este método es donde le indicamos donde se encuentra el fichero xml de spring donde se carga la configuración de la base de datos de test.

Posteriormente ya podemos implementar nuestros métodos de test (por supuesto empezando todos con "test" para que lo coja JUnit) en los que llamaremos a los daos y comprobaremos mediante asserts el correcto funcionamiento de los mismos.


public class GenericDaoTest extends AbstractJpaTests {

protected void onSetUpInTransaction() throws Exception {
jdbcTemplate.execute("insert into GenericModel (id, attr1) values (1, 'dato1')");
jdbcTemplate.execute("insert into GenericModel (id, attr1) values (2, 'dato2')");
jdbcTemplate.execute("insert into GenericModel (id, attr1) values (3, 'dato3')");
jdbcTemplate.execute("insert into GenericModel (id, attr1) values (4, 'dato4')");
}

protected String[] getConfigLocations() {
setAutowireMode(AUTOWIRE_BY_NAME);
return new String[] { "classpath:/junit-spring-services-config.xml" };
}

// METODOS DE TEST

}


A continuación se muestra un ejemplo de cómo podría ser el fichero de configuración de spring para que cargue correctamente los atributos de conexión a la base de datos: