sintesis

JUnit

Códigos disponibles en git

¿Por qué?

  • No es necesario ningún framework para la gestión de pruebas automáticas:

    • Instanciar objetos que contengan el SUT

    • Enviar mensajes para ejecutar el SUT

    • Elevar una exepción si se verifica que la respuesta del SUT no es igual a la esperada

¿Qué?

  • Framework sencillo desarrollado por Kent Beck y Erich Gamma para escribir pruebas repetibles.

  • Incluye:

    • Visualizadores de resultados, runners, en modo texto, gráfico …​

    • Plugins para principales IDEs: Eclipse, NetBeans, VisualStudio, IntelIJ, …​

    • Integración con gestores de proyectos: Ant, Maven, Gradle, …​

¿Para qué?

Gestionar eficientemente el código de pruebas unitarias, de componentes, integración y sistemas

  • Facilitar la reusabilidad específica del código de pruebas

  • Facilitar la legibilidad, escritura/lectura, con funciones estáticas especializadas, frente a la sentencia assert original del lenguaje

  • Agrupar jerarquias de Conjuntos de Clases de Pruebas (@TestSuite) que automatiza la ejecución de la totalidad de las Pruebas de Regresión

  • Categorizar de Clases de Pruebas (@Categories) que automatiza la ejecución de subconjuntos de la totalidad de los Casos de Prueba, para las pruebas Alfa, Beta o Humo

¿Cómo?

Pruebas Unitarias

Clases de Pruebas

  • Las Clases de Pruebas son clases normales del lenguaje con atributos, métodos públicos y privados, miembros estáticos…​ relacionadas con otra clases como las del SUT que se ejercite, auxiliares (Factory, Builder,…​) sin ninguna limitación excepto que su nombre debe terminar con el sufijo "Test"

    • XTest con la responsabilidad de probar cierta clase X

La Orgranización de Clases de Pruebas mantiene el Código de Producción separado físicamente en distintas carpetas del Código de Pruebas

  • src/main/java

    • xxx.yyy.zzz.A.java

    • xxx.yyy.zzz.B.java

    • …​

  • src/test/java

    • xxx.yyy.zzz.ATest.java

    • xxx.yyy.zzz.BTest.java

    • …​

La Orgranización de Clases de Pruebas mantiene el Código de Producción lógicamente junto en el mismo paquete al Código de Pruebas

  • src/main/resources

  • src/test/resources

  • para los Recursos de Producción: imágenes de UI, ficheros de configuración,…​ con la estructura que se considere más adecuada

  • para los Recursos de Pruebas: ficheros de datos para alimentar a la ejecución de pruebas,…​ con una Jerarquia de Paquetes paralela a la jerarquia Recursos de Producción para facilitar biunivocamente la localización de recursos de una prueba dada

Método de Pruebas

Cabecera

@Test (1)
public void test <nombre>() { (2)
    ...
}
1 decorado con la anotación @Test
2 siempre público sin parametros y ni retornos
Nombrado de métodos de test

testNames

Cuerpo

Parte Triple A BDD Objetivo

Preparacion

Arrange/ Setup

Given

para la creación y relación de los objetos del SUT, DOC, gestión de recursos (ficheros de datos, bases de datos,…​),…​

Acción

Act

When

para el ejercicio del SUT por el objetivo establecido en la prueba

Aserción

Assert

Then

para la comprobación de que el resultado esperado coincida con el resultado obtenido

Demolición

TearDown, no recomendado!!! …​ excepto en pruebas de integración de la base de datos, …​!!!

en caso necesarío, para liberar los recursos que fueron necesarios y devolverlos al estado anterior habilitando la independencia de otros Métodos de Pruebas que comparten dichos recursos y así reutilizarlos evitando su continua re-creación

Ejemplos:

Ejecución

  • Cuando se ejecuta una Clase de Pruebas, por cada Método de Prueba (@Test), en un orden" desconocido"

cicloVidaTest
  • se crea un objeto de la Clase de Pruebas, a través de reflexion, metaclases

  • se le pasa el mensaje correspondiente al Metodo de Prueba

  • Cuando ocurre un fallo en una aserción en el código de pruebas, se aborta su ejecucion

  • Cuando ocurre un error en una sentencia del código de pruebas o del código de producción, si no se captura especificamente con la sentencia try/catch/finally, se aborta su ejecución

  • En cualquera de los dos casos anteriores y cuando la prueba pasa, se continúa la ejecución con otro Método de Pruebas sobre otro objeto de la Clase de Pruebas

