提交 5ce75f3d 编写于 作者: S Sam Brannen

Support static methods with ReflectionTestUtils.invokeMethod()

Prior to this commit, the invokeMethod() utility method in
ReflectionTestUtils only supported instance methods.

This commit brings the invokeMethod() support on par with the getField()
support by supporting the invocation of static methods via two new
invokeMethod() variants.

Closes gh-23504
上级 d4360db4
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 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.
......@@ -403,14 +403,65 @@ public abstract class ReflectionTestUtils {
/**
* Invoke the method with the given {@code name} on the supplied target
* object with the supplied arguments.
* <p>This method delegates to {@link #invokeMethod(Object, Class, String, Object...)},
* supplying {@code null} for the {@code targetClass} argument.
* @param target the target object on which to invoke the specified method
* @param name the name of the method to invoke
* @param args the arguments to provide to the method
* @return the invocation result, if any
* @see #invokeMethod(Class, String, Object...)
* @see #invokeMethod(Object, Class, String, Object...)
* @see MethodInvoker
* @see ReflectionUtils#makeAccessible(Method)
* @see ReflectionUtils#invokeMethod(Method, Object, Object[])
* @see ReflectionUtils#handleReflectionException(Exception)
*/
@Nullable
public static <T> T invokeMethod(Object target, String name, Object... args) {
Assert.notNull(target, "Target object must not be null");
return invokeMethod(target, null, name, args);
}
/**
* Invoke the static method with the given {@code name} on the supplied target
* class with the supplied arguments.
* <p>This method delegates to {@link #invokeMethod(Object, Class, String, Object...)},
* supplying {@code null} for the {@code targetObject} argument.
* @param targetClass the target class on which to invoke the specified method
* @param name the name of the method to invoke
* @param args the arguments to provide to the method
* @return the invocation result, if any
* @since 5.2
* @see #invokeMethod(Object, String, Object...)
* @see #invokeMethod(Object, Class, String, Object...)
* @see MethodInvoker
* @see ReflectionUtils#makeAccessible(Method)
* @see ReflectionUtils#invokeMethod(Method, Object, Object[])
* @see ReflectionUtils#handleReflectionException(Exception)
*/
@Nullable
public static <T> T invokeMethod(Class<?> targetClass, String name, Object... args) {
Assert.notNull(targetClass, "Target class must not be null");
return invokeMethod(null, targetClass, name, args);
}
/**
* Invoke the method with the given {@code name} on the provided
* {@code targetObject}/{@code targetClass} with the supplied arguments.
* <p>This method traverses the class hierarchy in search of the desired
* method. In addition, an attempt will be made to make non-{@code public}
* methods <em>accessible</em>, thus allowing one to invoke {@code protected},
* {@code private}, and <em>package-private</em> methods.
* @param target the target object on which to invoke the specified method
* @param targetObject the target object on which to invoke the method; may
* be {@code null} if the method is static
* @param targetClass the target class on which to invoke the method; may
* be {@code null} if the method is an instance method
* @param name the name of the method to invoke
* @param args the arguments to provide to the method
* @return the invocation result, if any
* @since 5.2
* @see #invokeMethod(Object, String, Object...)
* @see #invokeMethod(Class, String, Object...)
* @see MethodInvoker
* @see ReflectionUtils#makeAccessible(Method)
* @see ReflectionUtils#invokeMethod(Method, Object, Object[])
......@@ -418,20 +469,26 @@ public abstract class ReflectionTestUtils {
*/
@SuppressWarnings("unchecked")
@Nullable
public static <T> T invokeMethod(Object target, String name, Object... args) {
Assert.notNull(target, "Target object must not be null");
public static <T> T invokeMethod(@Nullable Object targetObject, @Nullable Class<?> targetClass, String name,
Object... args) {
Assert.isTrue(targetObject != null || targetClass != null,
"Either 'targetObject' or 'targetClass' for the method must be specified");
Assert.hasText(name, "Method name must not be empty");
try {
MethodInvoker methodInvoker = new MethodInvoker();
methodInvoker.setTargetObject(target);
methodInvoker.setTargetObject(targetObject);
if (targetClass != null) {
methodInvoker.setTargetClass(targetClass);
}
methodInvoker.setTargetMethod(name);
methodInvoker.setArguments(args);
methodInvoker.prepare();
if (logger.isDebugEnabled()) {
logger.debug(String.format("Invoking method '%s' on %s with arguments %s", name, safeToString(target),
ObjectUtils.nullSafeToString(args)));
logger.debug(String.format("Invoking method '%s' on %s or %s with arguments %s", name,
safeToString(targetObject), safeToString(targetClass), ObjectUtils.nullSafeToString(args)));
}
return (T) methodInvoker.invoke();
......@@ -452,4 +509,8 @@ public abstract class ReflectionTestUtils {
}
}
private static String safeToString(@Nullable Class<?> clazz) {
return String.format("target class [%s]", (clazz != null ? clazz.getName() : null));
}
}
......@@ -27,6 +27,7 @@ import org.springframework.test.util.subpackage.LegacyEntity;
import org.springframework.test.util.subpackage.Person;
import org.springframework.test.util.subpackage.PersonEntity;
import org.springframework.test.util.subpackage.StaticFields;
import org.springframework.test.util.subpackage.StaticMethods;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
......@@ -57,6 +58,7 @@ class ReflectionTestUtilsTests {
@BeforeEach
void resetStaticFields() {
StaticFields.reset();
StaticMethods.reset();
}
@Test
......@@ -327,7 +329,7 @@ class ReflectionTestUtilsTests {
}
@Test
@Disabled("[SPR-8644] findMethod() does not currently support var-args")
@Disabled("[SPR-8644] MethodInvoker.findMatchingMethod() does not currently support var-args")
void invokeMethodWithPrimitiveVarArgs() {
// IntelliJ IDEA 11 won't accept int assignment here
Integer sum = invokeMethod(component, "add", 1, 2, 3, 4);
......@@ -422,4 +424,66 @@ class ReflectionTestUtilsTests {
assertThat(entity.toString().contains(testCollaborator)).isTrue();
}
@Test
void invokeStaticMethodWithNullTargetClass() {
assertThatIllegalArgumentException()
.isThrownBy(() -> invokeMethod((Class<?>) null, null))
.withMessage("Target class must not be null");
}
@Test
void invokeStaticMethodWithNullMethodName() {
assertThatIllegalArgumentException()
.isThrownBy(() -> invokeMethod(getClass(), null))
.withMessage("Method name must not be empty");
}
@Test
void invokeStaticMethodWithEmptyMethodName() {
assertThatIllegalArgumentException()
.isThrownBy(() -> invokeMethod(getClass(), " "))
.withMessage("Method name must not be empty");
}
@Test
void invokePublicStaticVoidMethodWithArguments() {
assertThat(StaticMethods.getPublicMethodValue()).isEqualTo("public");
String testCollaborator = "test collaborator";
invokeMethod(StaticMethods.class, "publicMethod", testCollaborator);
assertThat(StaticMethods.getPublicMethodValue()).isEqualTo(testCollaborator);
}
@Test
void invokePublicStaticMethodWithoutArguments() {
assertThat(StaticMethods.getPublicMethodValue()).isEqualTo("public");
String result = invokeMethod(StaticMethods.class, "publicMethod");
assertThat(result).isEqualTo(StaticMethods.getPublicMethodValue());
}
@Test
void invokePrivateStaticVoidMethodWithArguments() {
assertThat(StaticMethods.getPrivateMethodValue()).isEqualTo("private");
String testCollaborator = "test collaborator";
invokeMethod(StaticMethods.class, "privateMethod", testCollaborator);
assertThat(StaticMethods.getPrivateMethodValue()).isEqualTo(testCollaborator);
}
@Test
void invokePrivateStaticMethodWithoutArguments() {
assertThat(StaticMethods.getPrivateMethodValue()).isEqualTo("private");
String result = invokeMethod(StaticMethods.class, "privateMethod");
assertThat(result).isEqualTo(StaticMethods.getPrivateMethodValue());
}
@Test
void invokeStaticMethodWithNullTargetObjectAndNullTargetClass() {
assertThatIllegalArgumentException()
.isThrownBy(() -> invokeMethod((Object) null, (Class<?>) null, "id"))
.withMessage("Either 'targetObject' or 'targetClass' for the method must be specified");
}
}
/*
* Copyright 2002-2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.util.subpackage;
/**
* Simple class with static methods; intended for use in unit tests.
*
* @author Sam Brannen
* @since 5.2
*/
public class StaticMethods {
public static String publicMethodValue = "public";
private static String privateMethodValue = "private";
public static void publicMethod(String value) {
publicMethodValue = value;
}
public static String publicMethod() {
return publicMethodValue;
}
@SuppressWarnings("unused")
private static void privateMethod(String value) {
privateMethodValue = value;
}
@SuppressWarnings("unused")
private static String privateMethod() {
return privateMethodValue;
}
public static void reset() {
publicMethodValue = "public";
privateMethodValue = "private";
}
public static String getPublicMethodValue() {
return publicMethodValue;
}
public static String getPrivateMethodValue() {
return privateMethodValue;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册