diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java index b35dc58c20d8..357edb731c6e 100644 --- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java +++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java @@ -19,6 +19,7 @@ import static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields; import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation; import static org.junit.platform.commons.support.ReflectionSupport.makeAccessible; +import static org.junit.platform.commons.util.ReflectionUtils.isRecordObject; import java.io.File; import java.io.IOException; @@ -135,7 +136,9 @@ private void injectStaticFields(ExtensionContext context, Class testClass) { } private void injectInstanceFields(ExtensionContext context, Object instance) { - injectFields(context, instance, instance.getClass(), ModifierSupport::isNotStatic); + if (!isRecordObject(instance)) { + injectFields(context, instance, instance.getClass(), ModifierSupport::isNotStatic); + } } private void injectFields(ExtensionContext context, Object testInstance, Class testClass, diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java index d0c9921d92e3..01f0e89d0fd2 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java @@ -365,6 +365,25 @@ public static boolean isInnerClass(Class clazz) { return !isStatic(clazz) && clazz.isMemberClass(); } + /** + * {@return whether the supplied {@code object} is an instance of a record class} + * @since 1.12 + */ + @API(status = INTERNAL, since = "1.12") + public static boolean isRecordObject(Object object) { + return object != null && isRecordClass(object.getClass()); + } + + /** + * {@return whether the supplied {@code clazz} is a record class} + * @since 1.12 + */ + @API(status = INTERNAL, since = "1.12") + public static boolean isRecordClass(Class clazz) { + Class superclass = clazz.getSuperclass(); + return superclass != null && "java.lang.Record".equals(superclass.getName()); + } + /** * Determine if the return type of the supplied method is primitive {@code void}. * diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java index 2deb303768b9..5eddab66bcad 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPerDeclarationTests.java @@ -164,6 +164,12 @@ void resolvesSeparateTempDirsForEachAnnotationDeclaration(TestInstance.Lifecycle assertThat(testATempDirs).doesNotContainEntry("afterEach2", testBTempDirs.get("afterEach2")); } + @Test + void supportsConstructorInjectionOnRecords() { + executeTestsForClass(TempDirRecordTestCase.class).testEvents()// + .assertStatistics(stats -> stats.started(1).succeeded(1)); + } + @Test @DisplayName("does not prevent constructor parameter resolution") void tempDirectoryDoesNotPreventConstructorParameterResolution() { @@ -1527,4 +1533,12 @@ void test(@SuppressWarnings("unused") @TempDir Path tempDir) { } + @SuppressWarnings("JUnitMalformedDeclaration") + record TempDirRecordTestCase(@TempDir Path tempDir) { + @Test + void shouldExists() { + assertTrue(Files.exists(tempDir)); + } + } + } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java index fb45c4835725..6b89d7aaeb1d 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java @@ -285,6 +285,23 @@ void getInterfaceMethodIfPossible() throws Exception { assertThat(interfaceMethod.getDeclaringClass()).isEqualTo(Closeable.class); } + @Test + void isRecordObject() { + assertTrue(ReflectionUtils.isRecordObject(new SomeRecord(1))); + assertFalse(ReflectionUtils.isRecordObject(new ClassWithOneCustomConstructor(""))); + assertFalse(ReflectionUtils.isRecordObject(null)); + } + + @Test + void isRecordClass() { + assertTrue(ReflectionUtils.isRecordClass(SomeRecord.class)); + assertFalse(ReflectionUtils.isRecordClass(ClassWithOneCustomConstructor.class)); + assertFalse(ReflectionUtils.isRecordClass(Object.class)); + } + + record SomeRecord(int n) { + } + static class ClassWithVoidAndNonVoidMethods { void voidMethod() {