From 6fbc0dee3556b51f24b0f2fbd0f3d82e3dbd8a15 Mon Sep 17 00:00:00 2001 From: Stijn Dejongh Date: Sun, 22 Sep 2024 14:59:08 +0200 Subject: [PATCH] Add tests for the ReflectionTestUtils class --- .../testing/ReflectionAssertionUtils.java | 23 ++-- .../testing/ReflectionAssertionUtilsTest.java | 125 ++++++++++++++++++ 2 files changed, 138 insertions(+), 10 deletions(-) create mode 100755 commons-testing/src/test/java/be/sddevelopment/commons/testing/ReflectionAssertionUtilsTest.java diff --git a/commons-testing/src/main/java/be/sddevelopment/commons/testing/ReflectionAssertionUtils.java b/commons-testing/src/main/java/be/sddevelopment/commons/testing/ReflectionAssertionUtils.java index 94c307b..4054d00 100755 --- a/commons-testing/src/main/java/be/sddevelopment/commons/testing/ReflectionAssertionUtils.java +++ b/commons-testing/src/main/java/be/sddevelopment/commons/testing/ReflectionAssertionUtils.java @@ -50,27 +50,30 @@ */ public final class ReflectionAssertionUtils { + public static final String PROHIBITED_CONSTRUCTOR_ACCESS = + "Utility classes should not have a public or default constructor"; + private ReflectionAssertionUtils() { - throw new UnsupportedOperationException( - "Utility classes should not have a public or default constructor"); + throw new UnsupportedOperationException("Utility classes should not have a public or default constructor"); } public static void assertPrivateMemberReflectionProtection( @SuppressWarnings("rawtypes") final Constructor constructor) { constructor.setAccessible(true); - assertThatThrownBy(constructor::newInstance) + + assertThatThrownBy(constructor::newInstance, unprotectedConstructorError(constructor)) .isInstanceOf(InvocationTargetException.class) .hasCauseInstanceOf(UnsupportedOperationException.class) - .hasStackTraceContaining( - "This operation is not allowed for reason: [ Utility classes should not have a public or default constructor ]"); + .hasStackTraceContaining(PROHIBITED_CONSTRUCTOR_ACCESS); } public static void assertPrivateMember( @SuppressWarnings("rawtypes") final Constructor constructor) { - assertThatThrownBy(constructor::newInstance, - format("Constructor %s is expected to be protected from illegal access", - constructor - ) - ).isInstanceOfAny(InvocationTargetException.class, IllegalAccessException.class); + assertThatThrownBy(constructor::newInstance, unprotectedConstructorError(constructor)) + .isInstanceOfAny(InvocationTargetException.class, IllegalAccessException.class); + } + + private static String unprotectedConstructorError(final Constructor constructor) { + return format("Constructor %s is expected to be protected from illegal access", constructor); } } diff --git a/commons-testing/src/test/java/be/sddevelopment/commons/testing/ReflectionAssertionUtilsTest.java b/commons-testing/src/test/java/be/sddevelopment/commons/testing/ReflectionAssertionUtilsTest.java new file mode 100755 index 0000000..3f9a32c --- /dev/null +++ b/commons-testing/src/test/java/be/sddevelopment/commons/testing/ReflectionAssertionUtilsTest.java @@ -0,0 +1,125 @@ +/*- + * #%L + * commons-testing + * %% + * Copyright (C) 2020 - 2024 SD Development + * %% + * Licensed under the EUPL, Version 1.1 or – as soon they will be + * approved by the European Commission - subsequent versions of the + * EUPL (the "Licence"); + * + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * http://ec.europa.eu/idabc/eupl5 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * #L% + */ + +package be.sddevelopment.commons.testing; + +import static be.sddevelopment.commons.testing.ReflectionAssertionUtils.assertPrivateMember; +import static be.sddevelopment.commons.testing.ReflectionAssertionUtils.assertPrivateMemberReflectionProtection; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.assertj.core.api.WithAssertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(ReplaceUnderscoredCamelCasing.class) +class ReflectionAssertionUtilsTest implements WithAssertions { + + @Test + void constructorIsPrivate() throws NoSuchMethodException { + assertPrivateMember(ReflectionAssertionUtils.class.getDeclaredConstructor()); + } + + @Test + void constructorIsReflectionSafe() throws NoSuchMethodException { + assertPrivateMemberReflectionProtection(ReflectionAssertionUtils.class.getDeclaredConstructor()); + } + + @Nested + @DisplayName("Method: assertPrivateMember") + class AssertPrivateMember { + @Test + void passesWhenConstructorIsPrivate() throws NoSuchMethodException { + var constructorToTest = ClassWithPrivateConstructor.class.getDeclaredConstructor(); + assertThat(constructorToTest).isNotNull().extracting(Constructor::getModifiers).matches(Modifier::isPrivate); + + ThrowingCallable assertion = () -> assertPrivateMember(constructorToTest); + + assertThatCode(assertion).doesNotThrowAnyException(); + } + + @Test + void failsWhenConstructorIsPublic() throws NoSuchMethodException { + var constructorToTest = ClassWithPublicConstructor.class.getDeclaredConstructor(); + assertThat(constructorToTest).isNotNull().extracting(Constructor::getModifiers).matches(Modifier::isPublic); + + ThrowingCallable assertion = () -> assertPrivateMember(constructorToTest); + + assertThatCode(assertion).isInstanceOf(AssertionError.class).hasMessageContaining("is expected to be protected from illegal access"); + } + } + + @Nested + @DisplayName("Method: assertPrivateMemberReflectionProtection") + class AssertPrivateMemberReflectionProtection { + @Test + void failsWhenConstructorIsPublic() throws NoSuchMethodException { + var constructorToTest = ClassWithPublicConstructor.class.getDeclaredConstructor(); + assertThat(constructorToTest).isNotNull().extracting(Constructor::getModifiers).matches(Modifier::isPublic); + + ThrowingCallable assertion = () -> assertPrivateMemberReflectionProtection(constructorToTest); + + assertThatCode(assertion).isInstanceOf(AssertionError.class).hasMessageContaining("is expected to be protected from illegal access"); + } + + @Test + void failsWhenConstructorIsPrivateButNotReflectionSafe() throws NoSuchMethodException { + var constructorToTest = ClassWithPrivateConstructor.class.getDeclaredConstructor(); + assertThat(constructorToTest).isNotNull().extracting(Constructor::getModifiers).matches(Modifier::isPrivate); + + ThrowingCallable assertion = () -> assertPrivateMemberReflectionProtection(constructorToTest); + + assertThatCode(assertion).isNotNull().isInstanceOf(AssertionError.class).hasMessageContaining("is expected to be protected from illegal access"); + } + + @Test + void passesWhenConstructorIsPrivateAndReflectionSafe() throws NoSuchMethodException { + var constructorToTest = ClassWithReflectionSafeConstructor.class.getDeclaredConstructor(); + assertThat(constructorToTest).isNotNull().extracting(Constructor::getModifiers).matches(Modifier::isPrivate); + + ThrowingCallable assertion = () -> assertPrivateMemberReflectionProtection(constructorToTest); + + assertThatCode(assertion).doesNotThrowAnyException(); + } + } + + static final class ClassWithReflectionSafeConstructor { + private ClassWithReflectionSafeConstructor() { + throw new UnsupportedOperationException("Utility classes should not have a public or default constructor"); + } + } + + static final class ClassWithPrivateConstructor { + private ClassWithPrivateConstructor() { + } + } + + static final class ClassWithPublicConstructor { + public ClassWithPublicConstructor() { + } + } + +}