Reusabilidad

  • Para ejecutar automáticamente el código común de los Métodos de Prueba, a través de anotaciones en métodos:

    • @Before, para la preparación (Arrange/Given) de un conjunto igual de SUT, accesorios, recursos, …​

    • @After, para la demolición (TearDown) de un conjunto igual de SUT, accesorios, recursos, …​

    • Ambos son de instancia sin parámetros y no devuelven nada

  • Para ejecutar automáticamente el código previo y posterior a la ejecución de todos los Metodos de Pruebas:

    • @Beforeclass, para la preparación(Arrange/Given) del mismo conjunto de SUT, accesorios, recursos, …​

    • @AfterClass, para la demolición (TearDown) del mismo conjunto de SUT, accesorios, recursos, …​

    • Ambos son estáticos sin parámetros y no devuelven nada

Aserciones Avanzadas

assert <expresión>;
  • 1ª Generación para producción

    • - Sentencia del lenguaje de programación Java:

  • 2ª Generación

    • Del (framework JUnit), todas las aserciones:

      • funciones estáticas que aceptan como argumentos los valores esperados según los requisitos y el actual obtenido en el ejercicio del SUT, para todos los tipos primitivos, Objects y arrays de éstos.

      • tienen un parámetro opcional inicial de tipo String para especificar un mensaje en el informe de pruebas

assertEquals(<expected>, <actual>);
assertArrayEquals(<expected>, <actual>);
assertSame(<expected>, <actual>);
assertNotSame(<expected>, <actual>);
assertNull(<actual>);
assertNotNull(<actual>);
assertTrue(<actual>);
assertFalse(<actual>);
fail();

assertEquals(<message>, <expected>, <actual>);
assertNull(<message>, <actual>);
fail(<message>);
assertThat(<expected>, Matcher matcher);
assertThat(Matcher matcher, <actual>);
  • 3ª Generación

    • Del (framework Hamcrest)

      • Matcher, el "Emparejador", es la clase padre de la jerarquía del lenguaje de consulta: is, hasProperty, samePropertyValuesAs, empty, equalTo, arrayWithSize, …​

Objetivos de Hamcrest: ejemplos

Aumentar la legibilidad

assertEquals(expected,actual)
assertThat(actual,is(equalTo(expected)));

Mejorar los mensajes

assertTrue(expected.contains(actual));
assertThat(actual,containsString(expected));

Imponer seguridad de tipos

assertEquals("abc",123);
assertThat(123,is("abc"));

Aumentar la flexibilidad

assertTrue("test".contains("x")
  && "test".contains("y"));
assertThat("test", allOf(
  contantsString("x"),
  containsString("y")));
  • Ejemplo paradigmático del patrón Intérprete: dado un lenguaje, define una representación para su gramática junto con un intérprete del lenguaje que utiliza la representación para interpretar sentencias del lenguaje …​ sin métodos static y con métodos static

Exepciones del SUT

@Test
public void testX() {
    try{
        //arrange
        //act with XExcepcion
        fail(); (1)
    } catch(XExcepcion e) { (2)
    } catch (Excepcion e) {
        fail(); (1)
    }
}
1 pero fallando, con fail() o new Exception(), en ausencia de la excepcion o si fuera de otro tipo
2 Tras el ejercicio del SUT que eleva la excepción, capturar la exepción esperada sin ninguna acción
@Test(expected = XException) (1)
public void testX() {
    //arrange
    //act wihtXEception
}
1 Especificando la clase de excepción esperada en su atributo expected en la anotación @Test

Clase de Pruebas Parametrizada

Justificación Objetivo Versión Coordinate ClosedInterval
  • En muchas clases de pruebas se escribe y se reescribe la creación de muchos objetos del SUT para ser ejercitados en distintos métodos de pruebas

  • Las Clases de Pruebas Parametrizadas buscan reutilizar los mismos accesorios del SUT escritos en todos los métodos de pruebas de la clase para cada elemento de una colección de accesorios del SUT definida separada y extensible.

