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.