Skip to content

Las pruebas deben de ser un conjunto EFECTIVO (deben detectar el máximo número posible de errores) y EFICIENTE (con el menor número posible de casos de prueba).


Ubicaciones directorios

Para la clase Cosa.java.

project-home/                    --> Carpeta raíz que contiene el proyecto Maven
├── src/                         --> Ficheros
   ├── main/
      ├── java/                --> Código fuente en Java
         └── paquete/         --> Paquete raíz del proyecto
             ├── excepciones/
                └── CosaException.java  --> Manejo de excepciones
             └── Cosa.java               --> Clase principal
      └── resources/           --> Archivos adicionales usados por el código fuente
   └── test/                    --> Pruebas
       ├── java/                --> Código fuente de las pruebas en Java
          └── paquete/         --> Paquete raíz de pruebas
              ├── CosaTest.java          --> Clase de prueba
              ├── CosaTestable.java      --> Clase para la inyección de dependencias
              └── CosaStub.java          --> STUB para pruebas
       └── resources/           --> Archivos adicionales para pruebas

├── target/                      --> Generado automáticamente por Maven (build)
   ├── classes/                 --> Archivos compilados del código fuente
   ├── test-classes/            --> Archivos compilados del código de prueba
   ├── surefire-reports/        --> Reportes de ejecución de pruebas unitarias
      ├── paquete.CosaTest.txt
      └── TEST-paquete.CosaTest.xml
   ├── failsafe-reports/        --> Reportes de pruebas de integración
   ├── generated-sources/       --> Código fuente generado automáticamente
   ├── generated-test-sources/  --> Código de prueba generado automáticamente
   ├── maven-status/            --> Información del estado de compilación
   ├── site/                    --> Documentación del proyecto generada con `mvn site`
   ├── dependency/              --> Dependencias copiadas localmente (si se configura)
   └── [nombre].jar             --> Archivo JAR final generado (si aplica)

└── pom.xml                      --> Archivo de configuración del proyecto (POM)

Configuración pom.xml

item

Ejemplo de configuración completa siguiendo el esquema de estructura del pom:

<!-- Coordenadas -->
<groupId>ppss.practica3</groupId>  
<artifactId>drivers</artifactd>  
<version>1.0-SNAPSHOT</version>  

<!-- Variables -->
<properties>        
    <maven.compiler.source>25</maven.compiler.source>  
    <maven.compiler.target>25</maven.compiler.target>  
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
</properties> 

<!-- Librerías -->
<dependencies>        
    <dependency>            
        <groupId>org.junit.jupiter</groupId>  
        <artifactId>junit-jupiter</artifactId>  
        <version>5.11.4</version>  
        <scope>test</scope>  
    </dependency>    
</dependencies>  

<!-- Construcción -->
<build>        
    <plugins>            
        <plugin>                
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.5.2</version>
        </plugin>

        <plugin>                
            <groupId>org.apache.maven.plugins</groupId>  
            <artifactId>maven-compiler-plugin</artifactId>  
            <version>3.13.0</version>  
            <configuration>                    
                <source>21</source>  
                <target>21</target>  
            </configuration>            
        </plugin>        
    </plugins>    
</build>

Dependencias

JUnit5

<dependency>        
    <groupId>org.junit.jupiter</groupId>  
    <artifactId>junit-jupiter</artifactId>  
    <version>5.11.4</version>  
    <scope>test</scope>  
</dependency>    
  • Para definir y ejecutar las pruebas.
  • Proporciona las anotaciones necesarias para estructurar el código de prueba ( @Test, @BeforeEach, @AfterAll) y los métodos assert (assertEquals(), assertTrue(), assertThrows()).

EasyMock

<dependency>        
    <groupId>org.easymock</groupId>  
    <artifactId>easymock</artifactId>  
    <version>5.5.0</version>  
    <scope>test</scope>  
</dependency>
  • Para crear los dobles de pruebas.

Plugins

Maven Surefire Plugin (para JUnit-5 / tests unitarios)

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>3.5.2</version>
  <!-- Opcional: incluir/excluir tests -->
  <configuration>
    <!-- Por defecto ejecuta clases *Test.java, Test*.java y *Tests.java -->
    <!-- <includes><include>**/*Test.java</include></includes> -->
    <!-- <excludes><exclude>**/IT_*.java</exclude></excludes> -->
  </configuration>
</plugin>
  • Usos:
    • Ejecutar los unit-tests en la fase test.
    • Detecta automáticamente tests JUnit 5 (JUnitPlatform).
    • Permite filtros <includes>/<excludes> para naming conventions.

Configuración para drivers

<plugin>  
    <artifactId>maven-surefire-plugin</artifactId>  
    <version>3.5.2</version>  
    <dependencies>        
        <dependency>            
            <groupId>me.fabriciorby</groupId>  
            <artifactId>maven-surefire-junit5-tree-reporter</artifactId>  
            <version>1.4.0</version>  
        </dependency>    
    </dependencies>    
    <configuration>        
        <reportFormat>plain</reportFormat>  
        <consoleOutputReporter>            
            <disable>true</disable>  
        </consoleOutputReporter>        
        <statelessTestsetInfoReporter                implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">  
            <printStacktraceOnError>true</printStacktraceOnError>  
            <printStacktraceOnFailure>true</printStacktraceOnFailure>  
            <printStdoutOnError>true</printStdoutOnError>  
            <printStdoutOnFailure>true</printStdoutOnFailure>  
            <printStdoutOnSuccess>false</printStdoutOnSuccess>  
            <printStderrOnError>true</printStderrOnError>  
            <printStderrOnFailure>true</printStderrOnFailure>  
            <printStderrOnSuccess>false</printStderrOnSuccess>  
        </statelessTestsetInfoReporter>    
    </configuration>