Repitiendo código

Sin repetir código

Parametrizada

Parametrizada sin Constructor

Conjuntos de Pruebas

Jerarquías de Pruebas

Conjunto de pruebas Patrón Composite
  • Un Conjunto de Pruebas es una clase que permite la ejecucion de un conjunto de Clases de Pruebas, mediante el patrón Composite donde las hojas son los TestCase y los compuestos son TestSuite

    • Regla de Estilo:

      • incorpora una Conjunto de Pruebas denominado AllTest en cada paquete de clases de prueba, incluyendo a todas las Clases de Pruebas Unitarias del paquete y

      • los Conjuntos de Pruebas AllTest de todos subdirectorios, de tal manera que src/test/java/<proyecto>/AllTest dispare la ejecucion de todas las pruebas acumuladas (Pruebas de Regreción)

compositeTest
@RunWith(Suite.Class) (1)
@SuiteClasses({ (2)
  <ClaseATest>.class, (3)
  <ClaseBTest>.class, (3)
  <PackageX.AllTest>.class, (3)
  <PackageY.AllTest>.class (3)
})
class AllTest{}
1 Anotación @RunWith(Suite.class) para indicar que es un Conjunto de Pruebas
2 Anotación @SuiteClasses cuyo argumento define los contenidos del Conjunto de Pruebas
3 Clase de Pruebas, TestCase o TestSuite, añadidas al Conjunto de Pruebas

Categorías de Pruebas

Para configurar sub-conjuntos de pruebas como en el caso de pruebas alfa, beta, humo,…​ ejemplo

  • Definir una interfaz vacía por cada categoria

public interface SlowTest {}
  • Decorar el Conjunto de Pruebas a configurar con anotaciones:

    • @RunWith(Categories.class)

    • @IncludeCategory ({<Categoría1>.class [, <Categoria2>.class …​]}) y/o

    • @ExcludeCategory ({<Categoría1>.class [, <Categoría2>.class …​]})

    • cuyo argumento, en ambos casos, es la lista de clases de categoría incluidas y excluidas respectivamente

    • el framework ejecutará todos los métodos de prueba de las categorías especificadas como incluidas que no sean de las categorías excluidas

@RunWith(Categories.class)
@IncludeCategory
 (SlowTest.class)
@SuiteClasses({
    AllTests.class })
public class SlowSuiteTest {}
public class XTest {

 @Test
 @Category({SmokeTest.class, SlowTest.class})
 public void testOne() {
  System.out.println("testOne of XTest of pPackage.");
  System.out.println("SmokeTest!!! SlowTest!!!");
 }

 @Test
 public void testTwo() {
  System.out.println("testTwo of XTest of pPackage");
 }

}
  • Decorar los metodos de Prueba seleccionados de cada categoría con la anotación

    • @Category({<Categoría1>.class [, <Categoria2>.class, …​]})

    • cuyo argumento es la lista de clases de categoría a las que pertenece el método

Pruebas ignoradas

  • Mediante la anotación @Ignored acompañando al Metodo de Prueba o a las Clase de Prueba

    • el framework no ejecutará dicho caso de prueba

@Ignore
public class IgnoredTest {

@Ignore("Mensaje alternativo con justificación") @Test
public void testOne() {
        System.out.println("IgnoredTest");
}

@Test
public void testTwo() {
        System.out.println("No!!! IgnoredTest");
}

}

Expiración del Tiempo de Ejecución

  • Para Pruebas de Rendimiento de los Requisitos no funcionales, la anotación @Test incorpora el atributo timeout inicializado con el valor del tiempo máximo en milisegundos que se concede al método para su ejecución.

    • Una vez expirado el tiempo , la prueba falla.

    • En caso contrario, dependerá del código de la prueba

Aplicaciones

Pruebas de Componentes

