O Spring TestExecutionListener

O Spring TestExecutionListener

 1. Visão geral

Normalmente, usamos as anotações JUnit como@BeforeEach, @AfterEach, @BeforeAll,e@AfterAll, para orquestrar o ciclo de vida dos testes, mas às vezes isso não é suficiente - especialmente quando estamos trabalhando com o framework Spring.

É aqui que SpringTestExecutionListener se torna útil.

Neste tutorial,we’ll see what the TestExecutionListener offers, the default listeners provided by Spring, and how to implement a custom TestExecutionListener.

2. A interfaceTestExecutionListener

Primeiro, vamos visitar a interfaceTestExecutionListener:

public interface TestExecutionListener {
    default void beforeTestClass(TestContext testContext) throws Exception {};
    default void prepareTestInstance(TestContext testContext) throws Exception {};
    default void beforeTestMethod(TestContext testContext) throws Exception {};
    default void afterTestMethod(TestContext testContext) throws Exception {};
    default void afterTestClass(TestContext testContext) throws Exception {};
}

As implementações dessa interface podem receber eventos durante diferentes estágios de execução do teste. Conseqüentemente, cada um dos métodos na interface recebe um objetoTestContext.

Este objetoTestContext contém informações do contexto Spring e da classe e métodos de teste de destino. Esta informação pode ser usada para alterar o comportamento dos testes ou estender sua funcionalidade.

Agora, vamos dar uma olhada rápida em cada um desses métodos:

  • afterTestClass - pós-processa uma classe de testeafter the execution of all tests dentro da classe

  • afterTestExecution - pós-processa um testeimmediately after execution of the test method no contexto de teste fornecido

  • afterTestMethod - pós-processa um testeafter execution of after-lifecycle callbacks da estrutura de teste subjacente

  • beforeTestClass - pré-processa uma classe de testebefore execution of all tests dentro da classe

  • beforeTestExecution - pré-processa um testeimmediately before execution of the test method no contexto de teste fornecido

  • beforeTestMethod - pré-processa um testebefore execution of before-lifecycle callbacks da estrutura de teste subjacente

  • prepareTestInstance - prepara a instância de teste do contexto de teste fornecido

É importante notar que esta interface fornece implementações padrão vazias para todos os métodos. Consequentemente, implementações concretas podem optar por substituir apenas os métodos adequados para a tarefa em questão.

3. TestExecutionListeners padrão da mola

Por padrão, o Spring fornece algumas implementaçõesTestExecutionListener prontas para usar.

Vejamos rapidamente cada um deles:

  • ServletTestExecutionListener - configura simulações da API Servlet para umWebApplicationContext

  • DirtiesContextBeforeModesTestExecutionListener - lida com a anotação@DirtiesContext para modos “antes”

  • DependencyInjectionTestExecutionListener - fornece injeção de dependência para a instância de teste

  • DirtiesContextTestExecutionListener - lida com a anotação@DirtiesContext para modos “depois”

  • TransactionalTestExecutionListener - fornece execução de teste transacional com semântica de reversão padrão

  • SqlScriptsTestExecutionListener - executa scripts SQL configurados usando a anotação@Sql

Esses ouvintes são pré-registrados exatamente na ordem listada. Veremos mais sobre o pedido quando criarmos umTestExecutionListener personalizado.

4. Usando umTestExecutionListener personalizado

Agora, vamos definir umTestExecutionListener personalizado:

public class CustomTestExecutionListener implements TestExecutionListener, Ordered {
    private static final Logger logger = LoggerFactory.getLogger(CustomTestExecutionListener.class);

    public void beforeTestClass(TestContext testContext) throws Exception {
        logger.info("beforeTestClass : {}", testContext.getTestClass());
    };

    public void prepareTestInstance(TestContext testContext) throws Exception {
        logger.info("prepareTestInstance : {}", testContext.getTestClass());
    };

    public void beforeTestMethod(TestContext testContext) throws Exception {
        logger.info("beforeTestMethod : {}", testContext.getTestMethod());
    };

    public void afterTestMethod(TestContext testContext) throws Exception {
        logger.info("afterTestMethod : {}", testContext.getTestMethod());
    };

    public void afterTestClass(TestContext testContext) throws Exception {
        logger.info("afterTestClass : {}", testContext.getTestClass());
    }

    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    };
}

