SQLite y test unitarios

Al instalar WAMP en mi ordenador descubrí SQLite. Es una pequeña base de datos que no necesita nada de configuración y que escribe los datos en un fichero. Supongo que está pensada como base de datos simple para que puedan utilizar las aplicaciones, sin necesidad de todo el montaje necesario de un MySQL o similar.

Sin embargo, creo que hemos encontrado una posible aplicación para ella. Muchas veces, en los test unitarios es necesario tener una base de datos, por lo que el test no se puede correr si no está la base de datos disponible o si falla por algún motivo las conexiones con ella. Con SQLite parece que tenemos la posibilidad de tener ficheros con datos de base de datos, en el mismo sitio de los test, y sería a esos ficheros a los que nos "conectamos" para hacer el test. También es posible que la misma clase de test cree dicho fichero con ciertos datos, tablas o lo que sea.

Existe un driver de SQLite para java, por lo que una vez establecida la Connection, el resto del código es más o menos independiente de la base de datos a utilizar.

La posible pega es que SQLite está basada en C, por lo que es necesario tener su versión para el sistema operativo que se utilice. Esto quiere decir que en cualquier sitio donde se quiera pasar el test, se debe tener las librerías de esta base de datos.

De todas formas, es un pequeño descubrimiento que puede resultar útil para los test unitarios.

Programar para el test unitario

Me decido a escribir este post viendo los comentarios del post anterior y porque los test unitarios también es otra cosa en la que tengo interés. También tenemos que ir poniéndolos.

Estoy de acuerdo en que es muy difícil hacer determinados test -e incluso unos pocos bien hechos-, pero también es cierto que hacer el código de cierta forma ayuda a hacerlos.

Por ejemplo, si tengo una clase que instancia una clase de sockets, lee un mensaje de ella, actúa en consecuencia y como resultado envía otro mensaje con, no podré hacer un test fácilmente, salvo que en el test construya el servidor del socket y lo lance. Y quizás no pueda hacerlo, porque el servidor igual debe correr en otro ordenador y no puedo lanzarlo desde mi clase de test en remoto. Sin embargo, si tengo mi clase de socket que implementa una interface y luego se la paso a la clase que quiero, esa clase es más fácil de probar. Desde el test puedo simular la clase de socket con otra hecha a mi medida -creo que se conoce en este mundillo como mock-object- y que implemente la interface. Esta clase simulada de socket puede devolver un mensaje a medida cuando la clase de prueba intente leer y esta clase simulada recibirá los que la clase de prueba intente enviar, por lo que sí puedo comprobar como se comporta la clase sin necesidad de tener el socket abierto.

Para testear la clase de socket, sí puedo instanciar dos sockets en la clase de test de forma que se comuniquen entre ellos. Claro, siempre que la clase de socket sea suficientemente configurable como para indicarle que el servidor es "localhost".

Con base de datos pasa algo parecido. Si la clase consulta o inserta directamente, es difícil probar salvo que tengas la base de datos. Si haces una clase con interface que es la encargada de realizar las inserciones y consultas, es posible pasarle un mock-object de la clase de base de datos a la clase bajo prueba, con lo que se podría hacer alguna prueba sin necesidad de tener la base de datos y teniendo los datos controlados -es el mismo mock-object el que devuelve los datos a nuestra medida o el que recibe lo que la clase bajo prueba intenta insertar.

En fin, no digo que sea fácil y que no requiera trabajo, pero muchas veces el cómo se hace el código ayuda, dificulta e incluso hace imposible el hacer los test.

Principios básicos de la realización de pruebas

Me encanta lo que he leído aquí sobre los principios básicos de realización de pruebas, sobre todo lo de que hay que probar también que el código no hace lo que tiene no tiene que hacer.

Todo esto me recuerda además que tengo que seguir haciendo test de JUnit del código, porque cuando hay tiempo se van haciendo, pero cuando te entra la pereza o tienes prisas, pasas de ello -como es mi caso ahora mismo-. Pero luego la verdad es que cuando tocas código antiguo, sobre todo si es de una librería que utilizas mucho, lo tocas con más tranquilidad si están esos test hechos. En más de una ocasión nos han avisado antes de meter la pata en una de esas librerías.

