Páginas

20/2/15

JUnit pruebas unitarias parametrizadas. Test con parámetros en JUnit. JUnitParams.

JUnit pruebas unitarias parametrizadas. Test con parámetros en JUnit. JUnitParams.

Test con parámetros en JUnit.




Pruebas parametrizadas en JUnit. JUnitParams. Haciendo pruebas unitarias siempre queremos que las mismas sean lo más fuertes posibles, con los tutoriales de JUnit assertions, assumptions y Hamcrest se pueden hacer, pero falta algo, hacer las pruebas con distintos parámetros y probar ese test con varios casos para asegurarnos de que en realidad el test funciona de manera correcta, para hacer pruebas unitarias parametrizadas en JUnit hay varias maneras, yo en este tutorial voy a poner los más usados y el que recomiendo.

Una prueba unitaria parametrizada no es más que un método de prueba en el cual deben realizarse como si de un bucle for se tratara una prueba por cada grupo de parámetros, esto hace la prueba unitaria mucho mas fuerte ya que así verificamos que en todos los casos posibles pase la prueba. Por ejemplo si no sabemos bien como hacer pruebas parametrizadas podemos utilizar nuestra lógica sencilla y hacer una prueba unitaria parecida a esta, lo cual no está mal pero hay una mejor manera:
 /**
  * Creamos la prueba unitaria con varios parametros de entrada
  * 
  * @param num1
  * @param num2
  * @param resultadoEsperado
  */
 @Test
 public void testMetodoSumar(int num1, int num2, int resultadoEsperado) {
  assertThat(resultadoEsperado, is(sumar(num1, num2)));
 }

 /**
  * Metodo en el cual ejecutamos la prueba unitaria n veces
  */
 @Test
 public void testMetodoParametrizado() {
  // Creamos una lista de numeros enteros
  List lista = new ArrayList<>();

  // Agregamos ciertos numeros
  lista.add(2);
  lista.add(4);
  lista.add(6);

  // Por cada elemento en la lista ejecutamos la prueba unitaria
  for (int num : lista) {
   testMetodoSumar(num, num, (num + num));
  }
 }
Si ejecutamos esa prueba unitaria pasa sin ningún problema y esto es lo que sale en la pantalla de JUnit: Todo bien hasta aquí, pero cuantas veces se ejecuto la prueba unitaria???? No hay ningún tipo de detalle acerca de eso, además de eso que pasa si en la primera ejecución del test falla la prueba unitaria? Seguirá ejecutando los demás ítems hasta que finalice el bucle for??... Bueno para ver qué sucede cambiamos un poco el código en la parte del bucle for por algo como esto:
  for (int num : lista) {
   testMetodoSumar(num, num, (num + num+1));
  }
Y ahora vemos la pantalla de JUnit y nos aparece esto: Como vemos en la primera ejecución de la prueba unitaria falla y ahí finaliza nuestra prueba unitaria, no se ejecuta las demás veces así el bucle for no haya finalizado. Tampoco hay mucho detalle, así que esta manera de hacer pruebas unitarias parametrizadas no es la mejor ni la correcta.

Ya que la manera anterior queda descartada vamos a ver la siguiente, vamos a utilizar la clase Parametized, la cual con simples anotaciones nos permite correr nuestros test fácilmente, para hacerlo vamos a crear una clase nueva llamada SumarTest que obviamente se va encargar de hacerle la prueba unitaria a el método sumar(int a, int b), al crear la clase le ponemos la anotación @RunWith(Parameterized.class), creamos en este caso tres variables de tipo int que representan los números a sumar y el resultado, un constructor de la clase y una lista en la cual pasamos los parámetros con los cuales vamos hacer la prueba unitaria, vamos a la práctica de una:
package datojavaTest;

import static org.junit.Assert.assertThat;

import java.util.ArrayList;
import java.util.Collection;

import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

/**
 * @author datojava.blogspot.com
 */

@RunWith(Parameterized.class)
public class SumarTest extends CoreMatchers {

 private int num1;
 private int num2;
 private int resultado;

 public SumarTest(int x, int y, int z) {
  this.num1 = x;
  this.num2 = y;
  this.resultado = z;
 }

 @Parameters
 public static Collection init() throws Exception {
  Collection parametrosSuma = new ArrayList();

  // Agregamos varios parametros, vamos a provocar la falla del test
  parametrosSuma.add(new Integer[] { 5, 2, 7 });
  parametrosSuma.add(new Integer[] { 5, 4, 5 });
  parametrosSuma.add(new Integer[] { 1, 5, 5 });
  parametrosSuma.add(new Integer[] { 8, 1, 9 });

  return parametrosSuma;
 }

 public int sumar(int num1, int num2) {
  return num1 + num2;
 }

 @Test
 public void test() {
  assertThat("ERROR en la suma: ", this.resultado,
    is(sumar(this.num1, this.num2)));
 }
}
Ok una vez listo voy a explicar un poco como funciona eso de los parámetros:
 @Parameters
 public static Collection init() throws Exception {
  Collection parametrosSuma = new ArrayList();

  // Agregamos varios parametros, vamos a provocar la falla del test
  parametrosSuma.add(new Integer[] { 5, 2, 7 });
  parametrosSuma.add(new Integer[] { 5, 4, 5 });
  parametrosSuma.add(new Integer[] { 1, 5, 5 });
  parametrosSuma.add(new Integer[] { 8, 1, 9 });

  return parametrosSuma;
 }
