From 0fd94f1b9f19554c1fd951d9c59cbc4ef0fee76f Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:30:20 +0100 Subject: [PATCH 1/2] Polishing --- .../testing/testcontext-framework/tel-config.adoc | 14 ++++++++------ .../test/context/TestExecutionListener.java | 7 ++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/tel-config.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/tel-config.adoc index d4a41beb20..1d6c2d6497 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/tel-config.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/tel-config.adoc @@ -7,16 +7,17 @@ by default, exactly in the following order: * `ServletTestExecutionListener`: Configures Servlet API mocks for a `WebApplicationContext`. * `DirtiesContextBeforeModesTestExecutionListener`: Handles the `@DirtiesContext` - annotation for "`before`" modes. + annotation for "before" modes. * `ApplicationEventsTestExecutionListener`: Provides support for xref:testing/testcontext-framework/application-events.adoc[`ApplicationEvents`]. -* `BeanOverrideTestExecutionListener`: Provides support for xref:testing/testcontext-framework/bean-overriding.adoc[] . +* `BeanOverrideTestExecutionListener`: Provides support for + xref:testing/testcontext-framework/bean-overriding.adoc[]. * `DependencyInjectionTestExecutionListener`: Provides dependency injection for the test instance. * `MicrometerObservationRegistryTestExecutionListener`: Provides support for Micrometer's `ObservationRegistry`. * `DirtiesContextTestExecutionListener`: Handles the `@DirtiesContext` annotation for - "`after`" modes. + "after" modes. * `CommonCachesTestExecutionListener`: Clears resource caches in the test's `ApplicationContext` if necessary. * `TransactionalTestExecutionListener`: Provides transactional test execution with @@ -161,15 +162,16 @@ change from release to release -- for example, `SqlScriptsTestExecutionListener` introduced in Spring Framework 4.1, and `DirtiesContextBeforeModesTestExecutionListener` was introduced in Spring Framework 4.2. Furthermore, third-party frameworks like Spring Boot and Spring Security register their own default `TestExecutionListener` -implementations by using the aforementioned xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-automatic-discovery[automatic discovery mechanism] -. +implementations by using the aforementioned +xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-automatic-discovery[automatic discovery mechanism]. To avoid having to be aware of and re-declare all default listeners, you can set the `mergeMode` attribute of `@TestExecutionListeners` to `MergeMode.MERGE_WITH_DEFAULTS`. `MERGE_WITH_DEFAULTS` indicates that locally declared listeners should be merged with the default listeners. The merging algorithm ensures that duplicates are removed from the list and that the resulting set of merged listeners is sorted according to the semantics -of `AnnotationAwareOrderComparator`, as described in xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-ordering[Ordering `TestExecutionListener` Implementations]. +of `AnnotationAwareOrderComparator`, as described in +xref:testing/testcontext-framework/tel-config.adoc#testcontext-tel-config-ordering[Ordering `TestExecutionListener` Implementations]. If a listener implements `Ordered` or is annotated with `@Order`, it can influence the position in which it is merged with the defaults. Otherwise, locally declared listeners are appended to the list of default listeners when merged. diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java index ad8b6ba180..d21a816895 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,9 @@ package org.springframework.test.context; * the listener is registered. * *