</plugin>
  • La dependencia del plugin maven-surefire-junit5-tree-reporter cambia la salida de la consola de Maven como un árbol estructurado y jerárquico.

Maven Compiler Plugin

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.13.0</version>
  <configuration>
    <source>21</source>
    <target>21</target>
    <encoding>UTF-8</encoding>
  </configuration>
</plugin>
  • Usos:
    • Compilar el código Java de src/main/java (fase compile).
    • Compilar tests de src/test/java (fase test-compile).
    • Ajustar nivel de lenguaje (source/target) y codificación.

Dependencias

Stubs

S05.1 - Flujo de dependencias y ficheros en STUBs


Mocks

  • Estructura habitual de los tests con EasyMock:

VARIABLES

private IMocksControl control;  
private FicheroTexto sut;  
private FileReader fileReaderMock;  

SETUP

  • Para inicializar las variables, crear los dobles e inicializar la sut.
@BeforeEach  
public void setUp() {  
    control = EasyMock.createStrictControl();  
    // doble total
    fileReaderMock = control.createMock(FileReader.class);  
    // mock parcial de la sut para el metodo getFileReader  
    sut = EasyMock.partialMockBuilder(FicheroTexto.class)  
            .addMockedMethod("getFileReader")  
            .mock(control);  
}  

TEST

Los tests con EasyMock siguen un ciclo de vida de 4 pasos:

  1. Fase RECORD: Todo lo que hagamos con EasyMock.expect(...). Se prepara el escenario y le decimos a los dobles como deben comportarse y que llamadas deben esperar.
  2. Fase REPLAY: Al llamar a control.replay(). Esto le dice a EasyMock que ya tienen todas las instrucciones y puede prepararse para ejecutar la sut.
  3. Fase ACT (Ejecutar la SUT): se llama al método de la sut (sut.contarCaracteres(fichero1)). Al ejecutarse, la SUT hará las llamadas y los mocks responderán con lo que se les enseña en la fase 1. Si la SUT hace algo no programado, EasyMock lanza un error.
  4. Fase VERIFY: Se verifican los resultados reales, con los assertEquals o assertThrows y se llama a control.verify() para conformar la interacción de la SUT con los dobles según las veces y el orden especificado.
@Test  
public void C1_test1() { 
    // ===============
    // FASE 1: RECORD
    // ===============
    // para devolver el doble cuando se llame desde la sut  
    assertDoesNotThrow(() -> {  
        EasyMock.expect(sut.getFileReader(fichero1))
            .andReturn(fileReaderMock);  
    });  

    // instrucciones al doble sobre lo que debe devolver a la sut en su ejecución  
    assertDoesNotThrow(() -> {  
        EasyMock.expect(fileReaderMock.read())
        .andReturn((int) 'a')
        .andReturn((int) 'b')
        .andThrow(new IOException());  
    });  

    // ===============
    // FASE 2: REPLAY
    // ===============
    control.replay();  

    // ===============
    // FASE 3: ACT 
    // ===============
    FicheroException ex = assertThrows(FicheroException.class, () -> sut.contarCaracteres(fichero1));  
    assertEquals(fichero1 + " (Error al leer el archivo)", ex.getMessage());  

    // ===============
    // FASE 4: VERIFY
    // ===============
    control.verify();  
}  

Ejemplo completo

package ppss.P06;  

// imports  

import static org.junit.jupiter.api.Assertions.*;  

class FicheroTextoTest  {  
    private String fichero1 = "src/test/resources/fichero1.txt";  
    private String fichero2 = "src/test/resources/fichero2.txt";  

    // Se instancian las variables de la sut, dobles/deps y el StrictControl  
    private IMocksControl control;  
    private FicheroTexto sut;  
    private FileReader fileReaderMock;  

    @BeforeEach  
    public void setUp() {  
        control = EasyMock.createStrictControl();
        fileReaderMock = control.createMock(FileReader.class);  
        sut = EasyMock.partialMockBuilder(FicheroTexto.class)  
                .addMockedMethod("getFileReader")  
                .mock(control);  
    }  

    @Test  
    public void C1_test1() {  
        assertDoesNotThrow(() -> {  
            EasyMock.expect(sut.getFileReader(fichero1))
                .andReturn(fileReaderMock);  
        });  

        assertDoesNotThrow(() -> {  
            EasyMock.expect(fileReaderMock.read())
            .andReturn((int) 'a')
            .andReturn((int) 'b')
            .andThrow(new IOException());  
        });  

        control.replay();  

        FicheroException ex = assertThrows(FicheroException.class, () -> sut.contarCaracteres(fichero1));  
        assertEquals(fichero1 + " (Error al leer el archivo)", ex.getMessage());  

        control.verify();  
    }  

    @Test  
    public void C2_test2() {  
        assertDoesNotThrow(() -> {  
            EasyMock.expect(sut.getFileReader(fichero2)).andReturn(fileReaderMock);  
        });  

        assertDoesNotThrow(() -> {  
            EasyMock.expect(fileReaderMock.read())
                .andReturn((int) 'a')
                .andReturn((int) 'b')
                .andReturn((int) 'c')
                .andReturn(-1);  
            fileReaderMock.close();  
            EasyMock.expectLastCall().andThrow(new IOException());  
        });  

        control.replay();  

        FicheroException ex = assertThrows(FicheroException.class, () -> sut.contarCaracteres(fichero2));  
        assertEquals(fichero2 + " (Error al cerrar el archivo)", ex.getMessage());  

        control.verify();  
    }  
}

Partial Mocking

S06 - Dependencias externas 2 (mocks) -> Partial Mocking