Anotaciones de primavera: pruebas

Spring Framework es un marco muy robusto, lanzado en 2002. Sus características principales se pueden aplicar a aplicaciones simples de Java o extenderse a aplicaciones complejas y modernas...

Introducción

Spring Framework es un marco muy robusto, lanzado en 2002. Sus características principales se pueden aplicar a aplicaciones Java sencillas o extenderse a aplicaciones web complejas y modernas.

Como se actualiza constantemente y sigue nuevos paradigmas de arquitectura y programación, ofrece soporte para muchos otros marcos que funcionan de la mano con él.

Con una gama tan amplia de funcionalidades, es normal que nos presente algunas anotaciones nuevas, que son una parte clave del desarrollo de aplicaciones Spring.

La configuración de Spring es totalmente personalizable, lo que originalmente se hizo a través de archivos de configuración XML. Sin embargo, este enfoque se ha vuelto obsoleto y la mayoría de las personas hoy en día recurren a la configuración de anotaciones.

Dicho esto, esta serie de artículos tiene como objetivo desentrañar las opciones que tiene como desarrollador para configurar y usar Spring Framework:

Anotaciones de pruebas de primavera

El desarrollo basado en pruebas (TDD) se ha convertido en un tema importante hoy en día y se considera una práctica extremadamente mala no probar correctamente sus aplicaciones.

Existen varios marcos ampliamente utilizados que facilitan mucho este trabajo para los desarrolladores, donde JUnit es el más utilizado.

Para ponerse al día con las prácticas modernas de programación, Spring ha lanzado una nueva dependencia de “iniciador”, “spring-boot-starter-test”, que se compone de algunos marcos:

  • JUnidad
  • Prueba de resorte y prueba de arranque de resorte
  • AfirmarJ
  • Hamcrest
  • Mockito
  • JSONafirmar -JsonPath

En este artículo, cubriremos las siguientes anotaciones de prueba:

@BootstrapCon

La anotación @BootstrapWith es una anotación que probablemente usará muy raramente. Las configuraciones predeterminadas para Marco Spring TestContext son más que lo suficientemente bueno para la mayoría de los casos de uso.

Si no es así, puede cambiar el ‘ContextLoader’ o implementar ‘TestContext’ personalizados entre una miríada de otras configuraciones que puede cambiar.

Nuevamente, esta es una anotación que probablemente no usará si no es parte de un equipo que realmente necesita una configuración personalizada para Spring TestContext Framework.

@ConfiguraciónContexto

@ContextConfiguration una anotación de prueba de integración aplicada a nivel de clase utilizada para definir cómo Spring debe cargar el ApplicationContext.

Esta anotación se puede aplicar junto con @Component (así como anotaciones como @Service, @Repository, etc.) y anotaciones @Configuration, así como cualquier clase que contenga @Beans.

Puede usar la anotación para hacer referencia a archivos XML o clases de Java:

1
2
3
4
5
6
@ContextConfiguration("/some-test-configuration-file.xml")
// @ContetConfiguration(locations = "/some-test-configuration-file.xml")
// You can use the optional `locations` flag as well.
public class ApplicationTests {
    // Testing code...
}
1
2
3
4
@ContextConfiguration(classes = TestConfiguration.class)
public class ApplicationTests {
    // Testing code...
}

Por ejemplo, digamos que tenemos TestBean:

1
2
3
4
5
6
7
8
@Configuration
public class TestBean {

    @Bean
    public DeveloperService developerService() {
        return new DeveloperService();
    }
}

Si quisiéramos hacer algunas ‘afirmaciones’ en este bean, haríamos algo como:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@ContextConfiguration(classes = TestBean.class)
public class ApplicationTests {
    @Autowired
    private DeveloperService;

    @Test
    public void testBean() {
        Developer dev = developerService.getDeveloperById(5);
        assertEquals("David", dev.getName());
    }
}

Hoy en día, es preferible confiar en el enfoque de clase, ya que XML generalmente se considera un enfoque obsoleto para registrar beans. Si tiene más de una clase, por supuesto, simplemente las especificaría a través de classes = {TestBean.class, TestBean2.class, TestBean3.class}, etc.

Esto nos lleva a la anotación @Test, que se tratará en detalle a continuación. Por ahora, simplemente usémoslo con fines ilustrativos.

@Configuración de aplicación web

Si desea asegurarse de que Spring cargue un WebApplicationContext para sus pruebas en lugar del ApplicationContext normal, puede usar la anotación @WebAppConfiguration junto con la anotación @ContextConfiguration:

1
2
3
4
5
6
7
8
9
@ContextConfiguration(classes = TestBean.class)
@WebAppConfiguration
public class ApplicationTests {

    @Autowired
    private DeveloperService;

    // Rest of the code...
}

