Este documento presenta una introducción a los tests unitarios con JUnit 4. Explica conceptos como las clases de test, métodos de test, afirmaciones, métodos de configuración, runners de test y buenas prácticas. También incluye un ejemplo de cómo escribir tests unitarios para probar una clase Calculadora de Impuestos que implementa diferentes reglas de negocio.
2. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Agenda
● Tipos de Tests
● Introducción a JUnit
● Test Class
● Test Methods
● Condiciones de aceptación Assert
● Fixture Methods
● Test Suites
● Tests de Timeout
● Tests de manejo de excepciones
● Test Runners
● Tests Parametrizados
● AssertThat y Hamcrest
● Buenas Prácticas
● Tipos de validación
3. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tipos de Tests (1/3)
● Clasificación por Nivel
○ Tests Unitarios.
○ Tests de Integración.
○ Tests de Sistema.
○ Tests de Integración de Sistemas.
● Pruebas Funcionales
○ Tests Funcionales.
○ Tests de Aceptación.
○ Tests de Regresión.
○ Alpha testing.
○ Beta testing.
○ ...
4. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tipos de Tests (2/3)
● Pruebas No-funcionales
○ Tests de Rendimiento
○ Tests de Resistencia, Carga, Stress
○ Tests de Seguridad
○ Tests de Usabilidad
○ Tests de Mantenibilidad
○ ...
5. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tipos de Tests (3/3)
● Clasificación típica en un entorno TDD.
Desarrollador Dueño del Producto
Tests
Unitarios Tests de
Aceptación
Tests
Funcionales
Tests de Sistema
Tests de
Integración
6. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tests Unitarios (1/3)
Los tests unitarios son una forma de probar el correcto
funcionamiento de un módulo o una parte del sistema.
Con el fin de asegurar el correcto funcionamiento de todos
los módulos por separado y evitar así errores futuros en el
momento de la integración de todas sus partes.
La idea es escribir casos de prueba para cada función no
trivial o método en el módulo, de forma que cada caso sea
independiente del resto.
7. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tests Unitarios (2/3)
Ventajas:
1. Fomentan el cambio.
2. Simplifica la integración.
3. Documentan el código.
4. Separación de la interfaz y la implementación.
5. Los errores quedan más acotados y son más
fáciles de localizar.
8. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tests Unitarios (3/3)
● Un buen test unitario tiene que ser repetible y
debe poder ejecutarse de forma automática.
● Las Pruebas Automáticas reducen el riesgo y
ahorran el coste de repetir los test
manualmente.
● Los equipos ágiles, las utilizan como ayuda
para responder al cambio de forma rápida y a
un coste eficiente.
9. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Introducción a JUnit (1/3)
● JUnit es una librería Java que nos
ayudará en la realización de nuestros
tests automáticos de prueba.
● Creado por Erich Gamma y Kent Beck, es
uno de los frameworks de tests unitarios
conocidos colectivamente como xUnit,
originados por SUnit.
○ C (CUnit) ○ Javascript (JSUnit)
○ C++ (CPPUnit) ○ Objective-C (OCUnit)
○ Fortran (fUnit) ○ PHP (PHPUnit)
○ Delphi (DUnit) ○ ...
10. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Introducción a JUnit (2/3)
● JUnit proporciona:
○ Afirmaciones para verificar resultados.
○ Anotaciones para definir métodos de prueba.
○ Anotaciones para definir métodos adicionales pre y
post prueba.
○ Controlar excepciones, tiempos de espera.
○ Parametrización de datos.
○ Diferentes Runners para guiar la ejecución de las
pruebas.
○ ...
11. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Introducción a JUnit (3/3)
● Los conceptos básicos de JUnit son:
○ Test class
○ Test method
● Assert
○ Fixture method
○ Test suite
○ Test runner
12. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Test Class
Una Test Class es una clase java que contendrá un conjunto
de Test Methods.
Por ejemplo, para probar los métodos de una clase
Calculadora creamos la clase CalculadoraTest.
import org.junit.Test;
public class CalculadoraTest {
@Test
public void testXXX() {
...
}
}
13. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Test Methods (1/2)
Los métodos de prueba deben indicarse con la anotación
@Test.
Tienen que ser públicos, sin parámetros y devolver void.
import org.junit.Test;
public class CalculadoraTest {
@Test
public void testSuma() {
Calculadora calculadora = new CalculadoraImpl();
double res = calculadora.suma(1, 1);
assertEquals(2, res, 0);
}
}
JUnit crea una nueva instancia de la test class antes de
invocar a cada @Test method.
14. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Test Methods (2/2)
Los métodos de test los creamos siguiendo el patrón AAA:
1. Arrange (Preparar)
2. Act (Actuar)
3. Assert (Afirmar)
@Test
public void testSuma() {
// Arrange
Calculadora calculadora = new CalculadoraImpl();
// Act
double res = calculadora.suma(1, 1);
// Assert
assertEquals(2, res, 0);
}
15. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Condiciones de aceptación Assert
Para realizar validaciones en nuestros métodos de prueba,
utilizamos las condiciones de aceptación assertXXX(), que
proporciona la clase Assert de JUnit.
Ejemplos:
assertEquals("message", A, B) Validar la igualdad de los objetos A y B,
utiliza el método equals().
assertSame("message", A, B) Validar que A y B son el mismo objeto,
utiliza el operador ==.
assertTrue("message", A) Validar que la condición A es true.
assertNotNull("message", A) Validar que el objeto A no es nulo.
16. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Fixture Methods
Las clases de tests tienen que programarse eficientemente
y refactorizarse cuando sea necesario.
JUnit nos proporciona anotaciones para definir métodos
adicionales pre/post test. Mediante los que podemos
inicializar o finalizar elementos comunes, evitar
duplicidades, preparar datos, etc.
public class CalculadoraTest {
Calculadora calculadora;
@BeforeClass @Before
@Before public void inicializa() {
calculadora = new CalculadoraImpl();
}
@After
@Test
@AfterClass public void testSuma() {
double res = calculadora.suma(1, 1);
assertEquals(2, res, 0);
}
}
17. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tests de Timeout
Podemos realizar sencillos test de rendimiento verificando
que un test no exceda un tiempo límite de ejecución.
Para ello utilizamos el parámetro timeout.
@Test(timeout=100)
public void testProcessTimeout() {
...
}
El test fallará, si se demora más de 100 milisegundos.
18. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tests de manejo de excepciones
Si estamos probando un método que puede lanzar
excepciones, nos es de interés poder verificar, que para las
condiciones oportunas, nuestro código lanza la excepción
esperada.
Para ello utilizamos el parámetro expected.
@Test(expected=IllegalArgumentExcepcion.class)
public void testSumaGetIllegalArgumentException() {
...
}
El test fallará, si no se produce la excepción
IllegalArgumentException.
19. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Ignorar un Test
JUnit proporciona una anotación para indicar que un
determinado test no queremos que se ejecute.
@Ignore(value="Mensaje")
@Test
public void testXXX() {
...
}
Evitar un test no es una buena práctica, pero si en alguna
ocasión nos es necesario es mejor utilizar la anotación
@Ignore a simplemente comentar código.
Es importante indicar siempre una razon por la que
ignoramos un test con el parámetro value.
20. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tests Runners
JUnit permite definir cómo se han de ejecutar nuestros
tests, mediante el uso de diferentes Tests Runners.
org.junit.runners.JUnit4 Runner por defecto.
org.junit.runners.Parameterized Ejecuta múltiples veces cada test con
diferentes parámetros.
org.junit.runners.Suite Runner de los Test Suite
org.springframework.test. Runner de Spring para que los tests
context.junit4. puedan trabajar con el contenedor IoC.
SpringJUnit4ClassRunner
Utilizamos la anotación @RunWith
@RunWith(value=Parameterized.class)
public class CalculadoraImplParameterizedTest {
21. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tests Parametrizados (1/2)
Parameterized es un test runner que nos permite ejecutar un
test múltiples veces con diferentes juegos de parámetros.
@RunWith(value=Parameterized.class)
public class CalculadoraImplParameterizedTest {
private double esperado, num1, num2;
@Parameters
public static Collection<Integer[]> gestTestParameters(){
return Arrays.asList(new Integer[][] {
{2, 1, 1}, {3, 2, 1}, {4, 3 ,1},
});
}
public CalculadoraImplParameterizedTest(double esperado, double num1, double num2) {
this.esperado = esperado;
this.num1 = num1;
this.num2 = num2;
}
@Test
public void testSuma() {
CalculadoraImpl calculadora = new CalculadoraImpl();
double calculado = calculadora.suma(num1, num2);
assertEquals(esperado, calculado, 0);
}
}
22. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tests Parametrizados (2/2)
23. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
AssertThat y Hamcrest (1/2)
La librería de matchers Hamcrest nos permite escribir
validaciones de forma más clara y fácil.
@Test
public void testSumaHamcrest() {
CalculadoraImpl calculadora = new CalculadoraImpl();
double calculado = calculadora.suma(num1, num2);
assertThat(esperado, is(calculado));
}
Además, con Hamcrest, los errores en los assert son más
descriptivos.
24. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
AssertThat y Hamcrest (2/2)
Matches Hamcrest mas comunes:
● anything
● is
● allOf
● anyOf
● not
● instanceOf, isCompatibleType
● sameInstance
● notNullValue, nullValue
● hasProperty
● hasEntry, hasKey, hasValue
● hasItem, hasItems
● closeTo, greaterThan, lessThan
● containsString, endsWith, startWith
● ...
25. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Buenas Prácticas
● Un test unitario debe probar exclusivamente el SUT.
● Un test unitario no puede modificar el estado del
sistema.
● Un test unitario tiene que ser pequeño, debe probar una
sola funcionalidad, para un único objeto cada vez.
● Los nombres de los métodos de test deben ser los más
claros posibles. No importa que utilicemos nombres
largos.
● Indicar un mensaje en todas las llamadas assert.
● Deja que los tests mejoren el código.
26. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Tipos de validación
● Recapitulando, observamos que en nuestros tests
unitarios realizamos diferentes tipos de comprobaciones:
○ Validación de estado
assertEquals ( valorEsperado, valorCalculado )
○ Validación excepciones
@Test ( expected=IllegalArgumentException.class )
○ Validación rendimiento
@Test ( timeout=100 )
○ Validación interacción
Utilizaremos Dobles (Mocks, Stubs, etc)
27. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Caso de ejemplo (1/5)
● Una Calculadora de Impuestos.
public interface CalculadoraImpuestos {
public double calculaImpuestoMatriculacion(double valorVehiculo, int cv);
}
public class CalculadoraImpuestosImpl implements CalculadoraImpuestos {
public double calculaImpuestoMatriculacion(double valorVehiculo, int cv) {
...
}
}
28. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Caso de ejemplo (2/5)
● Creamos la clase que contendrá las pruebas.
import static org.junit.Assert.*;
import org.junit.Test;
public class CalculadoraImpuestosImplTest {
@Test
public void test() {
fail("Not yet implemented");
}
}
29. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Caso de ejemplo (3/5)
● 1ª Regla de negocio:
Vehículos con menos de 70CV impuesto 9%
● Primer test:
@Test
public void testUsoImpuestoEcologicoParaCvMenor70() {
CalculadoraImpuestosImpl calculadora = new CalculadoraImpuestosImpl();
double valorVehiculo = 10000;
int cv = 65;
double impuestoEsperado = 900;
double impuestoCalculado = calculadora.calculaImpuestoMatriculacion(valorVehiculo, cv);
assertEquals("Impuesto para vehiculos de menos de 70 CV debería ser del 9%",
impuestoEsperado, impuestoCalculado, 0);
}
30. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Caso de ejemplo (4/5)
● 2ª Regla de negocio:
Un vehículo no puede tener 0CV
● Segundo test:
@Test(expected=IllegalArgumentException.class)
public void test0CvGetIllegalArgumentException() {
CalculadoraImpuestosImpl calculadora = new CalculadoraImpuestosImpl();
calculadora.calculaImpuestoMatriculacion(100.0, 0);
}
31. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Caso de ejemplo (5/5)
● Podemos refactorizar la clase de test para evitar
duplicidad de código.
public class CalculadoraImpuestosImplTest {
private CalculadoraImpuestosImpl calculadora;
@Before
public void inicializa() {
calculadora = new CalculadoraImpuestosImpl();
}
@After
public void finaliza() {
calculadora = null;
}
@Test
public void testUsoImpuestoEcologicoParaCvMenor70() {
double valorVehiculo = 10000;
int cv = 65;
double impuestoEsperado = 900;
double impuestoCalculado = calculadora.calculaImpuestoMatriculacion(valorVehiculo, cv);
assertEquals("Impuesto para vehiculos de menos de 70 CV debería ser del 9%" impuestoEsperado,
,
impuestoCalculado, 0);
}
@Test(expected=IllegalArgumentException.class)
public void test0CvGetIllegalArgumentException() {
calculadora.calculaImpuestoMatriculacion(100.0, 0);
}
}
32. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Temas para otro día ...
● Dobles de Prueba (Mocks, Stubs)
● Tests de Integración con JUnit
● La extensión DBUnit
● TDD - Desarrollo Dirigido por Tests
33. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Recursos
● JUnit Framework
● Hamcrest
● EasyMock
● Mockito
34. Tests Unitarios con JUnit 4
Paulo Clavijo Esteban - Mayo 2012
Referencias
Libros:
● JUnit In Action (2º Edition) - Manning.
● Java Power Tools - John Ferguson.
● Diseño Ágil Con TDD - Carlos Blé.
Web:
● JUnit Documentacion
● The Hamcrest Tutorial
35. Sobre el Autor:
Paulo Clavijo Esteban
Software Engineer en ISC
clavijo_pau@ingenieriadesoftware.com
Blog: www.lintips.com
Email: paucls@gmail.com