diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc index ba8710106188..0d0100dcf0df 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc @@ -44,7 +44,17 @@ JUnit repository on GitHub. [[release-notes-5.12.0-M1-junit-jupiter-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes -* ❓ +* When injecting `TestInfo` into test class constructors it now contains data of the test + method the test class instance is being created for unless the test instance lifecycle + is set to `PER_CLASS` (in which case it continues to contain the data of the test + class). If you require the `TestInfo` of the test class, you can implement a class-level + lifecycle method (e.g., `@BeforeAll`) and inject `TestInfo` into that method. +* When injecting `TestReporter` into test class constructors the published report entries + are now associated with the test method rather than the test class unless the test + instance lifecycle is set to `PER_CLASS` (in which case they will continue to be + associated with the test class). If you want to publish report entries for the test + class, you can implement a class-level lifecycle method (e.g., `@BeforeAll`) and inject + `TestReporter` into that method. [[release-notes-5.12.0-M1-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements diff --git a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc index ccb0f5344588..8df05f70c3ba 100644 --- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc @@ -1058,8 +1058,8 @@ There are currently three built-in resolvers that are registered automatically. method, or a custom name configured via `@DisplayName`. + `{TestInfo}` acts as a drop-in replacement for the `TestName` rule from JUnit 4. The -following demonstrates how to have `TestInfo` injected into a test constructor, -`@BeforeEach` method, and `@Test` method. +following demonstrates how to have `TestInfo` injected into a `@BeforeAll` method, test +class constructor, `@BeforeEach` method, and `@Test` method. [source,java,indent=0] ---- diff --git a/documentation/src/test/java/example/TestInfoDemo.java b/documentation/src/test/java/example/TestInfoDemo.java index eae99ae63afc..a77cfb48b52a 100644 --- a/documentation/src/test/java/example/TestInfoDemo.java +++ b/documentation/src/test/java/example/TestInfoDemo.java @@ -14,6 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; @@ -23,10 +24,16 @@ @DisplayName("TestInfo Demo") class TestInfoDemo { - TestInfoDemo(TestInfo testInfo) { + @BeforeAll + static void beforeAll(TestInfo testInfo) { assertEquals("TestInfo Demo", testInfo.getDisplayName()); } + TestInfoDemo(TestInfo testInfo) { + String displayName = testInfo.getDisplayName(); + assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()")); + } + @BeforeEach void init(TestInfo testInfo) { String displayName = testInfo.getDisplayName(); diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java index e4975f866d52..6e1682db8ec6 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java @@ -45,6 +45,11 @@ class RepetitionExtension implements ParameterResolver, TestWatcher, ExecutionCo this.repetitionInfo = repetitionInfo; } + @Override + public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) { + return ExtensionContextScope.TEST_METHOD; + } + @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return (parameterContext.getParameter().getType() == RepetitionInfo.class); diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java index 6805daece4a1..96b7a617db4b 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java @@ -28,6 +28,11 @@ */ class TestInfoParameterResolver implements ParameterResolver { + @Override + public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) { + return ExtensionContextScope.TEST_METHOD; + } + @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return (parameterContext.getParameter().getType() == TestInfo.class); diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java index 57e402da37e8..c06536d448dc 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java @@ -22,6 +22,11 @@ */ class TestReporterParameterResolver implements ParameterResolver { + @Override + public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) { + return ExtensionContextScope.TEST_METHOD; + } + @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return (parameterContext.getParameter().getType() == TestReporter.class); diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java index f9b23e0e4cbd..f8b87a62bed8 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java @@ -45,6 +45,11 @@ class TimeoutExtension implements BeforeAllCallback, BeforeEachCallback, Invocat private static final String DISABLED_MODE_VALUE = "disabled"; private static final String DISABLED_ON_DEBUG_MODE_VALUE = "disabled_on_debug"; + @Override + public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) { + return ExtensionContextScope.TEST_METHOD; + } + @Override public void beforeAll(ExtensionContext context) { readAndStoreTimeoutSoChildrenInheritIt(context); diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java index f4ea4a177ec6..10e0085c1a73 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestParameterResolver.java @@ -44,6 +44,11 @@ class ParameterizedTestParameterResolver implements ParameterResolver, AfterTest this.invocationIndex = invocationIndex; } + @Override + public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) { + return ExtensionContextScope.TEST_METHOD; + } + @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { Executable declaringExecutable = parameterContext.getDeclaringExecutable(); diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/ReportingTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/ReportingTests.java index 534cb625f481..19176dd10003 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/ReportingTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/ReportingTests.java @@ -12,6 +12,9 @@ import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; import java.util.HashMap; import java.util.Map; @@ -19,7 +22,10 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.TestReporter; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.platform.commons.PreconditionViolationException; /** @@ -27,18 +33,32 @@ */ class ReportingTests extends AbstractJupiterTestEngineTests { - @Test - void reportEntriesArePublished() { - executeTestsForClass(MyReportingTestCase.class).testEvents().assertStatistics(stats -> stats // - .started(2) // - .succeeded(2) // - .failed(0) // - .reportingEntryPublished(7)); + @ParameterizedTest + @CsvSource(textBlock = """ + PER_CLASS, 7 + PER_METHOD, 9 + """) + void reportEntriesArePublished(Lifecycle lifecycle, int expectedReportEntryCount) { + var request = request() // + .selectors(selectClass(MyReportingTestCase.class)) // + .configurationParameter(DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, lifecycle.name()); + executeTests(request) // + .testEvents() // + .assertStatistics(stats -> stats // + .started(2) // + .succeeded(2) // + .failed(0) // + .reportingEntryPublished(expectedReportEntryCount)); } @SuppressWarnings("JUnitMalformedDeclaration") static class MyReportingTestCase { + public MyReportingTestCase(TestReporter reporter) { + // Reported on class-level for PER_CLASS lifecycle and on method-level for PER_METHOD lifecycle + reporter.publishEntry("Constructor"); + } + @BeforeEach void beforeEach(TestReporter reporter) { reporter.publishEntry("@BeforeEach"); diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java index df7b9f309014..410255454622 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java @@ -12,6 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.engine.Constants.DEFAULT_PARALLEL_EXECUTION_MODE; import static org.junit.jupiter.engine.Constants.PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME; @@ -120,6 +121,11 @@ static void afterAll() { assertEquals(42, fortyTwo); } + // Can be injected into test class constructors if the test class only has @RepeatedTest methods + public LifecycleMethodTests(RepetitionInfo repetitionInfo) { + assertNotNull(repetitionInfo); + } + @BeforeEach @AfterEach void beforeAndAfterEach(TestInfo testInfo, RepetitionInfo repetitionInfo) { diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java index 2a66c6f4ae2a..63a6cf8b985c 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java @@ -35,8 +35,13 @@ @Tag("class-tag") class TestInfoParameterResolverTests { - private static List allDisplayNames = Arrays.asList("defaultDisplayName(TestInfo)", "custom display name", - "getTags(TestInfo)", "customDisplayNameThatIsEmpty(TestInfo)"); + private static final List allDisplayNames = Arrays.asList("defaultDisplayName(TestInfo)", + "custom display name", "getTags(TestInfo)", "customDisplayNameThatIsEmpty(TestInfo)"); + + public TestInfoParameterResolverTests(TestInfo testInfo) { + assertThat(testInfo.getTestClass()).contains(TestInfoParameterResolverTests.class); + assertThat(testInfo.getTestMethod()).isPresent(); + } @Test void defaultDisplayName(TestInfo testInfo) { diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index 9e5c5a8e6910..c95e28b2dd63 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -404,20 +404,20 @@ void executesLifecycleMethods() { assertThat(LifecycleTestCase.lifecycleEvents).containsExactly( "beforeAll:ParameterizedTestIntegrationTests$LifecycleTestCase", "providerMethod", - "constructor:ParameterizedTestIntegrationTests$LifecycleTestCase", + "constructor:[1] argument=foo", "beforeEach:[1] argument=foo", testMethods.get(0) + ":[1] argument=foo", "afterEach:[1] argument=foo", - "constructor:ParameterizedTestIntegrationTests$LifecycleTestCase", + "constructor:[2] argument=bar", "beforeEach:[2] argument=bar", testMethods.get(0) + ":[2] argument=bar", "afterEach:[2] argument=bar", "providerMethod", - "constructor:ParameterizedTestIntegrationTests$LifecycleTestCase", + "constructor:[1] argument=foo", "beforeEach:[1] argument=foo", testMethods.get(1) + ":[1] argument=foo", "afterEach:[1] argument=foo", - "constructor:ParameterizedTestIntegrationTests$LifecycleTestCase", + "constructor:[2] argument=bar", "beforeEach:[2] argument=bar", testMethods.get(1) + ":[2] argument=bar", "afterEach:[2] argument=bar",