Saltar al contenido principal
Página

Tema 2.2 - JUnit

Introducción a JUnit

Como se había dicho anteriormente, JUnit es una librería de código libre que se basa en anotaciones, utilizado en la automatización de pruebas unitarias y cualquier otro tipo de testing automatizado para aplicaciones desarrolladas en Java, JUnit permite la ejecución de clases controladamente para evaluar el correcto funcionamiento de sus métodos, retornando una respuesta exitosa o un fallo a este, de acuerdo al valor de entrada.

  • En la actualidad, se cuenta con la más reciente versión de JUnit, JUnit 5:
Esta versión, requiere de Java 8 o versiones posteriores y está compuesto por los módulos 

  • JUnit Platform
  • JUnit Jupiter
  •  JUnit Vintage, 


Definición de cada modulo:

Módulo JUnit Platform: es la base para lanzar pruebas de framework en la máquina virtual de Java (JVM), permitiendo definir un TestEngine API (interfaz de JUnit que permite el descubrimiento y ejecución de pruebas en un modelo de programación particular) para desarrollar un marco de prueba que se ejecuta en la plataforma. Este módulo posee además soporte para los IDE: IntelliJ IDEA, Eclipse, Netbeans, Visual Studio Code y herramientas de compilación como (gradle, Maven y ant).

Módulo  JUnit Jupiter: sería la combinación del nuevo modelo de programación y un modelo de extensión para escribir pruebas y extensiones en JUnit, provee un TestEngine basado en pruebas de la plataforma. 

Módulo Junit Vintage: provee un TestEngine para correr Junit 3 y Junit 4 basado en pruebas de la plataforma, ahora bien, hablar de JUnit nos exige manejar varios conceptos y sus derivaciones como: estructura de métodos, anotaciones y asserts (o en su defecto, afirmaciones). 


Estructura de métodos

JUnit admite la clase de test con la estructura de sus 4 tipos de métodos posibles, los cuales se enunciarán a continuación.

  • El primer método admitido por JUnit es el setUp donde se asignan los valores o variables iniciales antes de ejecutar cada test
  • El segundo método es el tearDown que se llama después de cada test para liberar los recursos inicializados en el método setUp
  • El tercer método admitido es el Test, en esta parte, no es necesario que vaya solo un método, sino al contrario, la cantidad de métodos que tengamos en la clase seleccionada para realizar la prueba unitaria
  • Por último, el cuarto método permitido son los métodos auxiliares que se requieran en la prueba

 

Anotaciones 

Una anotación es un carácter especial, que se ha incluido desde la versión 4 de JUnit, para intentar simplificar más la labor del programador. Se trata de palabras clave que se colocan antes de los métodos definidos e indican a las librerías JUnit instrucciones concretas; (algunas anotaciones, son opcionales y su uso depende de las exigencias de cada aplicación).

En las siguientes líneas, veremos las anotaciones más usadas en las pruebas unitarias.

  • La anotación @Test Indica a JUnit que se trata de un método test, que como ya se había dicho, son los que se aplican a métodos de la clase que se haya seleccionado
  • La anotación @Before en JUnit4 o su respectiva en JUnit5, @BeforeEach, se ejecuta siempre antes de cada método de test y precede al método setUp. Este suele preparar el entorno de prueba, como lectura de datos de entrada, inicialización de clases. Si tiene que preceder al método setUpClass, la notación será "@BeforeClass" para JUnit4 o @BeforeAll para JUnit5.
  • La anotación @After en JUnit4 o @AfterEach en JUnit5, se ejecuta siempre después de cada método de test y precede al método tearDown. Se usa para limpiar el entorno de desarrollo como restauración de valores por defecto que se hayan inicializado en el método @Before. Si tiene que preceder al método tearDownClass, la notación será "@AfterClass" para JUnit4 o @AfterAll para JUnit5.
  • @BeforeClass en JUnit4 o @BeforeAll en JUnit5, se ejecuta una sola vez antes de ejecutar los test de la clase. En este se crean estructuras de datos y componentes que vayan a ser necesarios para todas las pruebas, como lo son, conexiones a bases de datos, dichos métodos deben ser estáticos.
  • @AfterClass en JUnit4 o @AfterAll en JUnit5, se ejecuta una sola vez después de todos los test de la clase. Libera los recursos definidos en el @BeforeClass o @BeforeAll y solo se aplica a métodos estáticos.
  • @RunWith se asigna una clase a la que JUnit invocará en lugar del ejecutor por defecto de JUnit
  • @Ignore (JUnit4) o @Disabled (JUnit5) indica que el método test esta deshabilitado. Por ejemplo, cuando el código se ha cambiado y el test no está preparado para ser aplicado a este, se usa esta anotación.

 

