ReflectionUtilsTest.java
package io.swagger.v3.core.util.reflection;
import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.v3.core.util.ReflectionUtils;
import io.swagger.v3.core.util.reflection.resources.Child;
import io.swagger.v3.core.util.reflection.resources.IParent;
import io.swagger.v3.core.util.reflection.resources.ObjectWithManyFields;
import io.swagger.v3.core.util.reflection.resources.Parent;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.testng.annotations.Test;
import javax.ws.rs.Path;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static java.lang.annotation.ElementType.PARAMETER;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
public class ReflectionUtilsTest {
@Test
public void typeFromStringTest() {
assertEquals(ReflectionUtils.typeFromString("int"), (Type) Integer.class);
assertEquals(ReflectionUtils.typeFromString("java.lang.String"), (Type) String.class);
assertNull(ReflectionUtils.typeFromString("FakeType"));
assertNull(ReflectionUtils.typeFromString(null));
}
@Test
public void isOverriddenMethodTest() throws NoSuchMethodException {
for (Method method : Child.class.getMethods()) {
if ("parametrizedMethod1".equals(method.getName())) {
final boolean result = ReflectionUtils.isOverriddenMethod(method, Child.class);
final Class<?> first = method.getParameterTypes()[0];
if (Number.class.equals(first)) {
assertTrue(result);
} else if (Integer.class.equals(first)) {
assertFalse(result);
}
} else if ("parametrizedMethod3".equals(method.getName())) {
assertFalse(ReflectionUtils.isOverriddenMethod(method, Child.class));
}
}
for (Method method : Object.class.getMethods()) {
if ("equals".equals(method.getName())) {
assertFalse(ReflectionUtils.isOverriddenMethod(method, Object.class));
}
}
for (Method method : IParent.class.getMethods()) {
if ("parametrizedMethod5".equals(method.getName())) {
assertFalse(ReflectionUtils.isOverriddenMethod(method, IParent.class));
} else if ("parametrizedMethod2".equals(method.getName())) {
assertFalse(ReflectionUtils.isOverriddenMethod(method, IParent.class));
} else {
fail("Method not expected");
}
}
}
@Test
public void getOverriddenMethodTest() throws NoSuchMethodException {
final Method method1 = ReflectionUtils.getOverriddenMethod(
Child.class.getMethod("parametrizedMethod1", Integer.class));
assertNotNull(method1);
assertEquals(method1.getParameterTypes()[0], Number.class);
final Method method2 = ReflectionUtils.getOverriddenMethod(
Child.class.getMethod("parametrizedMethod2", Long.class));
assertNotNull(method2);
assertEquals(method2.getParameterTypes()[0], Number.class);
final Method method3 = ReflectionUtils.getOverriddenMethod(
Child.class.getMethod("parametrizedMethod3", Long.class));
assertNull(method3);
assertNull(ReflectionUtils.getOverriddenMethod(Object.class.getMethod("equals", Object.class)));
}
@Test
public void findMethodTest() throws NoSuchMethodException {
final Method methodToFind1 = Child.class.getMethod("parametrizedMethod1", Integer.class);
final Method method1 = ReflectionUtils.findMethod(methodToFind1, Parent.class);
assertNotNull(method1);
assertEquals(method1.getParameterTypes()[0], Number.class);
final Method methodToFind2 = Child.class.getMethod("parametrizedMethod4", Long.class);
final Method method2 = ReflectionUtils.findMethod(methodToFind2, Parent.class);
assertNull(method2);
}
@Test
public void isInjectTest() throws NoSuchMethodException {
final Method injectableMethod = Child.class.getMethod("injectableMethod");
assertTrue(ReflectionUtils.isInject(Arrays.asList(injectableMethod.getDeclaredAnnotations())));
final Method methodToFind = Child.class.getMethod("parametrizedMethod1", Integer.class);
assertFalse(ReflectionUtils.isInject(Arrays.asList(methodToFind.getDeclaredAnnotations())));
}
@Test
public void isConstructorCompatibleTest() throws NoSuchMethodException {
assertFalse(ReflectionUtils.isConstructorCompatible(Child.class.getDeclaredConstructor()));
assertTrue(ReflectionUtils.isConstructorCompatible(Child.class.getDeclaredConstructor(String.class)));
}
@Test
public void getAnnotationTest() throws NoSuchMethodException {
final Method method = Child.class.getMethod("annotationHolder");
assertNotNull(ReflectionUtils.getAnnotation(method, Schema.class));
assertNull(ReflectionUtils.getAnnotation(method, ApiResponse.class));
}
@Test
public void isVoidTest() {
assertTrue(ReflectionUtils.isVoid(Void.class));
assertTrue(ReflectionUtils.isVoid(Void.TYPE));
assertFalse(ReflectionUtils.isVoid(String.class));
}
@Test
public void testDerivedAnnotation() {
final Path annotation = ReflectionUtils.getAnnotation(Child.class, javax.ws.rs.Path.class);
assertNotNull(annotation);
assertEquals(annotation.value(), "parentInterfacePath");
}
@Test
public void getDeclaredFieldsFromInterfaceTest() throws NoSuchMethodException {
final Class cls = IParent.class;
assertEquals(Collections.emptyList(), ReflectionUtils.getDeclaredFields(cls));
}
@Test
public void declaredFieldsShouldBeSorted() {
final Class cls = ObjectWithManyFields.class;
final List<Field> declaredFields = ReflectionUtils.getDeclaredFields(cls);
assertEquals(declaredFields.size(), 4);
assertEquals(Arrays.asList("a", "b", "c", "d"), declaredFields.stream().map(Field::getName).collect(Collectors.toList()));
}
@Test
public void testFindMethodForNullClass() throws Exception {
Method method = ReflectionUtilsTest.class.getMethod("testFindMethodForNullClass", (Class<?>[]) null);
assertNull(ReflectionUtils.findMethod(method, null));
}
@Test
public void getRepeatableAnnotationsArrayTest() {
Tag[] annotations = ReflectionUtils.getRepeatableAnnotationsArray(InheritingClass.class, Tag.class);
assertNotNull(annotations);
assertEquals(annotations.length, 1);
assertNotNull(annotations[0]);
assertEquals(annotations[0].name(), "inherited tag");
}
@Test
public void getParameterAnnotationsTest() throws NoSuchMethodException {
Method method = SecondLevelSubClass.class.getMethod("method", String.class);
Annotation[][] parameterAnnotations = ReflectionUtils.getParameterAnnotations(method);
assertEquals(parameterAnnotations.length, 1);
assertEquals(parameterAnnotations[0].length, 1);
assertTrue(parameterAnnotations[0][0] instanceof AnnotationInterface);
assertEquals(((AnnotationInterface)parameterAnnotations[0][0]).value(), "level1");
}
@Test
public void getParameterAnnotationsForOverriddenAnnotationTest() throws NoSuchMethodException {
Method method = ThirdLevelSubClass.class.getMethod("method", String.class);
Annotation[][] parameterAnnotations = ReflectionUtils.getParameterAnnotations(method);
assertEquals(parameterAnnotations.length, 1);
assertEquals(parameterAnnotations[0].length, 1);
assertTrue(parameterAnnotations[0][0] instanceof AnnotationInterface);
assertEquals(((AnnotationInterface)parameterAnnotations[0][0]).value(), "level4");
}
@Test
public void getAnnotatedMethods_returnsEmptyWhenNoMethodHasAnnotation() {
List<Method> methods = ReflectionUtils.getAnnotatedMethods(String.class, JsonValue.class);
assertTrue(methods.isEmpty());
}
@Test
public void getAnnotatedMethods_findsAnnotationInSuperclass() {
List<Method> methods = ReflectionUtils.getAnnotatedMethods(SubclassWithoutJsonValue.class, JsonValue.class);
assertEquals(methods.size(), 1);
assertEquals(methods.get(0).getName(), "getJsonValue");
}
@Test
public void getAnnotatedMethods_findsMultipleMatchesAcrossClassHierarchy() {
List<Method> methods = ReflectionUtils.getAnnotatedMethods(SubclassWithOwnJsonValue.class, JsonValue.class);
assertEquals(methods.size(), 2);
List<String> names = methods.stream().map(Method::getName).collect(Collectors.toList());
assertTrue(names.contains("getJsonValue"));
assertTrue(names.contains("getSubJsonValue"));
}
@Test
public void getAnnotatedMethods_findsDefaultMethodAnnotationFromInterface() {
List<Method> methods = ReflectionUtils.getAnnotatedMethods(ImplementorWithoutOverride.class, JsonValue.class);
assertEquals(methods.size(), 1);
assertEquals(methods.get(0).getName(), "toValue");
}
@Test
public void getAnnotatedMethods_excludesBridgeMethodsFromGenericInterface() {
// GenericValueEnum implements PersistableEnum<String> which causes the compiler
// to generate a bridge method Object getValue() alongside String getValue().
// Both may carry @JsonValue, but only the non-bridge method should be returned.
List<Method> methods = ReflectionUtils.getAnnotatedMethods(GenericValueEnum.class, JsonValue.class);
assertEquals(methods.size(), 1, "should find exactly one @JsonValue method (excluding bridge)");
assertEquals(methods.get(0).getReturnType(), String.class, "should find the String-returning method, not the Object bridge");
}
// --- Support classes for getAnnotatedMethods tests ---
private interface GenericPersistableEnum<T> {
T getValue();
}
private enum GenericValueEnum implements GenericPersistableEnum<String> {
A("alpha"), B("beta");
private final String value;
GenericValueEnum(String value) { this.value = value; }
@JsonValue
@Override
public String getValue() { return value; }
}
private static class SuperclassWithJsonValue {
@JsonValue
public String getJsonValue() { return "super"; }
}
private static class SubclassWithoutJsonValue extends SuperclassWithJsonValue {
public String getOtherMethod() { return "other"; }
}
private static class SubclassWithOwnJsonValue extends SuperclassWithJsonValue {
@JsonValue
public String getSubJsonValue() { return "sub"; }
}
private interface InterfaceWithDefaultJsonValue {
@JsonValue
default String toValue() { return "from-interface"; }
}
private static class ImplementorWithoutOverride implements InterfaceWithDefaultJsonValue {}
// ---
@Tag(name = "inherited tag")
private interface AnnotatedInterface {}
private class InheritingClass implements AnnotatedInterface {}
@Target({PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
private @interface AnnotationInterface {
String value();
}
private static class BaseClass {
public void method(@AnnotationInterface("level1") String example) {}
}
private static class FirstLevelSubClass extends BaseClass {
@Override
public void method(String example){}
}
private static class SecondLevelSubClass extends FirstLevelSubClass {
@Override
public void method(String example){}
}
private static class ThirdLevelSubClass extends SecondLevelSubClass {
@Override
public void method(@AnnotationInterface("level4") String example){}
}
}