/* * Copyright 2002-2008 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 * * http://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; import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** * <p> * ReflectionTestUtils is a collection of reflection-based utility methods for * use in unit and integration testing scenarios. * </p> * <p> * There are often situations in which it would be beneficial to be able to set * a non-<code>public</code> field or invoke a non-<code>public</code> * setter method when testing code involving, for example: * </p> * <ul> * <li>ORM frameworks such as JPA and Hibernate which condone the usage of * <code>private</code> or <code>protected</code> field access as opposed to * <code>public</code> setter methods for properties in a domain entity.</li> * <li>Spring's support for annotations such as * {@link org.springframework.beans.factory.annotation.Autowired @Autowired} and * {@link javax.annotation.Resource @Resource} which provides dependency * injection for <code>private</code> or <code>protected</code> fields, * setter methods, and configuration methods.</li> * </ul> * * @author Sam Brannen * @author Juergen Hoeller * @since 2.5 * @see ReflectionUtils */ public class ReflectionTestUtils { private static final String SETTER_PREFIX = "set"; private static final String GETTER_PREFIX = "get"; private static final Log logger = LogFactory.getLog(ReflectionTestUtils.class); /** * Set the {@link Field field} with the given <code>name</code> on the * provided {@link Object target object} to the supplied <code>value</code>. * <p>This method traverses the class hierarchy in search of the desired field. * In addition, an attempt will be made to make non-<code>public</code> * fields <em>accessible</em>, thus allowing one to set * <code>protected</code>, <code>private</code>, and * <em>package-private</em> fields. * @param target the target object on which to set the field * @param name the name of the field to set * @param value the value to set * @see ReflectionUtils#findField(Class, String, Class) * @see ReflectionUtils#makeAccessible(Field) * @see ReflectionUtils#setField(Field, Object, Object) */ public static void setField(Object target, String name, Object value) { setField(target, name, value, null); } /** * Set the {@link Field field} with the given <code>name</code> on the * provided {@link Object target object} to the supplied <code>value</code>. * <p>This method traverses the class hierarchy in search of the desired field. * In addition, an attempt will be made to make non-<code>public</code> * fields <em>accessible</em>, thus allowing one to set * <code>protected</code>, <code>private</code>, and * <em>package-private</em> fields. * @param target the target object on which to set the field * @param name the name of the field to set * @param value the value to set * @param type the type of the field (may be <code>null</code>) * @see ReflectionUtils#findField(Class, String, Class) * @see ReflectionUtils#makeAccessible(Field) * @see ReflectionUtils#setField(Field, Object, Object) */ public static void setField(Object target, String name, Object value, Class type) { Assert.notNull(target, "Target object must not be null"); Field field = ReflectionUtils.findField(target.getClass(), name, type); if (field == null) { throw new IllegalArgumentException("Could not find field [" + name + "] on target [" + target + "]"); } if (logger.isDebugEnabled()) { logger.debug("Setting field [" + name + "] on target [" + target + "]"); } ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, target, value); } /** * Get the field with the given <code>name</code> from the provided * target object. * <p>This method traverses the class hierarchy in search of the desired field. * In addition, an attempt will be made to make non-<code>public</code> fields * <em>accessible</em>, thus allowing one to get <code>protected</code>, * <code>private</code>, and <em>package-private</em> fields. * @param target the target object on which to set the field * @param name the name of the field to get * @return the field's current value * @see ReflectionUtils#findField(Class, String, Class) * @see ReflectionUtils#makeAccessible(Field) * @see ReflectionUtils#setField(Field, Object, Object) */ public static Object getField(Object target, String name) { Assert.notNull(target, "Target object must not be null"); Field field = ReflectionUtils.findField(target.getClass(), name); if (field == null) { throw new IllegalArgumentException("Could not find field [" + name + "] on target [" + target + "]"); } if (logger.isDebugEnabled()) { logger.debug("Getting field [" + name + "] from target [" + target + "]"); } ReflectionUtils.makeAccessible(field); return ReflectionUtils.getField(field, target); } /** * Invoke the setter method with the given <code>name</code> on the supplied * target object with the supplied <code>value</code>. * <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</code> * methods <em>accessible</em>, thus allowing one to invoke <code>protected</code>, * <code>private</code>, and <em>package-private</em> setter methods. * <p>In addition, this method supports JavaBean-style <em>property</em> * names. For example, if you wish to set the <code>name</code> property * on the target object, you may pass either "name" or * "setName" as the method name. * @param target the target object on which to invoke the specified setter method * @param name the name of the setter method to invoke or the corresponding property name * @param value the value to provide to the setter method * @see ReflectionUtils#findMethod(Class, String, Class[]) * @see ReflectionUtils#makeAccessible(Method) * @see ReflectionUtils#invokeMethod(Method, Object, Object[]) */ public static void invokeSetterMethod(Object target, String name, Object value) { invokeSetterMethod(target, name, value, null); } /** * Invoke the setter method with the given <code>name</code> on the supplied * target object with the supplied <code>value</code>. * <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</code> * methods <em>accessible</em>, thus allowing one to invoke <code>protected</code>, * <code>private</code>, and <em>package-private</em> setter methods. * <p>In addition, this method supports JavaBean-style <em>property</em> * names. For example, if you wish to set the <code>name</code> property * on the target object, you may pass either "name" or * "setName" as the method name. * @param target the target object on which to invoke the specified setter method * @param name the name of the setter method to invoke or the corresponding property name * @param value the value to provide to the setter method * @param type the formal parameter type declared by the setter method * @see ReflectionUtils#findMethod(Class, String, Class[]) * @see ReflectionUtils#makeAccessible(Method) * @see ReflectionUtils#invokeMethod(Method, Object, Object[]) */ public static void invokeSetterMethod(Object target, String name, Object value, Class type) { Assert.notNull(target, "Target object must not be null"); Assert.notNull(name, "Method name must not be empty"); Class[] paramTypes = (type != null ? new Class[] {type} : null); String setterMethodName = name; if (!name.startsWith(SETTER_PREFIX)) { setterMethodName = SETTER_PREFIX + StringUtils.capitalize(name); } Method method = ReflectionUtils.findMethod(target.getClass(), setterMethodName, paramTypes); if (method == null && !setterMethodName.equals(name)) { setterMethodName = name; method = ReflectionUtils.findMethod(target.getClass(), setterMethodName, paramTypes); } if (method == null) { throw new IllegalArgumentException("Could not find setter method [" + setterMethodName + "] on target [" + target + "] with parameter type [" + type + "]"); } if (logger.isDebugEnabled()) { logger.debug("Invoking setter method [" + setterMethodName + "] on target [" + target + "]"); } ReflectionUtils.makeAccessible(method); ReflectionUtils.invokeMethod(method, target, new Object[] {value}); } /** * Invoke the getter method with the given <code>name</code> on the supplied * target object with the supplied <code>value</code>. * <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</code> * methods <em>accessible</em>, thus allowing one to invoke <code>protected</code>, * <code>private</code>, and <em>package-private</em> getter methods. * <p>In addition, this method supports JavaBean-style <em>property</em> * names. For example, if you wish to get the <code>name</code> property * on the target object, you may pass either "name" or * "getName" as the method name. * @param target the target object on which to invoke the specified getter method * @param name the name of the getter method to invoke or the corresponding property name * @return the value returned from the invocation * @see ReflectionUtils#findMethod(Class, String, Class[]) * @see ReflectionUtils#makeAccessible(Method) * @see ReflectionUtils#invokeMethod(Method, Object, Object[]) */ public static Object invokeGetterMethod(Object target, String name) { Assert.notNull(target, "Target object must not be null"); Assert.notNull(name, "Method name must not be empty"); String getterMethodName = name; if (!name.startsWith(GETTER_PREFIX)) { getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); } Method method = ReflectionUtils.findMethod(target.getClass(), getterMethodName); if (method == null && !getterMethodName.equals(name)) { getterMethodName = name; method = ReflectionUtils.findMethod(target.getClass(), getterMethodName); } if (method == null) { throw new IllegalArgumentException("Could not find getter method [" + getterMethodName + "] on target [" + target + "]"); } if (logger.isDebugEnabled()) { logger.debug("Invoking getter method [" + getterMethodName + "] on target [" + target + "]"); } ReflectionUtils.makeAccessible(method); return ReflectionUtils.invokeMethod(method, target); } }