Asserts (Afirmaciones)

Después de tener aplicado en nuestro código las anotaciones con sus respectivos métodos, es necesario comprobar los resultados que se obtendrán. Por lo anterior, la clase Assert dispone de una lista de funciones que ayudaran en dicho proceso. Estas funciones comparan el valor obtenido con el valor esperado lanzando excepciones si no son iguales. A continuación, se hace la relación entre la estructura de los métodos con su respectiva descripción.

Método

Descripción

assertTrue([mensaje], condición booleana)

Verifica que la condición sea verdadera

assertFalse([mensaje], condición booleana)

Verifica que la condición sea falsa

assertEquals([mensaje], valor esperado, valor obtenido)

Comprueba que dos valores sean iguales

assertSame([mensaje], valor esperado, valor actual)

Valida que ambos parámetros sean el mismo objeto

assertNotSame([mensaje], valor esperado, valor actual)

Valida que los parámetros no sean el mismo objeto

assertNull([mensaje], objeto)

Comprueba que el objeto sea nulo

assertNotNull([mensaje], objeto)

Valida que el objeto no sea nulo

 

JUnit está disponible en algunas herramientas de desarrollo como Netbeans y Eclipse las cuales poseen plug-ins que admiten la generación de plantillas para la creación de las pruebas de una clase Java automáticamente. En la siguiente tabla, se hace la generación de una plantilla usando JUnit en sus versiones 4 y 5 de una clase calculadora que posee los métodos básicos de suma, resta, multiplicación y división.

JUnit4 en eclipse

JUnit5 en eclipse

package test;

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class CalculadoraTest {

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void testSumar() {
		fail("Not yet implemented");
	}

}
package test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class CalculadoraTest {

	@BeforeAll
	static void setUpBeforeClass() throws Exception {
	}

	@AfterAll
	static void tearDownAfterClass() throws Exception {
	}

	@BeforeEach
	void setUp() throws Exception {
	}

	@AfterEach
	void tearDown() throws Exception {
	}

	@Test
	void testSumar() {
		fail("Not yet implemented");
	}

}

Nótese que, para ambos casos, la plantilla generada está acorde con los estándares de nombramiento de clases y métodos, además de incluir la estructura de métodos permitidos por JUnit como el setUp, tearDown, etc. y las anotaciones básicas.

Un aspecto importante sobre JUnit, es que permite realizar muchas pruebas a la vez, es decir, si tenemos varias clases a las cuales debemos hacer pruebas unitarias, en el proceso normal tendríamos que acceder a cada una y ejecutarlas; para evadir ese proceso largo y tedioso, JUnit nos ofrece JUnit Test Suite que nos permite correr todas las clases de prueba a la vez.

 

Aplicación

Vista la teoría es necesario replantearla y aplicar dichos conceptos a la realidad, y que mejor manera que presentando un ejemplo para asignación de notas a un estudiante. El entorno utilizado para éste, será Eclipse, por lo tanto, no será necesario descargar el paquete de instalación de JUnit ya que el IDE lo trae consigo. Sin embargo, si se desea utilizar otro entorno de desarrollo que carezca de JUnit por defecto, puede visitar la página oficial y descargar la versión que desee. El enlace a continuación lo re direccionará a la descarga de la versión 5 de JUnit https://junit.org/junit5/

Haciendo uso de JUnit Test Case

Lo primero que vamos hacer, es crear un nuevo proyecto Java, llamado SistemaBasicoNotas, con sus respectivas clases, para este caso, Estudiante y Boletin.

Instrucciones para la creación de un nuevo proyecto Java


Para el ejemplo, el código fuente será el siguiente.

Clase Estudiante

package main;

public class Estudiante {
	
	public String id;
	public String nombreCompleto;
	public String sexo;
	
	public String getNombre () {
		return nombreCompleto;
	}

	public boolean blankSpaceValidation () throws Exception {
		boolean validation;
		
		if (id !="" && nombreCompleto != "" && sexo !="") {
			validation= true;
		}else {
			validation = false;
			throw new Exception("Por favor, no dejes valores"
					+ " en blanco al momento de crear un estudiante");
		}
		return validation;
	}

}