Test Continuo

Igual que tenemos integración continua, que básicamente consiste en compilar con frecuencia todo el proyecto desde cero y que hay herramientas que lo hacen automáticmente todos los días, como Cruise Control, también -acabo de descubrir- existe el concepto de test continuo.

Si tenemos unas mínimas buenas costumbres de programación, haremos test unitarios para nuestras clases. Herramientas como Cruise Control y maven se encargan de pasar esos test cada vez que compilamos y nos dicen si hemos o no metido la pata. Pero … ¿por qué no ir más allá?

Acabo de descubrir un plug-in para eclipse -que todavía no he probado, pero lo haré pronto- llamado Continuous Testing Plugin. Tiene pinta de que tú lo instalas en Eclipse, le dices cuales son las clases de test y él, según vas tocando código, va pasando los test de la que eclipse recompila automáticamente. Esto hace que si estropeas un test tocando código, te enteres inmediatamente.

Este mismo Lunes lo pruebo en el trabajo, aunque me da un poco de miedo que ralentice mucho eclipse.

Maven y Junit4

Bueno, a lo que venía la entrada anterior sobre JUnit4 es porque lo he probado también con maven … y no ha funcionado.

Según leo en este artículo, allá por Agosto JUnit4 era muy moderno y maven no lo soportaba. Ahora, en Diciembre, puede que pase lo mismo, o al menos, a mi no me ha funcionado. Se ejecutan los test, quizás porque el nombre que le puse era de testNoSeQue() como en versiones anteriores de JUnit, pero no se ejecuta el método setUp(), al que no he llamado setUp(), sino que simplemente le puse el @org.junit.Before antes.

La persona del artículo parece que se hizo un plugin rápido para soportar esto, que no está todo lo completo que debiera y con sus limitaciones. Bueno, lo he probado así, en plan rápido, y tampoco me ha funcionado.

Todavía tengo que hacer algunas pruebas, como además de poner las anotaciones, nombrar a los métodos como se llamaban antiguamente, setUp() y demás. También en JUnit4 hay una especie de adaptador para que la clase de test se vea como si fuera de un JUnit anterior, simplemente poniendo algo asi en la clase de test

public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(SimpleTest.class);
}

Ya contaré qué tal ha ido el tema.

Junit4

Acabo de hacer un pequeño test tonto con JUnit4 para probarlo. El test es este

import static org.junit.Assert.*;
import org.junit.*;

public class UnTest
{
// Se inicializa a false, pare comprobar que se llama al metodo inicializa()
private boolean valor=false;

@org.junit.Before
public void inicializa()
{
valor=true;
}

@Test
public void laPrueba()
{
// Este test falla si no se ha llamado a inicializa()
assertEquals(valor,true);
}

public static void main (String [] args)
{
org.junit.runner.JUnitCore.main(«UnTest»);
}
}

Lo primero que me llama la atención es la nueva forma de marcar los métodos. En las versiones anteriores, JUnit sabía lo que era cada método por el nombre del método. Por ejemplo, el método que debía ejecutarse antes de iniciar los test y para darnos oportunidad de inicializar las cosas debía llamarse setUp(). Los métodos de test debían empezar por testLoQueSea(). Además la clase debía heredar de alguna de las clases de Test de JUnit.

Ahora no. Se usan las anotaciones de java (los @no.se.que que se pone delante de los métodos). Marcando un método como @org.junit.Before, JUnit sabe que debe ejecutar ese método antes de los test. Etiquetando un método como @Test, JUnit sabe que ese es un método de Test. Las clases de test ahora no tienen que heredar de nadie.

Otra cosa que me ha llamado la atención, aunque seguramente es de java 5, es el import static del principio. El método assertEquals() pertenece a la clase org.junit.Assert, así que imagino que ese import static tan curioso es para importar los métodos estáticos de esa clase y poder usarlos sin más.