Self contained Java sample
- TestNG 6.8
- Javassist 3.20
package com.stackoverflow; | |
import javassist.util.proxy.MethodHandler; | |
import javassist.util.proxy.ProxyFactory; | |
import javassist.util.proxy.ProxyObject; | |
import org.testng.annotations.BeforeMethod; | |
import org.testng.annotations.Test; | |
import java.beans.*; | |
import java.lang.reflect.Method; | |
import java.util.HashSet; | |
import java.util.Set; | |
import static org.testng.Assert.fail; | |
public class EnsureAllFieldsAreRead { | |
/** | |
* This is a standard JavaBean domain class | |
*/ | |
public static class Person { | |
public String name; | |
public int age; | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
public int getAge() { | |
return age; | |
} | |
public void setAge(int age) { | |
this.age = age; | |
} | |
} | |
/** | |
* API class | |
* | |
* Objects proxied by this factory keep counters of method invocations | |
*/ | |
public static class CountingProxyFactory { | |
@SuppressWarnings("unchecked") | |
public <T> T proxy(Class<T> classToProxy) { | |
try { | |
ProxyFactory factory = new ProxyFactory(); | |
factory.setSuperclass(classToProxy); | |
Class clazz = factory.createClass(); | |
T instance = (T) clazz.newInstance(); | |
ProxyObject proxy = (ProxyObject) instance; | |
MethodCallCounter handler = new MethodCallCounter(); | |
proxy.setHandler(handler); | |
return instance; | |
} catch (Exception e) { | |
throw new RuntimeException("Could not proxy class", e); | |
} | |
} | |
public void verifyAllGettersCalled(Object bean) { | |
if (!(bean instanceof ProxyObject)) { | |
fail("This bean is not a proxy!"); | |
} | |
ProxyObject proxy = (ProxyObject) bean; | |
if (!(proxy.getHandler() instanceof MethodCallCounter)) { | |
fail("Could not find handler for " + bean + ". This object were not made by this factory!"); | |
} | |
MethodCallCounter handler = (MethodCallCounter) proxy.getHandler(); | |
handler.countAccessOfGetters(bean.getClass()); | |
} | |
} | |
/** | |
* This is simply a counter | |
*/ | |
private static class MethodCallCounter implements MethodHandler { | |
private final Set<String> counter = new HashSet<>(); | |
@Override | |
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { | |
counter.add(thisMethod.getName()); | |
Class<?> returnType = thisMethod.getReturnType(); | |
if (returnType.isPrimitive()) { | |
if (returnType.equals(int.class)) { | |
return 0; | |
} else if (returnType.equals(double.class)) { | |
return 0.0; | |
} else if (returnType.equals(boolean.class)) { | |
return false; | |
} else if (returnType.equals(char.class)) { | |
return 0; | |
} else { | |
throw new RuntimeException("I forgot to program return value for " + returnType.getName()); | |
} | |
} else { | |
return null; | |
} | |
} | |
public void countAccessOfGetters(Class<?> beanProxyClass) { | |
Class<?> beanClass = beanProxyClass.getSuperclass(); | |
BeanInfo info; | |
try { | |
info = Introspector.getBeanInfo(beanClass, Object.class); | |
} catch (IntrospectionException e) { | |
throw new RuntimeException(e); | |
} | |
PropertyDescriptor[] pds = info.getPropertyDescriptors(); | |
for (PropertyDescriptor prop : pds) { | |
String methodName = prop.getReadMethod().getName(); | |
if (!counter.contains(methodName)) { | |
fail("Nobody called " + beanClass.getName() + "." + methodName + "()"); | |
} | |
} | |
} | |
} | |
public static class TestCase { | |
private CountingProxyFactory countingProxyFactory; | |
private Person person; | |
@BeforeMethod | |
public void setUp() { | |
countingProxyFactory = new CountingProxyFactory(); | |
person = countingProxyFactory.proxy(Person.class); | |
} | |
@Test | |
public void succeedBecauseAllMethodsAreCalled() { | |
person.getName(); | |
person.getAge(); | |
verifyGettersCalls(); | |
} | |
@Test | |
public void failBecauseSomeMethodsAreNotCalled() { | |
person.getName(); | |
verifyGettersCalls(); | |
} | |
private void verifyGettersCalls() { | |
countingProxyFactory.verifyAllGettersCalled(person); | |
} | |
} | |
} |