Mostrando entradas con la etiqueta testing. Mostrar todas las entradas
Mostrando entradas con la etiqueta testing. Mostrar todas las entradas

sábado, septiembre 29, 2018

Autotools, tutorial de uso (VIII): Añadir soporte para ejecutar tests

Introducción

Test-driven development (TDD) es una técnica de desarrollo de software consistente en escribir primero las pruebas que debe de superar una unidad de código determinada, posteriormente implementar dicha unidad y llegado el caso, refactorizarla. Por otra parte, las pruebas se puede desarrollar para probar ciertas unidades de código que ya se tienen . Estos últimos años, donde los sistemas de integración contínua son cada vez más comunes, implementar pruebas que nos permitan detectar cuanto antes problemas en el código es una práctica muy recomendable de ingeniería de software.

Las autotools (automake, autoconf, etc) permite construir de manera fácil los ejecutables de las pruebas. Después de generar el makefile, estas pruebas se ejecutarán con un simple:

make check

Esta pequeña entrada se dan los pasos básicos para poder incorporar a nuestro proyecto que usa las autotools la ejecución de las pruebas.

Añadiendo los tests a nuestro proyecto con automake

Supongamos que tenemos una estructura de directorios tal que así de un proyecto llamado tut6:

tut6
  src
    tests

Queremos que cuando se ejecute la orden make check se ejecuten los tests necesarios de nuestro paquete de software. Estos checks pueden ser programas compilados o bien scripts que llaman a otros programas. Para eso, se define una varible TESTS en nuestro Makefile.am situado en el directorio tests, que es donde residierán los programas o scripts que se quiere ejecutar en la fase de pruebas. Por ejemplo, si el programa que hace los tests se llama list_test bastaría con poner en el Makefile.am:

TESTS = list_test

Por otra parte, puede que sea necesario compilar programas específicos para tests, que no se instalarán y que sólo se ejecutaran cuando el usuario quiera pasar los tests. Para ello existe una serie de variables, cuyo prefijo es check_ que nos permitirán compilar el programa, librerias, etc que nos interese durante la ejecución de los pasos asociados al make check.

Siguiendo el ejemplo anterior, supongamos que el programa list_test depende del fichero list_test.c y del fichero situado en las fuentes list.c. Queremos que dicho programa se compile cuando se ejecuta la orden make check en nuestro proyecto. Bien, pues basta con añadir a nuestro fichero Makefile.am las siguientes líneas:

TOP = $(top_srcdir)
TESTS = list_test
check_PROGRAMS = list_test
# List lib
list_test_SOURCES = list_test.c $(TOP)/src/list.c
list_test_CFLAGS = -I$(TOP)/src/

Como puede verse, las variables se establecen con el prefijo del programa (list_test_ en este ejemplo) corresponde a opciones que se pasarán al compilador de C (si fuera, C++, sería _CPPFLAGS)

Puede encontrarse el código de un ejemplo de todo lo anterior en tut6

lunes, octubre 23, 2017

Powermock: Notas rápidas para interceptar (mock) llamadas estáticas

Powermock es una librería que añade nuevas capacidades de test a algunas librerías de test, usando su propio cargador de clases y reescribiendo el bytecode de las clases que están siendo probadas. De esta manera se pueden probar métodos estáticos, probar clases saltándose bloques de inicialización estáticos o acceder al estado interno de un objeto. Por defecto extienden las funcionalidades de mockito y EasyTest

Si estamos usando maven, se puede añadir una referencia a powermock automáticamente para que se baje de los repositorios que se tenga configurado. Si se está usando Mockito 1.x y JUnit 4.4 se puede usar la siguiente configuración en las dependencias del pom. Si se consulta en enlace, veréis que es diferente dependiendo de si es JUnit 4.4 o una versión anterior. Notar que el uso de JUnit 3.x está desaconsejado por obsoleto. Una vez añadido a nuestro pom.xml y maven está configurado correctamente, se bajará las dependencias necesarias.

<properties>
    <powermock.version>1.7.1</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>

Puesto que uso JUnit 4 junto con PowerMock, es necesario configurar la ejecución de los test para que haga uso de la infraestructura de Powermock cuando se ejecuten. Eso se consigue anotando la clase que implementa los test con @RunWith(PowerMockRunner.class). Además, para que Powermock sepa qué clases debe de preparar, es necesario especificar con otra anotación qué clases deben de transformarse. Esta anotación es @PrepareForTest. Se ve mejor con el siguiente ejemplo. Supongamos que queremos verificar la funcionalidad de la siguiente clase que sólo tiene dos métodos estáticos:

package org.menzoberrazan.maven;
public class StaticClass {
    static public String metodo1(){
        return "Método 1 llamado";
    }
    static public String metodo2(){
        return "Método 2 llamado";
    }
}

La clase que se va usar para probar está al final del párrafo. En ella hay tres tests:

  • Un primer test normal, que nos muestra lo que devolvería los métodos estáticos son mocks
  • Un segundo test, donde se mockea toda la clase, por lo tanto hay que dar en el test una implementación de cada método que se llame, cosa que se hace con when
  • Un último test, donde vemos un mock parcial de uno de los métodos de la clase estática.
package org.menzoberrazan.maven;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.junit.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PrepareForTest({StaticClass.class})
public class AppTest
{
    @Test
    public void test1(){
        assertEquals("Método 1 llamado",StaticClass.metodo1());
        assertEquals("Método 2 llamado", StaticClass.metodo2());
    }
    @Test
    public void test2(){
        PowerMockito.mockStatic(StaticClass.class);
        PowerMockito.when(StaticClass.metodo1()).thenReturn("Método 1 mockeado");
        PowerMockito.when(StaticClass.metodo2()).thenReturn("Método 2 mockeado");
        assertEquals("Método 1 mockeado", StaticClass.metodo1());
        assertEquals("Método 2 mockeado", StaticClass.metodo2());
    }
    @Test
    public void test3(){
        PowerMockito.stub(PowerMockito.method(StaticClass.class, "metodo1")).toReturn("Método 1 parcial");
        assertEquals("Método 1 parcial", StaticClass.metodo1());
        assertEquals("Método 2 llamado", StaticClass.metodo2());
    }
}

Los puntos claves a tener en cuenta en este ejemplo son:

  • Las anotaciones en la clase que implementa los test.
  • Las anotaciones que se usan para indicar que un determinado método es un test (@Test)
  • El uso de las diferentes funciones assert* que nos provee junit para poder comparar los valores.
  • El mock total o parcial de la clase.
  • El uso de las versiones correctas de los paquetes en el pom.xml