Vemos que la lista tiene por cada ítem un Array de tipo de datos Integer el cual tiene tres enteros, por cada arreglo esta es la representación:

new Integer[] { 5, 2, 7 }
Primer entero es = num1
Segundo entero es = num2
Tercer entero es = resultado
Esto es lo que estamos representado como operación (num1 + num2 = resultado)
Esta justo en este orden por que en el constructor lo estamos haciendo de esa manera:
 public SumarTest(int x, int y, int z) {
  this.num1 = x;
  this.num2 = y;
  this.resultado = z;
 }


Como vemos el constructor recibe tres parámetros int con las cuales inicializamos las variables num1, num2 y resultado, por ejemplo si digo que num1 = z ya todo cambia porque estamos pasando lo que nosotros estamos diciendo que es el resultado a la variable num1. De este constructor dependen todas los parámetros que vamos a utilizar para la prueba unitaria.
Bueno una vez aclarado el tema de cómo funciona a nivel de código podemos pasar a la práctica, si analizamos el código vemos que falla en la segunda y tercera ronda de prueba porque estamos diciendo que:
(5 + 4 = 5) Falso
(1 + 5 = 5) Falso
Provocamos el error para ver cómo funciona en comparación con el primer ejemplo, esto es lo que sucede: Hay una salida en JUnit mucho más detallada, aparte de eso no importa si la primera ejecución de la prueba unitaria falla el seguirá haciendo pruebas unitarias hasta finalizar todos los parámetros de la lista. Todo muy bonito hasta aquí pero que pasa cuando queremos hacer en la misma clase un test de otro método con otros parámetros? Bueno hasta aquí llega nuestra creatividad porque eso no se puede hacer con la clase Parametized, esta clase tiene ciertas limitaciones, solo puedes crear un constructor de la clase porque si no lanza la excepción java.lang.IllegalArgumentException: Test class can only have one constructor, eso quiere decir que todas las pruebas unitarias van a correr con los mismos parámetros, ósea que para hacer pruebas parametrizadas con la clase Parametized tendrás que crear una clase para cada prueba unitaria que quieras correr con distintos parámetros, una cag...

Ahora te presento otra manera de hacer tus test parametrizados en JUnit con la librería JUnitParams-1.0.2, puedes descargar el .jar aquí, una vez descargado lo incorporas al build path de tu proyecto, esta librería nos permite hacer lo que hemos planteado anteriormente y de una manera mas fácil, en una clase podemos hacer varios test con parámetros diferentes sin necesidad de crear variables y constructores. También trabaja con anotaciones, la clase en donde vamos a implementar las pruebas unitarias tenemos que agregarle esta anotacion @RunWith(JUnitParamsRunner.class), y para utilizar los parámetros solo tenemos que agregar la anotación @Parameters con los respectivos parámetros. Vamos a la práctica de una:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

import java.util.ArrayList;
import java.util.Collection;

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;

import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(JUnitParamsRunner.class)
public class DatoJavaJUnitParams extends CoreMatchers {
 
 public Collection listaInt() {
  Collection datos = new ArrayList();
  datos.add(new Integer[] { 5, 2, 7 });
  datos.add(new Integer[] { 5, 4, 9 });
  datos.add(new Integer[] { 1, 5, 6 });
  datos.add(new Integer[] { 8, 1, 9 });

  return datos;
 }
 
 public int sumar(int num1, int num2) {
  return num1 + num2;
 }

 @Test
 @Parameters({ "1, 3, 4", "5, 5, 10", "6, 8, 14", "98, 1, 99" })
 public void testSuma(int x, int y, int e) {
  assertThat("ERROR, no es el resultado esperado", e, is(sumar(x, y)));
 }

 @Test
 @Parameters({ "16, 15, 31", "52, 6, 55", "45, 48, 93", "12, 65, 67", "27, 63, 90" })
 public void testSuma1(int x, int y, int e) {
  assertEquals("ERROR, no es el resultado esperado", e, sumar(x, y));
 }
 
 @Test
 @Parameters(method= "listaInt")
 public void testSuma2(int x, int y, int e) {
  assertEquals("ERROR, no es el resultado esperado", e, sumar(x, y));
 }
}
Si analizamos el código del ejemplo anterior vemos que podemos hacer varias pruebas unitarias con distintos parámetros y de distintas formas, la prueba unitaria testSuma2(int x, int y, int e) es distinta a las demás, de hecho al principio del codigo hemos creado un método que se llama listaInt() en el que llenamos una lista con distintos parámetros, ese método lo utilizamos como parámetros de prueba con la anotación @Parameters(method= "listaInt"), Algo que es bastante util a la hora de hacer pruebas unitarias con los mismo parámetros. Esta es la salida en JUnit de esta clase: En mi opinión esta es la mejor manera de hacer pruebas parametrizadas en JUnit, utilizando la librería JUnitParams. Pon en práctica lo aprendido y si tienes alguna duda o comentario no te olvides de decírmelo por aquí...

No hay comentarios :

Publicar un comentario