Clase Boletín

package main;

public class Boletin {
	public String idBoletin;
	public Estudiante estudiante;
	
	//Inicialización de variables
	static int MAXMATERIA = 5;
	int indexArrayMateria= 0;
	double [] notas = new double[MAXMATERIA];
	double suma=0;
	int contador=0;
	double resultado=0;
	
	public boolean blankSpaceValidation () throws Exception {
		boolean validation;
		
		if (idBoletin !="") {
			validation= true;
		}else {
			validation = false;
			throw new Exception("Por favor, no dejes valores en "
					+ "blanco al momento de crear un nuevo boletin ");
		}
		return validation;
	}
	
	public void guardarNotas (double nota) throws Exception {
		if(indexArrayMateria<MAXMATERIA ) {
			notas [indexArrayMateria]= nota;
			indexArrayMateria ++;
		}else {
			throw new Exception("Lo sentimos, pero el estudiante no "
					+ "puede tener más de 5 notas, por solo tener 5 materias "
					+ "registradas en el sistema");
		}
	}
	public double calcularPromedio () {
		for (int i = 0; i < notas.length; i++) {
			  suma+=notas[i];
			  contador+=1;
			}
		resultado=suma/MAXMATERIA;
		return resultado;
	}
    
}

 Una vez creadas las clases e implementado el código, debemos hacer uso de JUnit5 para realizar las pruebas, para ello, debemos dar click derecho sobre la clase a la cual se le harán las pruebas, seleccionamos “New” y luego “Other” como se muestra a continuación.

Nota: En algunos casos JUnit Case, se presenta una vez seleccionado “New”, como aparece en la imagen.

Creando la prueba de una clase con JUnit Case

Cuando seleccionamos en “Other”, se abrirá una nueva ventana. Ahora solo basta buscar “JUnit”, seleccionar “JUnit Test Case” y dar click en “Next”

Proceso para crear la prueba unitaria


Una vez seleccionado “Next”, se nos abrirá una nueva ventana para configurar JUnit Test Case.


  • La nueva ventana tiene las versiones de JUnit disponibles en Eclipse (JUnit3, JUnit4 y JUnit5 o en su defecto JUnit Jupiter), para nuestro caso, damos click a la última versión “New JUnit Jupiter test”.
  • Source Folder: La carpeta de origen la dejamos por defecto, ya que eclipse detecta automáticamente la ubicación de la clase que le vamos aplicar la prueba.
  • Package: El paquete debe ser como el estándar lo define, por lo tanto, este debe ser de nombre “test”. Basta con escribirlo en el espacio y este se genera en el último punto
  • Name: Nótese que el nombre generado, cumple con los estándares vistos (nombre de la clase + palabra “Test”). Esta sección no se modifica.
  • Para el ejemplo, seleccionamos cada uno de los métodos que aparecen.

Cuando hayamos revisado los puntos, damos clic en “Next”

Configuración de la clase para prueba

La ventana que se nos abrirá al dar siguiente será la que nos pedirá los métodos de la clase que vamos a crear para realizarle las pruebas. En el caso nuestro, seleccionaremos todos y finalizamos.

Selección de los métodos de la clase para prueba


Al ser primera vez que utilizamos la librería de JUnit, nos pedirán que agreguemos la librería de JUnit5 al build path del proyecto, para ello lo seleccionamos y damos OK

Descarga de la librería JUnit6 al build path


En este punto, ya tenemos nuestra clase de prueba, con los métodos básicos que admite JUnit y los métodos propios de la clase que elegimos.


package test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class BoletinTests {

	@BeforeAll
	static void setUpBeforeClass() throws Exception {
	}

	@AfterAll
	static void tearDownAfterClass() throws Exception {
	}

	@BeforeEach
	void setUp() throws Exception {
	}

	@AfterEach
	void tearDown() throws Exception {
	}

	@Test
	void testBlankSpaceValidation() {
		fail("Not yet implemented");
	}

	@Test
	void testGuardarNotas() {
		fail("Not yet implemented");
	}

	@Test
	void testCalcularPromedio() {
		fail("Not yet implemented");
	}

}


A modo de comprender que sucede con las anotaciones, vamos a borrar los assert fail de cada método de la clase y hacemos un System.out.println() en los métodos de todas las anotaciones. El código debe quedar de la siguiente manera.

