HSQLDB para pruebas unitarias con JUnit



HSQLDB (HyperSQL Data Base) es un gestor de base de base de datos escrito en java, tiene algunas opciones de acceder a esta base de datos:

  • Modo Servidor: Esta modalidad es la clásica utilizada por los gestores de base de datos, se encuentra en un servidor donde te conectas por medio de un puerto. Para esta opción necesitas tener el gestor de base de datos montado en un servidor y utilizar el archivo jar que nos da el driver del gestor (hsqldb.jar). Se especifica con el protocolo hsql.
  • Modo Stand Alone (modo de archivo): Esta modalidad guarda la base de datos en un archivo externo, no se necesita tener el gestor de base de datos en servidor, para desarrollo se puede utilizar con solo el jar que nos proporciona hsqldb. Se especifica con el protocolo file.
  • Modo en memoria: Esta modalidad nos genera una base de datos en memoria que utiliza la aplicación, esta modalidad es poco usual. Esta modalidad es la que utilizaremos para hacer nuestros casos de prueba, así podremos controlar perfectamente que información tenemos en la base de datos y podremos saber cual es el resultado que queremos obtener de nuestra prueba unitaria. Se especifica con el protocolo mem.

Para este ejemplo utilizaremos el tutorial anterior Hibernate 3 integración con Spring  y crearemos las pruebas unitarias de nuestra clase DAO. Ya teniendo nuestro DAO tenemos que probarlo, para hacerle pruebas unitarias utilizaremos JUnit 4 con la integración que hace Spring (podemos basarnos en el tutorial JUnit4 integración con Maven2 y Spring) para poder inicializar los beans de Spring para utilizar la integración que Spring tiene de Hibernate.



En las carpetas podemos observar que crearemos nuestra clase de prueba ModelDaoHibernateImplTest, que se encuentra en la misma ruta de nuestro clase que queremos probar. También tenemos dos archivos de configuración de spring en la misma ruta de carpetas, AnnotationModel-context.xml y MappingModel-context.xml.