Alternativamente, puede especificar el indicador value, o más bien, la ubicación del WebApplicationContext, si no está ubicado en el directorio predeterminado src/main/webapp:

1
2
@WebAppConfiguration("some/other/location")
public class ApplicationTests {}

@Jerarquía de contexto

Otra anotación que generalmente se usa raramente (personalmente no he visto a nadie usarla en un proyecto) es la anotación @ContextHierarchy.

Permite al desarrollador definir múltiples @ContextConfigurations en niveles a través de una relación padre-hijo.

La idea es que los contextos hijos puedan usar los beans registrados en los contextos padres y esto mejora la reutilización de los beans:

1
2
3
4
5
6
7
@ContextHierarchy({
    @ContextConfiguration(classes = ApplicationTestConfiguration.class),
    @ContextConfiguration(classes = WebApplicationTestConfiguration.class)
})
public class ApplicationTests {

}

Si desea leer más sobre esta anotación, [la documentación](https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-ctx-management- ctx-hierarchies) contiene información en profundidad sobre la jerarquía de contexto.

@Perfiles activos

La anotación @ActiveProfiles es una anotación bastante directa y simple. Define qué perfil debe estar activo al cargar la configuración de contexto:

1
2
3
@ContextConfiguration
@ActiveProfiles("dev")
public class ApplicationTests {}

Esto indica que el perfil "dev" debe estar activo.

El nombre de la anotación implica que podemos definir múltiples perfiles, los cuales podemos:

1
2
3
@ContextConfiguration
@ActiveProfiles({"dev", "prod"})
public class ApplicationTests {}

If you'd like to read more about Perfiles de resorte, we've got you covered!

@Retroceder

A veces, cuando se trata de bases de datos, queremos revertir los cambios que hemos realizado, especialmente si hemos causado una excepción.

La anotación @Rollback define si la transacción de un método marcado con @Transactional debe revertirse, después de que se haya completado el método de prueba que lo llama.

Se puede aplicar a nivel de clase y método:

  • Nivel de clase: define la reversión predeterminada para todos los métodos de prueba dentro de la clase
  • Nivel de método: define la reversión para el método de prueba específico
1
2
3
4
5
@Rollback(true)
@Test
public void someTest() {
    // ...calling some transactional method
}

Una vez finalizada la prueba, se revertirán todos los cambios realizados por el método transaccional.

Un punto interesante a destacar es el hecho de que puede establecer el indicador opcional en falso, en el que Spring se asegura de que los cambios no se reviertan. Establecer la anotación @Rollback en falso se comportará exactamente igual que @Commit.

@Comprometerse

Agregando a la sección anterior, la anotación @Commit se usa cuando queremos asegurar los cambios en la base de datos después de ejecutar los métodos de prueba.

Se comporta igual que @Rollback(false) y se puede aplicar a nivel de clase o método:

1
2
3
4
5
@Commit
@Test
public void someTest() {
    // ...calling some transactional method
}

@Antes de la transacción

A veces, queremos ejecutar piezas de código específicas antes de que se realicen las transacciones. Para hacerlo, obviamente necesitamos definir métodos escritos específicamente para esto.

Para invocarlos antes de cada transacción, simplemente los anotamos con la anotación @BeforeTransaction:

1
2
3
4
@BeforeTransaction
void methodBeforeTransaction() {
    // ...ran before a transaction
}

Para que la anotación funcione correctamente, debe marcar sus métodos transaccionales con @Transactional.

Nota: A partir de Spring 4.3, estos métodos no están obligados a ser públicos.

@Después de la transacción

Con la misma naturaleza que la anotación @BeforeTransaction, la anotación @AfterTransaction ejecuta un determinado método después de que se haya realizado una transacción:

1
2
3
4
@AfterTransaction
void methodAfterTransaction() {
    // ...ran after a transaction
}

Nota: A partir de Spring 4.3, estos métodos no están obligados a ser públicos.

@Sql

Usando la anotación @Sql y pasando los nombres de los esquemas que deseamos que se ejecuten, podemos ejecutar secuencias de comandos SQL de forma programática (o declarativa).

De forma predeterminada, estos scripts se ejecutan antes que cualquier método @Before.

Si definimos un script, como createTable.sql:

1
CREATE TABLE ITEM (ITEM_ID INT PRIMARY KEY, ITEM_NAME VARCHAR(256) NOT NULL);

Podemos referenciarlo y ejecutarlo fácilmente:

1
2
3
4
5
@Test
@Sql("/createTable.sql")
public void itemTest {
    // ...some code that depends on the sql script above
}

@SqlGroup

La anotación @SqlGroup nos permite agrupar varios scripts SQL y ejecutarlos.

Si tenemos otro script, como uno para soltar la misma tabla, dropTable.sql:

1
DROP TABLE ITEM;