package test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import main.Boletin;
import main.Estudiante;

 class BoletinTest {

	@BeforeAll
	 static void setUpBeforeClass() throws Exception {
	  System.out.println("Inicialización de variables antes de iniciar los tests\n");
	 }

	 @AfterAll
	 static void tearDownAfterClass() throws Exception {
	  System.out.println("\nSe ejecuta para limpiar el entorno despues de todos los tests");

	 }

	 @BeforeEach
	 void setUp() throws Exception {
	  System.out.println("Inicialización de variables antes de iniciar cada test");

	 }

	 @AfterEach
	 void tearDown() throws Exception {
	  System.out.println("Se ejecuta para limpiar el entorno despues de cada tests\n");

	 }
	 

		@Test
		void testBlankSpaceValidation() {
			System.out.println("****Ejecuta la prueba 1");
		}

		@Test
		void testGuardarNotas() {
			System.out.println("****Ejecuta la prueba 2");
		}

		@Test
		void testCalcularPromedio() {
			System.out.println("****Ejecuta la prueba 3");
		}

}


Para ejecutar JUnit solo basta dar clic derecho sobre la clase de prueba que generamos, Run As y JUnit test.


Corriendo JUnit


Analicemos el resultado generado, de acuerdo a la teoría vista.

  • Los textos rodeados de los rectángulos rojos son las anotaciones @BeforeAll y @AfterAll. El primero encargado de ejecutarse solo UNA VEZ para crear estructuras de datos e inicializar variables necesarias para todas las variables; y el segundo para limpiar el entorno de variables y estructuras de datos inicializados con el @BeforeAll.
  • Los encerrados por los rectángulos azules son las anotaciones @BeforeEach y @AfterEach, cuya ejecución es directamente proporcional a la cantidad de métodos @Test existentes.
  •  Finalmente, el resultado encerrado por el rectángulo amarillo, corresponde a los métodos de la clase Boletín, en este caso tres.

Resultados de las anotaciones de las pruebas unitarias con JUnit 5


Otro ejemplo de una anotación que puede servir es el @Disabled, el cual da la instrucción a JUnit de que ese método de prueba no se va a ejecutar.

		@Test
		void testCalcularPromedio() throws Exception {
			System.out.println("*** Aquí se ejecuta el método de calcular promedio");

		}
		
		@Disabled
		void testBlankSpaceValidation () throws Exception {
			System.out.println("*** Aquí se ejecuta el método de validación de espacios");
		}


Observe que para el ejemplo hacemos uso del disabled para el test de validación de espacios, por lo cual, el resultado debe ignorar ese método y solo ejecutar el testCalcularPromedio.

Resultado de la anotación disabled


Es momento de analizar el funcionamiento de algunos asserts vistos, para ello generamos la prueba a la clase Estudiante como se hizo anteriormente con la clase Boletín.

 

AssertEquals para tipos String:

Una vez realizado lo anterior, creamos un objeto de la clase Estudiante. Para corroborar que el nombre si se ha guardado correctamente, hacemos un assertEquals con el valor esperado y el arrojado por el sistema con el getNombre() acompañado de un mensaje que nos avisara cuando el nombre no coincida


	@Test
	void testGetNombre() {
	
		//Creamos un nuevo objeto estudiante con los datos respectivos
		Estudiante estu1= new Estudiante();
		estu1.id="102456258";
		estu1.nombreCompleto="Andrés Rafael";
		estu1.sexo="Masculino";
		
		//definimos las variables para actual y esperado
		String actual = estu1.getNombre();
		String esperado="Andrés Rafael";
		
		//implementamos el assertEquals
		assertEquals("Los valores no coinciden", esperado, actual);
		
	}


Note que al correr JUnit sobre EstudianteTest, se abre en la parte inferior los resultados arrojados por JUnit. Presentando cero errores, cero fallos, el indicador de fallo en verde y el nombre de la clase. Lo que indica que tanto el nombre guardado como el consultado coinciden y no hubo algún error en el guardado de este atributo.


Resultado del assert equals comparando strings

Si por error hubiéramos guardado el nombre “Andrés Rafael” y el valor de la variable esperado hubiera sido “Andres Rafael” el resultado hubiera generado un fallo, ya que Andrés es diferente a Andres. Una vez ejecutado el código, el resultado nos mostraría el indicador en rojo y en la traza de fallos, el error generado y el mensaje definido en el assert “Los valores no coinciden”.

