Justificación: ¿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.

  • …​ pero es muy conveniente para facilitar y acelerar su gestión

Ejemplos sin Junit

Definición: ¿Qué?

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

    • Es una instancia de XUnit, arquitectura para framework de pruebas unitarias: CppUnit, PhpUnit,…​

    • Basado en: Metaclases, Anotaciones y Aserciones

    • Incluye:

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

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

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

Objetivos: ¿Para qué?

  • Gestionar eficientemente el código de pruebas unitarias, de componentes, integración y sistemas para estilos arquitectónicos como MVP-PM

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

    • Facilitar la legibilidad, escritura/lectura, con funciones estáticas especializadas para 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

Descripción: ¿Cómo?

Pruebas Unitarias

Clases de Pruebas

  • Las Clases de Pruebas son clases normales (POJO) 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"

Ejemplos: patrón Factory Method en pruebas
  • La Orgranización de Clases de Pruebas mantiene el Código de Producción separado del Código de Pruebas

para el Código de Producción con un Jerarquia de Paquetes de Paquetes de la Arquitectura del Software

el Código de Pruebas Unitarias con una Jerarquia de Paquetes paralela a la Arquitectura del Software del Código de Producción

  • src/main/java

    • xxx.yyy.zzz.A

    • xxx.yyy.zzz.B

    • …​

  • src/test/java

    • xxx.yyy.zzz.ATest

    • xxx.yyy.zzz.BTest

    • …​

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 las 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

Ejemplos

Método de Pruebas

Declaración Ejecución
@Test (1)
public void test <nombre>() { (2)
    ...
}
1 decorado con la anotación @Test
2 siempre público sin parametros y no devuelve nada
  • el nombre debe determinar de forma inequívoca lo que se está probando del SUT, alcanzando la longtitud que sea necesaria comenzando por el prefijo "test".

    • Por ejemplo, testLenght.

    • En caso de colisión por sobrecarga del método del SUT, añadir al nombre los nombres de los parámetros que los distinguen.

      • Por ejemplo, testIncludesInt y testIncludesClosedInterval.

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

    • 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

Cuerpo del Método de Prueba Una triple A (arrange/act/assert) y una parte opcional final:

Preparacion (Arrange/Seup)

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

Acción (Act)

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

Aserción (Assert)

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

Demolición (TearDown)

en caso necesarío, para liberar los recursos que fueron necesarios y devolverlos al estado anterior faciitando la idependencia de otros Métodos de Pruebas poder reutilizarlos en otro Metodos de Pruebas evitando su continua re-creación(no recomendado!!!)

Ejemplos:

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) 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) 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

Ejemplos:

Aserciones Avanzadas

Generación Descripción
  • 1ª Generación

  • Del lenguaje de programación Java

assert <expresión>;
  • 2ª Generación

  • Del framework JUnit (Baeldug, buen tutor!)

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

    • aceptan como argumentos, el esperado según los requisitos y el actual obtenido en el ejercicio del SUT, todos los tipos primitivos, Objects y arrays de éstos.

    • tienen un parámetro opcional inicial de tipo String para especificar un mensaje:

assertEquals(<message>, <expected>, <actual>);
  • 3ª Generación

  • Del framework Hamcrest (Baeldug, buen tutor!)

    • Es un 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 …​ implantación sin métodos static y con métodos static

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

assertThat(<expected>, Matcher matcher);
assertThat(Matcher matcher, <actual>);
- Objetivos de Hamcrest
  • 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")));
Ejemplos: auxiliar.junit.AssertThatTest

Exepciones del SUT

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

Clase de Pruebas Parametrizada

  • 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.

Ejemplos:

Repitiendo código

Sin repetir código

Parametrizada

Parametrizada sin Constructor

Expiración del Tiempo de Ejecución

  • Para Pruebas de Rendimiento de los Requisitos no funcionales.

    • Por ejemplo, el cálculo de la existencia de las Tres en Raya no puede exceder los 5msg

    • 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

Ejemplos:

Conjuntos de Pruebas

Jerarquías de Pruebas

  • 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)
  <Clase1Test>.class, (3)
  <Clase2Test>.class, (3)
  <ConjuntoPruebas3Test>.class, (3)
  <ConjuntoPruebas4Test>.class (3)
})
class <ConjuntoDePruebas>Test{}
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 poder configurar sub-conjuntos de pruebas como en el caso de pruebas alfa, beta, humo,…​

  • 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 {}
  • 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

public class XTest {

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

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

}
Ejemplos: src.test.java.auxiliar.junit.categories

Pruebas ignoradas

  • Mediante la anotación @Ignored acompañando al Metodo de Prueba, el framework no ejecutará dicho caso de prueba

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

Elemplos:

Pruebas de Integración

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

Ejemplos: integration.DecisionTreeBroadTest
  • 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 BufferedReader, FileReader y IOException y, asi convertirla en Prueba Unitaria/de Componente

Pruebas de Sistema

  • Arquitectura Modelo/VistaPresentador 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

height32

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

Metaclases

  • Es una clase cuyas instancias son clases que permiten la reflexión del código: un código que manipula como información paquetes,clases,método,atributos,objetos y mensajes

    • Ejemplo: auxiliar.withoutJUnit.MetaClazzesExample

    • Ejemplo: auxiliar.withoutJUnit.MetaClazzesInspectorExample

height32

Anotaciones

  • Anotación es un mecanismo para añadir metadatos al código fuente Java que están disponibles en tiempo de compilación/ejecución.

    • Ejemplos:

      • [lime]#@Override,@SupressWarning#_…​para el IDE

      • [lime]#@Entity,@Key#_,…​para JPA de Swing

        • Ejemplos: auxiliar.withoutJUnit.AnnotationExample

        • Ejemplo: auxiliar.withoutJUnit.ClazzExample

        • Ejemplo: auxiliar.withoutJUnit.MetaClazzesWihtAnnotationsExample

Aserciones

  • Todas las funciones tienen un semántica similar al assert:

    • se comprueba una condición,si es cierta continúa la ejecución.

    • En caso contrario,la prueba no pasa y se continúa la ejecución por otro método de prueba de la clase

height32