¿Todas las Clases de Pruebas mostradas anteriormente son unitarias? Existe un debate:

  • No! Estrictamente son aquellas que NO incorporan más de una clase en el SUT, sin ejercitar DOC´s

    • Por ejemplo: Coordinate Test no porque incorpora el enumerado Direction; BoardTest no porque incorpora objetos de la clase Coordinate; …​ sin dobles, se ejercita más de una clase!!!

    • Entonces deberían ser consideradas como Pruebas de Componentes…​casi todas! O se ponen dobles!

  • Si! Relajadamente porque no salen del ámbito del componente: no acceden a clases de otros paquetes, no son de componentes

    • Entonces deberían ser consideradas como Pruebas de Unidad! No existen Pruebas de Componentes!

  • Si! Relajadamente porque no salen del código de producción: no acceden a recursos externos (por ejemplo, ficheros, base de datos, servicios,…​) a través de otros componentes software (por ejemplo, librerías, protocolos,…​), no son de integración

    • Entonces no existen Pruebas de Componentes! Se consideran todas unitarias!

  • Solución "practica": muchos desarrolladores no contemplan la existencia de Pruebas de Componentes especificamente y las consideran junto con las Pruebas Unitarias sin distinción

Pruebas de Integración

  • Serán aquellas que en el ejercicio del SUT se colabora con un DOC desarrollado en otro componente: jar de una libreria, un servicio de bases de datos, …​

  • Si no interesa que sea una Prueba de Integración(p.e. para no decelerar las Pruebas de Regresión), habría que incorporar un doble para el DOC (p.e. BufferedReader y FileReader y, asi convertirla en Prueba Unitaria/de Componente

Pruebas de Sistema

MasterMind
  • Arquitectura Modelo/Vista/Presentador con Presentador de Modelo:

    • Modelo (Model) con la responsabilidad de manejar los datos y la funcionalidad del negocio

    • Presentador(Presenter) con la responsabilidad total del estado y lógica de la presentación y solicitar funcionalidades de los modelos (Plain Old Java Object, POJO)

      • Pruebas de los Presentadores ejercitan el sistema un ~9X%

    • Vista (View) con la responsabilidad de manejar los controles de interfaz, la sincronización de la presentación pero eliminando el estado y la lógica de la presentación Humple View pattern

  • Ejemplo:

Sintesis

sintesis

Bibliografía

Obra, Autor y Edición Portada Obra, Autor y Edición Portada
  • xUnit Test Patterns: Refactoring Test Code

    • Gerard Meszaros

    • Addison-Wesley Educational Publishers Inc; Edición: 01 (21 de mayo de 2007)

xUnitTestPatternsRefactoringTestCode

  • Effective Unit Testing

    • Lasse Koskela

    • Manning Publications Company; Edición: 1 (16 de febrero de 2013)

EffectiveUnitTesting

  • The Art of Software Testing

    • Glenford J. Myers

    • John Wiley & Sons Inc; Edición: 3. Auflage (16 de diciembre de 2011)

TheArtofSoftwareTesting

  • Pragmatic Unit Testing in Java with JUnit

    • Andy Hunt, Dave Thomas

    • The Pragmatic Programmers (1674)

PragmaticUnitTestinginJavawithJUnit

  • Testing Computer Software

    • Cem Kaner,Jack Falk,Hung Q. Nguyen

    • Wiley John + Sons; Edición: 2nd (12 de abril de 1999)

TestingComputerSoftware

  • Diseno Agil con TDD

    • Ble Jurado, Carlos

    • Lulu.com (18 de enero de 2010)

DisenoAgilconTDD

  • Test Driven Development

    • Kent Beck

    • Addison Wesley; Edición: 01 (8 de noviembre de 2002)

TestDrivenDevelopment

  • Growing Object-Oriented Software, Guided by Tests

    • Steve Freeman

    • Addison-Wesley Professional (12 de octubre de 2009)

GrowingObjectOrientedSoftware,GuidedbyTests

  • How Google Tests Software

    • James A. Whittaker,Jason Arbon,Jeff Carollo

    • Addison-Wesley Educational Publishers Inc; Edición: 01 (2 de abril de 2012)

HowGoogleTestsSoftware

  • Pragmatic Project Automation: How to Build, Deploy, and Monitor Java Apps

    • Mike Clark

    • The Pragmatic Programmers (1900)

PragmaticProjectAutomation

Ponente

  • Luis Fernández Muñoz

setillo

  • Doctor en Inteligencia Artificial por la UPM

  • Ingeniero en Informática por la UMA

  • Diplomado en Informática por la UPM

  • Profesor Titular de ETSISI de la UPM