Browse Source

Merge pull request #1006 from mdeinum/SPR-14050

* SPR-14050:
  Fully test proxy support in ReflectionTestUtils
  Polishing
  Document proxy support in ReflectionTestUtils
  Fix bugs in ReflectionTestUtils related to proxies
  Polishing
  Support proxied objects in ReflectionTestUtils
pull/1008/head
Sam Brannen 10 years ago
parent
commit
9439a008d7
  1. 3
      spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java
  2. 30
      spring-test/src/main/java/org/springframework/test/util/ReflectionTestUtils.java
  3. 59
      spring-test/src/test/java/org/springframework/test/util/ReflectionTestUtilsTests.java
  4. 7
      spring-test/src/test/java/org/springframework/test/util/subpackage/PersistentEntity.java
  5. 84
      spring-test/src/test/java/org/springframework/test/util/subpackage/Person.java
  6. 96
      spring-test/src/test/java/org/springframework/test/util/subpackage/PersonEntity.java

3
spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@ -32,6 +32,7 @@ import org.springframework.util.Assert; @@ -32,6 +32,7 @@ import org.springframework.util.Assert;
* @since 4.2
* @see org.springframework.aop.support.AopUtils
* @see org.springframework.aop.framework.AopProxyUtils
* @see ReflectionTestUtils
*/
public class AopTestUtils {

30
spring-test/src/main/java/org/springframework/test/util/ReflectionTestUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@ -60,6 +60,7 @@ import org.springframework.util.StringUtils; @@ -60,6 +60,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @since 2.5
* @see ReflectionUtils
* @see AopTestUtils
*/
public class ReflectionTestUtils {
@ -137,6 +138,9 @@ public class ReflectionTestUtils { @@ -137,6 +138,9 @@ public class ReflectionTestUtils {
* Set the {@linkplain Field field} with the given {@code name}/{@code type}
* on the provided {@code targetObject}/{@code targetClass} to the supplied
* {@code value}.
* <p>If the supplied {@code targetObject} is a <em>proxy</em>, it will
* be {@linkplain AopTestUtils#getUltimateTargetObject unwrapped} allowing
* the field to be set on the ultimate target of the proxy.
* <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}
* fields <em>accessible</em>, thus allowing one to set {@code protected},
@ -153,30 +157,33 @@ public class ReflectionTestUtils { @@ -153,30 +157,33 @@ public class ReflectionTestUtils {
* @see ReflectionUtils#findField(Class, String, Class)
* @see ReflectionUtils#makeAccessible(Field)
* @see ReflectionUtils#setField(Field, Object, Object)
* @see AopTestUtils#getUltimateTargetObject(Object)
* @since 4.2
*/
public static void setField(Object targetObject, Class<?> targetClass, String name, Object value, Class<?> type) {
Assert.isTrue(targetObject != null || targetClass != null,
"Either targetObject or targetClass for the field must be specified");
Object ultimateTarget = (targetObject != null ? AopTestUtils.getUltimateTargetObject(targetObject) : null);
if (targetClass == null) {
targetClass = targetObject.getClass();
targetClass = ultimateTarget.getClass();
}
Field field = ReflectionUtils.findField(targetClass, name, type);
if (field == null) {
throw new IllegalArgumentException(String.format(
"Could not find field '%s' of type [%s] on target object [%s] or target class [%s]", name, type,
targetObject, targetClass));
ultimateTarget, targetClass));
}
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Setting field '%s' of type [%s] on target object [%s] or target class [%s] to value [%s]", name, type,
targetObject, targetClass, value));
ultimateTarget, targetClass, value));
}
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, targetObject, value);
ReflectionUtils.setField(field, ultimateTarget, value);
}
/**
@ -213,6 +220,9 @@ public class ReflectionTestUtils { @@ -213,6 +220,9 @@ public class ReflectionTestUtils {
/**
* Get the value of the {@linkplain Field field} with the given {@code name}
* from the provided {@code targetObject}/{@code targetClass}.
* <p>If the supplied {@code targetObject} is a <em>proxy</em>, it will
* be {@linkplain AopTestUtils#getUltimateTargetObject unwrapped} allowing
* the field to be retrieved from the ultimate target of the proxy.
* <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}
* fields <em>accessible</em>, thus allowing one to get {@code protected},
@ -234,23 +244,25 @@ public class ReflectionTestUtils { @@ -234,23 +244,25 @@ public class ReflectionTestUtils {
Assert.isTrue(targetObject != null || targetClass != null,
"Either targetObject or targetClass for the field must be specified");
Object ultimateTarget = (targetObject != null ? AopTestUtils.getUltimateTargetObject(targetObject) : null);
if (targetClass == null) {
targetClass = targetObject.getClass();
targetClass = ultimateTarget.getClass();
}
Field field = ReflectionUtils.findField(targetClass, name);
if (field == null) {
throw new IllegalArgumentException(
String.format("Could not find field '%s' on target object [%s] or target class [%s]", name,
targetObject, targetClass));
ultimateTarget, targetClass));
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("Getting field '%s' from target object [%s] or target class [%s]", name,
targetObject, targetClass));
ultimateTarget, targetClass));
}
ReflectionUtils.makeAccessible(field);
return ReflectionUtils.getField(field, targetObject);
return ReflectionUtils.getField(field, ultimateTarget);
}
/**

59
spring-test/src/test/java/org/springframework/test/util/ReflectionTestUtilsTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@ -22,9 +22,12 @@ import org.junit.Rule; @@ -22,9 +22,12 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.test.util.subpackage.Component;
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 static org.hamcrest.CoreMatchers.*;
@ -41,12 +44,12 @@ public class ReflectionTestUtilsTests { @@ -41,12 +44,12 @@ public class ReflectionTestUtilsTests {
private static final Float PI = new Float((float) 22 / 7);
private final Person person = new Person();
private final Person person = new PersonEntity();
private final Component component = new Component();
@Rule
public ExpectedException exception = ExpectedException.none();
public final ExpectedException exception = ExpectedException.none();
@Before
@ -54,7 +57,6 @@ public class ReflectionTestUtilsTests { @@ -54,7 +57,6 @@ public class ReflectionTestUtilsTests {
StaticFields.reset();
}
@Test
public void setFieldWithNullTargetObject() throws Exception {
exception.expect(IllegalArgumentException.class);
@ -106,6 +108,29 @@ public class ReflectionTestUtilsTests { @@ -106,6 +108,29 @@ public class ReflectionTestUtilsTests {
@Test
public void setFieldAndGetFieldForStandardUseCases() throws Exception {
assertSetFieldAndGetFieldBehavior(this.person);
}
@Test
public void setFieldAndGetFieldViaJdkDynamicProxy() throws Exception {
ProxyFactory pf = new ProxyFactory(this.person);
pf.addInterface(Person.class);
Person proxy = (Person) pf.getProxy();
assertTrue("Proxy is a JDK dynamic proxy", AopUtils.isJdkDynamicProxy(proxy));
assertSetFieldAndGetFieldBehaviorForProxy(proxy, this.person);
}
@Test
public void setFieldAndGetFieldViaCglibProxy() throws Exception {
ProxyFactory pf = new ProxyFactory(this.person);
pf.setProxyTargetClass(true);
Person proxy = (Person) pf.getProxy();
assertTrue("Proxy is a CGLIB proxy", AopUtils.isCglibProxy(proxy));
assertSetFieldAndGetFieldBehaviorForProxy(proxy, this.person);
}
private static void assertSetFieldAndGetFieldBehavior(Person person) {
// Set reflectively
setField(person, "id", new Long(99), long.class);
setField(person, "name", "Tom");
setField(person, "age", new Integer(42));
@ -113,19 +138,33 @@ public class ReflectionTestUtilsTests { @@ -113,19 +138,33 @@ public class ReflectionTestUtilsTests {
setField(person, "likesPets", Boolean.TRUE);
setField(person, "favoriteNumber", PI, Number.class);
// Get reflectively
assertEquals(new Long(99), getField(person, "id"));
assertEquals("Tom", getField(person, "name"));
assertEquals(new Integer(42), getField(person, "age"));
assertEquals("blue", getField(person, "eyeColor"));
assertEquals(Boolean.TRUE, getField(person, "likesPets"));
assertEquals(PI, getField(person, "favoriteNumber"));
// Get directly
assertEquals("ID (private field in a superclass)", 99, person.getId());
assertEquals("name (protected field)", "Tom", person.getName());
assertEquals("age (private field)", 42, person.getAge());
assertEquals("eye color (package private field)", "blue", person.getEyeColor());
assertEquals("'likes pets' flag (package private boolean field)", true, person.likesPets());
assertEquals("'favorite number' (package field)", PI, person.getFavoriteNumber());
}
assertEquals(new Long(99), getField(person, "id"));
assertEquals("Tom", getField(person, "name"));
assertEquals(new Integer(42), getField(person, "age"));
assertEquals("blue", getField(person, "eyeColor"));
assertEquals(Boolean.TRUE, getField(person, "likesPets"));
assertEquals(PI, getField(person, "favoriteNumber"));
private static void assertSetFieldAndGetFieldBehaviorForProxy(Person proxy, Person target) {
assertSetFieldAndGetFieldBehavior(proxy);
// Get directly from Target
assertEquals("ID (private field in a superclass)", 99, target.getId());
assertEquals("name (protected field)", "Tom", target.getName());
assertEquals("age (private field)", 42, target.getAge());
assertEquals("eye color (package private field)", "blue", target.getEyeColor());
assertEquals("'likes pets' flag (package private boolean field)", true, target.likesPets());
assertEquals("'favorite number' (package field)", PI, target.getFavoriteNumber());
}
@Test

7
spring-test/src/test/java/org/springframework/test/util/subpackage/PersistentEntity.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2007-2011 the original author or authors.
* Copyright 2007-2016 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.
@ -28,11 +28,12 @@ public abstract class PersistentEntity { @@ -28,11 +28,12 @@ public abstract class PersistentEntity {
private long id;
public final long getId() {
public long getId() {
return this.id;
}
protected final void setId(long id) {
protected void setId(long id) {
this.id = id;
}
}

84
spring-test/src/test/java/org/springframework/test/util/subpackage/Person.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2016 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.
@ -16,85 +16,27 @@ @@ -16,85 +16,27 @@
package org.springframework.test.util.subpackage;
import org.springframework.core.style.ToStringCreator;
/**
* Concrete subclass of {@link PersistentEntity} representing a <em>person</em>
* entity; intended for use in unit tests.
* Interface representing a <em>person</em> entity; intended for use in unit tests.
*
* <p>The introduction of an interface is necessary in order to test support for
* JDK dynamic proxies.
*
* @author Sam Brannen
* @since 2.5
* @since 4.3
*/
public class Person extends PersistentEntity {
protected String name;
private int age;
String eyeColor;
boolean likesPets = false;
private Number favoriteNumber;
public final String getName() {
return this.name;
}
@SuppressWarnings("unused")
private final void setName(final String name) {
this.name = name;
}
public final int getAge() {
return this.age;
}
protected final void setAge(final int age) {
this.age = age;
}
public final String getEyeColor() {
return this.eyeColor;
}
final void setEyeColor(final String eyeColor) {
this.eyeColor = eyeColor;
}
public final boolean likesPets() {
return this.likesPets;
}
protected final void setLikesPets(final boolean likesPets) {
this.likesPets = likesPets;
}
public final Number getFavoriteNumber() {
return this.favoriteNumber;
}
protected final void setFavoriteNumber(Number favoriteNumber) {
this.favoriteNumber = favoriteNumber;
}
@Override
public String toString() {
return new ToStringCreator(this)
public interface Person {
.append("id", this.getId())
long getId();
.append("name", this.name)
String getName();
.append("age", this.age)
int getAge();
.append("eyeColor", this.eyeColor)
String getEyeColor();
.append("likesPets", this.likesPets)
boolean likesPets();
.append("favoriteNumber", this.favoriteNumber)
Number getFavoriteNumber();
.toString();
}
}

96
spring-test/src/test/java/org/springframework/test/util/subpackage/PersonEntity.java

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
/*
* Copyright 2002-2016 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.subpackage;
import org.springframework.core.style.ToStringCreator;
/**
* Concrete subclass of {@link PersistentEntity} representing a <em>person</em>
* entity; intended for use in unit tests.
*
* @author Sam Brannen
* @since 2.5
*/
public class PersonEntity extends PersistentEntity implements Person {
protected String name;
private int age;
String eyeColor;
boolean likesPets = false;
private Number favoriteNumber;
public String getName() {
return this.name;
}
@SuppressWarnings("unused")
private void setName(final String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
protected void setAge(final int age) {
this.age = age;
}
public String getEyeColor() {
return this.eyeColor;
}
void setEyeColor(final String eyeColor) {
this.eyeColor = eyeColor;
}
public boolean likesPets() {
return this.likesPets;
}
protected void setLikesPets(final boolean likesPets) {
this.likesPets = likesPets;
}
public Number getFavoriteNumber() {
return this.favoriteNumber;
}
protected void setFavoriteNumber(Number favoriteNumber) {
this.favoriteNumber = favoriteNumber;
}
@Override
public String toString() {
// @formatter:off
return new ToStringCreator(this)
.append("id", this.getId())
.append("name", this.name)
.append("age", this.age)
.append("eyeColor", this.eyeColor)
.append("likesPets", this.likesPets)
.append("favoriteNumber", this.favoriteNumber)
.toString();
// @formatter:on
}
}
Loading…
Cancel
Save