Table of Contents
JUnit4
Desde que empecé a estudiar esto del java, hace unos dos años, han cambiado muchas cosas, y se nota por el perfil de este artículo que voy a escribir. Mientras que antes hablaba de cómo se manejaba un fichero, o cómo se conectaba a una base de datos usando jdbc, ahora vamos a ir a un tema muy específico: integrar JUnit4 con Spring 3.0.2 y con Eclipse Helios.
Introducción
Los que hayais intentado integrar JUnit 4 con Spring 3.0.2 tendreis dos opiniones del asunto: 1) es tremendamente fácil, no tiene sentido este artículo y 2) ¿Porqué demonios hay tanto material en internet –incluso en la propia web de Junit y de Spring y nada me funciona??? ¿qué demonios estoy haciendo mal???
Y la tercera pregunta ¿Porqué el testSuite que me he creado para Junit no me funciona???
Vamos por partes, queridos amigos.
El ejemplo
Comenzaremos por configurar Eclipse Helios para que acepte JUnit 4 y nada más:
- Haremos click con el botón derecho sobre el icono del proyecto y seleccionaremos “propiedades”
- En la opcion “Java Build Path” seleccionaremos “Libraries”
- click en “add library”: seleccionar JUnit y luego JUnit4
Luego, dentro de la carpeta de los fuentes –“src” en mi caso–, crearemos un nuevo paquete para alojar las clases de los tests.
Regla 1: dentro de este paquete tiene que haber una copia del fichero applicationContext.xml
de Spring. No me pregunteis porqué, pero en mi ejemplo lo va a buscar ahí, y no hay manera de hacerle desistir. ¿A lo mejor es porque dice el manual que lo buscará sólamente en el CLASSPATH?? puede ser….
Usuarios de linux: para mantener una única copia del fichero applicationContext.xml
se puede crear un enlace virtual. Viva Linux!!!
Y ahora creamos nuestro primer test.
Primer error: creando el primer test
Ingenuo de mí, pensé que si configuraba eclipse para que usara la librería de JUnit4, crearía tests para JUnit4…. ¡Qué ingenuo, madre!!!! Los muchachos de eclipse.org aún no se han dado cuenta y el asistente de eclipse crea solamente test para JUnit 3.x.
Regla 2: los tests hay que escribirlos A MANO. Olvidaros de asistentes y otras chorradas. A MANO ¿He dicho ya que los tests hay que escribirlos a mano??? Pues eso, que los escribais a mano.
Aquí os pongo un test de ejemplo que he escrito A MANO leyendo de aquí y de allí en internet.
package test; import htmlCleaner.htmlElement; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; import org.springframework.test.context.ContextConfiguration; import org.springframework.beans.factory.annotation.Autowired; import static org.junit.Assert.*; @ContextConfiguration(locations={"applicationContext.xml"}) public class HTMLElementTest extends AbstractJUnit4SpringContextTests { // just for using AbstractJUnit4springContextTests // we have the applicationContext instance variable // will access to the instance of the Application // Context // this instance will be dependency injected by name @Autowired private htmlElement element; @Before public void heating() throws Exception { System.out.println("hello setup method"); } @After public void cooling() throws Exception { System.out.println("goodbye setup"); } @Test public void testATest() { System.out.println("hello spring"); element.setIdAndContent( -1, new StringBuilder("hola") ); assertTrue( true ); } }
Por cierto, agradeceré enormemente que alguien me explique porqué los ejemplos los pone la gente sin los “import”. Hala, ahí te las compongas para buscar de dónde han salido las clases!!!
Explicando un poco el asunto
@ContextConfiguration(locations={"applicationContext.xml"})
El meollo del maridaje JUnit - Spring. ContextConfiguration consigue proveer un contexto de aplicación para el test. Además de que nos va a hacer el Autowiring –o sea, crear automáticamente los beans que necesitemos–, es así de majo que nos deja disponible en la propiedad applicationContext
linea directa con el contexto de aplicación, por si nos apetece crear un bean que nos haga falta.
Enchufando cosas
// this instance will be dependency injected by type @Autowired private htmlElement element;
La anotacion @Autowired
permite que una propiedad sea auto-conectada en el momento de la creación del test. Va a identificarla por tipo. En otras palabras, buscará un bean que sea del mismo tipo htmlElement que hemos definido, y va y se lo casca.
¿Qué pasa si hay dos bean del mismo tipo??? pues que entonces se hace un lío. En ese caso @Qualifier
viene al rescate, permitiendonos identificar el bean concreto.
Las anotaciones @Before
, @After
, @Test
creo que os podeis imaginar lo que hacen.
Ejecutando el test
Sobre la clase, boton derecho, Run As → Junit Test y se lanzará el test, mostrando algo así:
31-jul-2010 0:54:00 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [test/applicationContext.xml] 31-jul-2010 0:54:00 org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.GenericApplicationContext@351e1e67: startup date [Sat Jul 31 00:54:00 CEST 2010]; root of context hierarchy 31-jul-2010 0:54:00 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4ce66f56: defining beans [htmlCleaner,htmlTokenizer,htmlElement,tagTokenizer,tagParser,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor]; root of factory hierarchy hello setup method hello spring goodbye setup
Si os sale una ventana de configuración de ejecución teneis que seleccionar que eclipse ejecute el test como un test de JUnit4. Dato importante.
Otro problema bastante común es que se produce un error “File not found exception”: a mí me ocurrió porque no tenía el fichero applicationContext.xml en la misma carpeta que la clase.
Segundo error: Creando una Suite que agrupe a todos los test
Tampoco se os ocurra crear la suite de tests usando el asistente de eclipse. Aquí hay que picarlo todo a mano. Os paso un ejemplo de los muchos que hay por internet:
package test; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({HTMLTokenizerTest.class, HTMLCleanerTest.class, HTMLElementTest.class}) public class AllTests { }
HTMLTokenizerTest.class
, HTMLCleanerTest.class
y HTMLElementTest.class
son las clases de test que he creado para este ejemplo.