Podemos agrupar el script createTable.sql con el script dropTable.sql para ejecutar antes y después del método de prueba, por ejemplo:

1
2
3
4
5
6
7
8
@Test
@SqlGroup({
    @Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD, scripts = ""),
    @Sql(executionPhase = ExecutionPhase.AFTER_TEST_METHOD, scripts = ""),
})
public void itemTest {
    // ...item table gets created, tested by the code and then dropped
}

@SqlConfig

Como su nombre lo indica, siguiendo los ejemplos estándar de anotaciones de Spring, la anotación @SqlConfig se usa para definir la configuración de los scripts SQL, cómo se analizan y ejecutan.

Se puede aplicar a nivel de clase o de método. Las pruebas de integración, que requieren una configuración global para todos los scripts SQL en ejecución, suelen utilizar el enfoque de nivel de clase, mientras que el enfoque de nivel de método es para configuraciones locales de ciertos métodos:

1
2
3
4
5
6
@Test
@Sql(scripts = "/createTable.sql",
    config = @SqlConfig(attribute = "val", attribute2 = "val"))
public void itemTest {
    // Some code...
}

Hay 9 atributos que puede pasar a la anotación @SqlConfig:

  • blockCommentEndDelimiter: delimitador final para comentarios de bloque
  • blockCommentStartDelimiter: delimitador de inicio para comentarios de bloque
  • commentPrefix: el prefijo para comentarios de una sola línea
  • dataSource: Nombre del bean dataSource
  • encoding: especificando la codificación para los scripts
  • errorMode: qué modo usar cuando se encuentra un error
  • separator: El carácter que se usa para separar declaraciones
  • transactionManager: Nombre del bean administrador de transacciones
  • modo de transacción: qué modo usar al ejecutar scripts SQL

@Prueba de arranque de primavera

La anotación @SpringBootTest busca la clase de prueba anotada con @SpringBootConfiguration, que en la mayoría de los casos es nuestra clase de aplicación principal, ya que @SpringBootApplication incluye la anotación anterior dentro de sí misma.

Una vez encontrado, construye el contexto de la aplicación para el entorno de prueba. Incluso puede iniciar un entorno web utilizando el atributo webEnvironment:

1
2
3
4
5
6
7
8
9
@SpringBootTest
public class IntegrationTests {
    // Rest of the code
}

@SpringBootTest(webEnvironment = pringBootTest.WebEnvironment.RANDOM_PORT)
public class WebEnvIntegrationTests {
    // Rest of the code
}

@DataJpaTest

Usando la anotación @DataJpaTest, podemos probar aplicaciones JPA. Se aplica a nivel de clase y construye un contexto de aplicación para todas las clases @Enitity, junto con una base de datos incrustada que se aplica de forma predeterminada.

Nota: Las clases normales @Component no se cargan en el contexto de la aplicación creado por la anotación @DataJpaTest.

Se usa junto con la anotación @RunWith(SpringRunner.class), que indica qué instalaciones usará la clase marcada.

De forma predeterminada, todas las transacciones JPA se revertirán (puede cambiar este comportamiento aplicando @Rollback(false) o @Commit):

1
2
3
4
5
@RunWith(SpringRunner.class)
@DataJpaTest
public class SomeJpaTest {
    // Rest of the code
}

Sin embargo, esta es una prueba clásica de JPA, si desea utilizar la base de datos real, en lugar de la base de datos integrada en la memoria provista, simplemente puede agregar otra anotación para evitar tal comportamiento:

1
2
3
4
5
6
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class SomeJpaTest {
    // Rest of the code
}

@Prueba de datosMongo

Muy similar a la anotación @DataJpaTest, para realizar pruebas clásicas de MongoDB, aplicamos la anotación @DataMongoTest junto con la anotación @RunWith(SpringRunner.class).

Tenga en cuenta que esta anotación se usa cuando la prueba a la que se aplica solo prueba los componentes de MongoDB y agrega solo las clases @Document al contexto de la aplicación:

1
2
3
4
5
@RunWith(SpringRunner.class)
@DataMongoTest
public class SomeMongoTest {
    // Rest of the code
}

Por otra parte, si desea ejecutar esto con la base de datos real y no con la base de datos incrustada en memoria proporcionada por Mongo, puede excluir esta opción:

1
2
3
4
5
@RunWith(SpringRunner.class)
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
public class SomeMongoTest {
    // Rest of the code
}

@WebMvcTest

Nuevamente, muy similar a las anotaciones @DataJpaTest y @DataMongoTest, para realizar pruebas clásicas de Spring MVC, aplicamos la anotación @WebMvcTest junto con la anotación @RunWith(SpringRunner.class).

Tenga en cuenta que los efectos de esta anotación solo se aplican a la infraestructura de MVC. Dicho esto, no instancia todo el contexto.