Note that not all testing frameworks support all lifecycle callbacks defined - * in this API. For example, {@link #beforeTestExecution} and - * {@link #afterTestExecution} are not supported in conjunction with JUnit 4 when + * in this API. For example, the {@link #beforeTestExecution(TestContext) + * beforeTestExecution} and {@link #afterTestExecution(TestContext) + * afterTestExecution} callbacks are not supported in conjunction with JUnit 4 when * using the {@link org.springframework.test.context.junit4.rules.SpringMethodRule * SpringMethodRule}. * From c5ecc50bfe51644cf9bf2509cb6b83a134db5682 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Thu, 6 Mar 2025 13:31:23 +0100 Subject: [PATCH 2/2] Document wrapping behavior for TestExecutionListener callbacks Closes gh-34422 --- .../test/context/TestExecutionListener.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java index d21a816895..c3fbbeff81 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java @@ -42,6 +42,35 @@ package org.springframework.test.context; * {@link org.springframework.core.annotation.Order @Order} annotation. See * {@link TestContextBootstrapper#getTestExecutionListeners()} for details. * + *

Wrapping Behavior for Listeners

+ * + *

The {@link TestContextManager} guarantees wrapping behavior for + * multiple registered listeners that implement lifecycle callbacks such as + * {@link #beforeTestClass(TestContext) beforeTestClass}, + * {@link #afterTestClass(TestContext) afterTestClass}, + * {@link #beforeTestMethod(TestContext) beforeTestMethod}, + * {@link #afterTestMethod(TestContext) afterTestMethod}, + * {@link #beforeTestExecution(TestContext) beforeTestExecution}, and + * {@link #afterTestExecution(TestContext) afterTestExecution}. This means that, + * given two listeners {@code Listener1} and {@code Listener2} with {@code Listener1} + * registered before {@code Listener2}, any before callbacks implemented + * by {@code Listener1} are guaranteed to be invoked before any + * before callbacks implemented by {@code Listener2}. Similarly, given + * the same two listeners registered in the same order, any after + * callbacks implemented by {@code Listener1} are guaranteed to be invoked + * after any after callbacks implemented by + * {@code Listener2}. {@code Listener1} is therefore said to wrap + * {@code Listener2}. + * + *

For a concrete example, consider the relationship between the + * {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener + * TransactionalTestExecutionListener} and the + * {@link org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener + * SqlScriptsTestExecutionListener}. The {@code SqlScriptsTestExecutionListener} + * is registered after the {@code TransactionalTestExecutionListener}, so that + * SQL scripts are executed within a transaction managed by the + * {@code TransactionalTestExecutionListener}. + * *

Registering TestExecutionListener Implementations

* *

A {@code TestExecutionListener} can be registered explicitly for a test class, @@ -101,6 +130,8 @@ public interface TestExecutionListener { * the class. *

This method should be called immediately before framework-specific * before class lifecycle callbacks. + *

See the {@linkplain TestExecutionListener class-level documentation} + * for details on wrapping behavior for lifecycle callbacks. *

The default implementation is empty. Can be overridden by * concrete classes as necessary. * @param testContext the test context for the test; never {@code null} @@ -119,6 +150,8 @@ public interface TestExecutionListener { * {@link org.springframework.test.context.junit4.rules.SpringMethodRule * SpringMethodRule}). In any case, this method must be called prior to any * framework-specific lifecycle callbacks. + *

See the {@linkplain TestExecutionListener class-level documentation} + * for details on wrapping behavior for listeners. *

The default implementation is empty. Can be overridden by * concrete classes as necessary. * @param testContext the test context for the test; never {@code null} @@ -138,6 +171,8 @@ public interface TestExecutionListener { * this method might be something like {@code beforeTestSetUp} or * {@code beforeEach}; however, it is unfortunately impossible to rename * this method due to backward compatibility concerns. + *

See the {@linkplain TestExecutionListener class-level documentation} + * for details on wrapping behavior for lifecycle callbacks. *

The default implementation is empty. Can be overridden by * concrete classes as necessary. * @param testContext the test context in which the test method will be @@ -157,6 +192,8 @@ public interface TestExecutionListener { * or logging purposes. *

This method must be called after framework-specific * before lifecycle callbacks. + *

See the {@linkplain TestExecutionListener class-level documentation} + * for details on wrapping behavior for lifecycle callbacks. *

The default implementation is empty. Can be overridden by * concrete classes as necessary. * @param testContext the test context in which the test method will be @@ -177,6 +214,8 @@ public interface TestExecutionListener { * or logging purposes. *

This method must be called before framework-specific * after lifecycle callbacks. + *

See the {@linkplain TestExecutionListener class-level documentation} + * for details on wrapping behavior for lifecycle callbacks. *

The default implementation is empty. Can be overridden by * concrete classes as necessary. * @param testContext the test context in which the test method will be @@ -201,6 +240,8 @@ public interface TestExecutionListener { * this method might be something like {@code afterTestTearDown} or * {@code afterEach}; however, it is unfortunately impossible to rename * this method due to backward compatibility concerns. + *

See the {@linkplain TestExecutionListener class-level documentation} + * for details on wrapping behavior for lifecycle callbacks. *

The default implementation is empty. Can be overridden by * concrete classes as necessary. * @param testContext the test context in which the test method was @@ -218,6 +259,8 @@ public interface TestExecutionListener { * the class. *

This method should be called immediately after framework-specific * after class lifecycle callbacks. + *

See the {@linkplain TestExecutionListener class-level documentation} + * for details on wrapping behavior for lifecycle callbacks. *

The default implementation is empty. Can be overridden by * concrete classes as necessary. * @param testContext the test context for the test; never {@code null}