Provocando un error en el assertEquals


AssertEquals para tipos double:

Para ejecutar este assert, haremos uso de BoletinTest del método testCalcularPromedio. Creamos el objeto de la clase estudiante y la clase Boletin. Llamamos a calcularPromedio() y usamos el asserEquals para verificar que el valor coincida con el esperado (4.5).

		@Test
		void testCalcularPromedio() throws Exception {
			System.out.println("****Ejecuta la prueba 3");
			//Creamos un nuevo Objeto Boletin
			Boletin bole1= new Boletin();
			bole1.idBoletin="1";
			bole1.guardarNotas(5.0);
			bole1.guardarNotas(5.0);
			bole1.guardarNotas(5.0);
			bole1.guardarNotas(5.0);
			double esperado = 4.5;
			double actual = bole1.calcularPromedio();
			
			assertEquals(esperado, actual, 0.1);
		}


Es de aclarar que el promedio se realiza con el número total de materias, es decir, 5. Por tal motivo el resultado del assert crea un fallo por solo haberse registrado 4 notas además de no coincidir el promedio.


Prueba del assertEquals double


AssertTrue:

Otro assert es el assertTrue, que permite verificar si una condición es verdadera. Como en la clase se tiene un método que verifica que no exista espacios en blancos al momento de añadir el valor a los atributos, este arroja un true cuando los valores están llenos y false cuando se intenta guardar un objeto con algún atributo sin valor. Para el ejemplo, vamos a guardar un estudiante sin id (identificación).

	@Test
	void testBlankSpaceValidation () throws Exception {
		//Creamos un nuevo Objeto estudiante
		Estudiante estu2= new Estudiante();
		estu2.id="";
		estu2.nombreCompleto="Mariana Rosales Franco";
		estu2.sexo="Femenino";
		
		//definimos variables para el assert
		boolean actual = estu2.blankSpaceValidation();
				
		//implementamos el assertTrue
		assertTrue(actual);
	}


Al ejecutarlo, efectivamente nos lanza el error con el respectivo mensaje del throws Exception definido en el método de la clase de origen. Note que como se tienen dos test, el resultado los muestra ambos; el testGetNombre con una buena calificación, y el testBlanckSpaceValidation con un error.


error


Sin embargo, si se añade la información correcta el resultado será exitoso. Dándonos como conclusión el correcto funcionamiento de los métodos

exitoso


Haciendo uso de JUnit Test Suite

Ya vimos cómo hacer pruebas unitarias a una sola clase, ahora veremos, como hacerla a varias haciendo uso de JUnit Test Suite. Siguiendo el ejemplo anterior y suponiendo que vamos a realizar las pruebas a cada clase generada, damos clic derecho sobre nuestro proyecto seleccionamos “new”  y luego “Other” como se muestra en la siguiente imagen.

Habilitando una nueva clase de JUnit para correr todas las clases de prueba en una sola ejecución a través de JUnit test Suite


Consecuentemente, eclipse abrirá la siguiente ventana donde seleccionaremos “Java”, “JUnit”, “JUnit Test Suite” y finalmente, “Next”.

Selección de JUnit test Suite


En la próxima ventana, dejamos seleccionado “New JUnit4 suite” seleccionamos el paquete donde va a quedar la clase generada, asignamos el nombre de ella y damos “Finish”.

Seleccion JUnit Suite


La clase generada, tendrá la anotación de @RunWith (Suite.class) y @SuiteClasses ({}) sobre la clase AllTest, sin embargo, para que corra sobre JUnit 5 suite, debemos cambiar las anotaciones anteriores por @RunWith (JUnitPlatform.class) y @SelectClasses ({}).Consecuentemente, añadimos las clases que queremos incluir en la anotación @SuiteClasses, para nuestro caso, BoletinTest y EstudianteTest.

package test;

import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.runner.RunWith;


@RunWith(JUnitPlatform.class)
@SelectClasses({BoletinTest.class, EstudianteTest.class})
public class AllTests {

}


Finalmente solo bastará con ejecutarla y tendremos un resultado exitoso en caso de haber pasado cada uno de las pruebas unitarias a las clases vinculadas, en caso contrario, aparecerá el indicador de color rojo y podremos ver todas las clases de prueba y aquellas que fallaron.

JUnit Suite



Última modificación: miércoles, 16 de marzo de 2022, 13:19