Nuestro archivo pom.xml quedaría de esta forma

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.blogspot.appcode</groupId> <artifactId>tutorial-hibernate</artifactId> <version>1.0.0</version> <name>Tutorial Hibernate</name> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>${org.hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>${org.hibernate.hibernate-annotations.version}</version> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>1.8.0.10</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.2.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${org.slf4j.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${org.slf4j.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${org.slf4j.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> <properties> <org.slf4j.version>1.6.1</org.slf4j.version> <org.springframework.version>3.0.5.RELEASE</org.springframework.version> <junit.version>4.8.2</junit.version> <org.hibernate.version>3.2.7.ga</org.hibernate.version> <org.hibernate.hibernate-annotations.version>3.4.0.GA</org.hibernate.hibernate-annotations.version> </properties> </project>


En el archivo AnnotationModel-context.xml tenemos definido que nuestro mapeo de hibernate se realizara promedio de anotaciones, indicando que clases queremos mapear y donde se encuentran.


<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> <property name="url" value="jdbc:hsqldb:mem:." /> <property name="username" value="sa" /> <property name="password" value="" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="annotatedClasses"> <list> <value>com.blogspot.appcode.tutorial.hibernate.model.Model</value> <value>com.blogspot.appcode.tutorial.hibernate.model.ModelType</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> </props> </property> </bean> <bean class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource" /> </bean> <bean class="com.blogspot.appcode.tutorial.hibernate.persistance.impl.ModelDaoHibernateImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>

En el archivo MappingModel-context.xml tenemos definido que los mapeos de hibernate se realizaran por archivos de configuración xml.

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="org.hsqldb.jdbcDriver" /> <property name="url" value="jdbc:hsqldb:mem:." /> <property name="username" value="sa" /> <property name="password" value="" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="mappingResources"> <list> <value>com/blogspot/appcode/tutorial/hibernate/persistance/hbm/Model.hbm.xml</value> <value>com/blogspot/appcode/tutorial/hibernate/persistance/hbm/ModelType.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> </props> </property> </bean> <bean class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource" /> </bean> <bean class="com.blogspot.appcode.tutorial.hibernate.persistance.impl.ModelDaoHibernateImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>

En los dos archivos podemos ver que el data source esta conectando a una base de datos de HSQLDB por la forma de conexión de memoria (base de datos no persistente), también podemos ver que en nuestro bean de sessionFactory le colocamos varias propiedades de hibernate, una es que utilice un dialecto de HSQLDialect, también colocamos, solo a forma de prueba, que nos muestre el código sql de lo que ejecutara y también que nos lo coloque en un formato fácil de leer, también colocamos una propiedad para que nos cree la base de datos si esta no existe, esto se logra con la propiedad de hibernate.hbm2ddl.auto=create. Esto nos servirá en nuestra prueba para que se cree automáticamente la base de datos hsqldb en la memoria de la aplicación. La base de datos se creara automáticamente dependiendo de nuestros mapeos de hibernate que coloquemos. Y gracias a HSQLDB en modalidad de memoria, esta base de datos quedara en memoria únicamente siendo perfecta para ser utilizada como pruebas y al terminar nuestro test la memoria quedara libre y nuestra base de datos destruida.

Tenemos ya nuestros archivos de configuración de spring para probar nuestra clase DAO, los archivos definen los mismos beans pero aplican mapeos de hibernate diferentes, entonces para poder probar uno y después del otro, como forma de ejemplo, podemos hacer el cambio en la clase de prueba configurando la ruta del archivo xml que deseo tomar.

ModelDaoHibernateImplTest.java

package com.blogspot.appcode.tutorial.hibernate.persistance.impl; import java.util.List; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.blogspot.appcode.tutorial.hibernate.persistance.ModelDao; import com.blogspot.appcode.tutorial.hibernate.model.Model; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "AnnotationModel-context.xml") // @ContextConfiguration(locations="MappingModel-context.xml") public class ModelDaoHibernateImplTest { @Autowired ModelDao modelDao; @Autowired JdbcTemplate jdbcTemplate; @Before public void before() { jdbcTemplate.execute("delete from model"); jdbcTemplate.execute("delete from model_type"); jdbcTemplate .execute("insert into model_type (id,name,deleted) values ('T1','Model Type 1',false)"); jdbcTemplate .execute("insert into model (id,name,deleted,model_type_id) values (1,'Model 1',false,'T1')"); jdbcTemplate .execute("insert into model (id,name,deleted,model_type_id) values (2,'Model 2',true,'T1')"); jdbcTemplate .execute("insert into model (id,name,deleted,model_type_id) values (3,'Model 3',false,'T1')"); } @Test public void findHqlTest() { List<Model> models = modelDao.find(true); System.out.println("\nfindHqlTest print list"); printList(models); Assert.assertEquals(models.size(), 1); } @Test public void findSqlTest() { List<Model> models = modelDao.find("odel"); System.out.println("\nfindSqlTest print list"); printList(models); Assert.assertEquals(models.size(), 3); } @Test public void findCriteriaTest() { List<Model> models = modelDao.find("2", true); System.out.println("\nfindCriteriaTest print list"); printList(models); Assert.assertEquals(models.size(), 1); } private void printList(List<Model> models) { for (Model model : models) System.out.println(model.getId() + " - " + model.getName() + " - " + model.getDeleted()); } }

En el método @Before de nuestra clase de prueba podemos observar que borramos los datos de la base de datos para asegurarnos que este limpia, y después agregamos algunos registros, recordemos que el método @Before se ejecuta antes de antes de cada método @Test, así cada vez que se ejecute un método @Test tendrá los mismos datos en la BD, así estaríamos seguros de lo que nuestro método debe regresar y así verificar si nuestra clase DAO esta funcionando de forma adecuada. También podemos observar en la clase que esta documenta un @ContextConfiguration, así podemos probar las dos formas de mapear clases, por anotación y por archivos de configuracion xml.

Ahora solo basta probar nuestra clase, con el comando mvn test.

Si desean descargar este tutorial les dejo la liga aquí