La anotación se puede usar para probar un solo controlador, pasándolo como un atributo como @WebMvcTest(SomeController.class).

Para instanciar otras dependencias necesarias, como los servicios, normalmente usamos la anotación @MockBean. @WebMvcTest configura MockMvc, que se puede usar para probar fácil y rápidamente los controladores MVC e instanciar a otros colaboradores:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
public class ControllerTests {

    // Auto-configured to make mocking easier
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private SomeBean someBean;

    @Test
    public void someTest() {
        // Test logic
    }
}

@MockBean

Al probar unidades específicas, como, por ejemplo, un controlador, queremos aislarlas tanto como podamos. Dado que la mayoría de los componentes de Spring Application se basan en un montón de otros componentes (dependencias), es esencial asegurarse de que estos componentes se puedan probar individualmente.

Para aislar con éxito los objetos que queremos probar, mientras permitimos que la aplicación funcione bien, simulamos o simulamos las dependencias. Una anotación @MockBean se usa cuando queremos simular una dependencia en una aplicación:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
public class ControllerTests {

    // Auto-configured to make mocking easier
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private SomeBean someBean;

    @Test
    public void someTest() {
        // Test logic
    }
}

En este ejemplo, la dependencia someBean está simulando una dependencia real. Si el bean existe en el contexto, el simulacro lo reemplaza. Si no existe, el simulacro se agrega al contexto como un bean.

Nota: Hay una diferencia entre las anotaciones @Mock y @MockBean. La anotación @Mock proviene de la biblioteca Mockito y es equivalente a llamar al método Mockito.mock(). Por otro lado, @MockBean es el envoltorio de la biblioteca Spring de la anotación @Mock.

@AutoConfigurarMockMvc

Como sugiere el nombre, la anotación @AutoConfigureMockMvc, cuando se aplica a una clase de prueba, configurará automáticamente MockMvc, de la misma manera que @WebMvcTest lo configura automáticamente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class ControllerTests {

    @Autowired
    private MockMvc mockMvc;

    // Rest of the logic
}

Si desea centrarse solo en la capa web, considere usar la anotación @WebMvcTest en su lugar.

@JsonTest

Muchas aplicaciones están tratando con Serialización/deserialización JSON. Por lo tanto, tiene mucho sentido asegurarse de que funcione correctamente mientras se prueba la aplicación. Al usar la anotación @JsonTest, Spring configura automáticamente el mapeador JSON compatible (Jackson, Gson o Jsonb).

Por lo general, se usa junto con @RunWith(SpringRunner.class) y se usa para pruebas JSON clásicas, buscando @JsonComponents.

1
2
3
4
5
6
7
8
@RunWith(SpringRunner.class)
@JsonTest
public class JsonTests {
    @Test
    public void someJsonTest() {
        // Rest of the logic
    }
}

@TestPropertySource

La anotación @TestPropertySource se aplica a nivel de clase y define las ubicaciones de las fuentes de propiedades que queremos usar para la prueba.

Estas propiedades se guardan como un conjunto de @PropertySources en el entorno del contexto de la aplicación. Estas propiedades tienen prioridad sobre las propiedades del sistema o de la aplicación.

Esencialmente, cuando deseamos anular las propiedades del sistema/aplicación con propiedades específicas para nuestras pruebas, simplemente anotamos la clase de prueba:

1
2
3
4
5
6
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
@TestPropertySource("classpath:applicationtest.properties")
public class ApplicationTest {
    // Rest of the logic
}

Por otro lado, puede especificar propiedades en línea, en lugar de todo el archivo de propiedades:

1
2
3
4
5
6
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
@TestPropertySource(properties = {"sa.website_name = wikihtp", "sa.website_url = www.wikihtp.com"})
public class ApplicationTest {
    // Rest of the logic
}

@Cronometrado

La anotación @Timed define el tiempo en milisegundos en el que el método de prueba tiene que terminar la ejecución, de lo contrario fallará:

1
2
3
4
@Timed(millis = 1000)
public void testMethod() {
    // Some test logic
}

Si la prueba tarda más de un segundo en ejecutarse, fallará. Esto incluye todas las repeticiones del método, si la anotación @Repeat está presente.

@Repetir

La anotación @Repeat define cuántas veces se debe repetir un método de prueba:

1
2
3
4
5
@Repeat(5)
@Test
public void testMethod() {
    // Some test logic
}

Esta prueba se repetirá cinco veces.

Conclusión

El framework Spring es un framework poderoso y robusto que realmente cambió el juego cuando se trata de desarrollar aplicaciones web. Entre todas las cosas que admite, ofrece una excelente compatibilidad con TDD para Spring Applications y permite a los desarrolladores configurar fácil y rápidamente cualquier tipo de prueba.