Para simplificar, tudo o que esta classe faz é registrar algumas das informações deTestContext.

4.1. Registrando o ouvinte personalizado usando@TestExecutionListeners

Agora, vamos usar esse ouvinte em nossa classe de teste. Para fazer isso, vamos registrá-lo usando a anotação@TestExecutionListeners:

@RunWith(SpringRunner.class)
@TestExecutionListeners(value = {
  CustomTestExecutionListener.class,
  DependencyInjectionTestExecutionListener.class
})
@ContextConfiguration(classes = AdditionService.class)
public class AdditionServiceUnitTest {
    // ...
}

É importante observar queusing the annotation will de-register all default listeners. Portanto, adicionamosDependencyInjectionTestExecutionListener explicitamente para que possamos usar a fiação automática em nossa classe de teste.

Se precisarmos de qualquer um dos outros ouvintes padrão, teremos que especificar cada um deles. Mas, também podemos usar a propriedademergeMode da anotação:

@TestExecutionListeners(
  value = { CustomTestExecutionListener.class },
  mergeMode = MergeMode.MERGE_WITH_DEFAULTS)

Aqui,MERGE_WITH_DEFAULTS indica que os ouvintes declarados localmente devem ser mesclados com os ouvintes padrão.

Agora, quando executarmos o teste acima, o ouvinte registrará cada evento que receber:

[main] INFO  o.s.t.c.s.DefaultTestContextBootstrapper - Using TestExecutionListeners:
[[email protected]38364841,
org.springframewor[email protected]28c4711c]
[main] INFO  c.b.t.CustomTestExecutionListener - beforeTestClass :
class com.example.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest
[main] INFO  c.b.t.CustomTestExecutionListener - prepareTestInstance :
class com.example.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest
[main] INFO  o.s.c.s.GenericApplicationContext -
Refreshing [email protected]68ef40: startup date [XXX];
root of context hierarchy
[main] INFO  c.b.t.CustomTestExecutionListener - beforeTestMethod :
public void com.example.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest
.whenValidNumbersPassed_thenReturnSum()
[main] INFO  c.b.t.CustomTestExecutionListener - afterTestMethod :
public void com.example.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest
.whenValidNumbersPassed_thenReturnSum()
[main] INFO  c.b.t.CustomTestExecutionListener - afterTestClass :
class com.example.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest

4.2. Descoberta automática de implementações deTestExecutionListener padrão

Usar@TestExecutionListener para registrar ouvintes é adequado se for usado em um número limitado de classes de teste. Porém, pode ser complicado adicioná-lo a um conjunto de testes inteiro.

Podemos resolver esse problema tirando proveito do suporte fornecido pelo mecanismoSpringFactoriesLoader para a descoberta automática de implementaçõesTestExecutionListener.

O módulospring-test declara todos os ouvintes padrão do núcleo na chaveorg.springframework.test.context.TestExecutionListener em seu arquivo de propriedadesMETA-INF/spring.factories. Da mesma forma, podemosregister our custom listener by using the above key in our own META-INF/spring.factories properties file:

org.springframework.test.context.TestExecutionListener=\
com.example.testexecutionlisteners.CustomTestExecutionListener

4.3. Implementações deTestExecutionListener padrão de pedido

Quando o Spring descobrir implementações padrão deTestExecutionListener através do mecanismoSpringFactoriesLoader, ele as classificará usandoAnnotationAwareOrderComparator. do Spring. Isso honra a interfaceOrdered do Spring e a anotação@Order para pedidos .

Observe que todas as implementações padrãoTestExecutionListener fornecidas pelo Spring implementamOrdered com os valores apropriados. Portanto, temos que nos certificar de que nossa implementaçãoTestExecutionListener personalizada está registrada com o pedido adequado. Consequentemente, implementamosOrdered em nosso ouvinte personalizado:

public class CustomTestExecutionListener implements TestExecutionListener, Ordered {
    // ...
    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    };
}

Mas, podemos usar a anotação@Order em vez de.

5. Conclusão

Neste artigo, vimos como implementar umTestExecutionListener personalizado. Também analisamos os ouvintes padrão fornecidos pela estrutura Spring.

E, claro, o código que acompanha este artigo está disponívelover on GitHub.