In object-oriented programming, there are plenty of accessors and mutators to test. This post demonstrates that this effort can be automated with reflection 🚀. The inspiration came from discussions I had with my students during our software-engineering class: how to increase code coverage without lots of manual effort? 🤔
Roughly speaking, the reflection mechanism allows the code to analyse itself. At runtime, we are able to construct calls based on extracted class properties. The idea is not novel, see for instance this gist. To add the value and improve the presentation, I modernized and completed the code to a fully featured project on GitHub with CI/CD on GitHub Actions and Code Coverage connected 😎.
Here is how the testing class looks like. Java reflection accesses classes, extracts fields and their types and constructs calls with type-matching values accordingly:
// tester class
class AutoTests {
private static final Class[] classToTest = new Class[]{
// the list of classes to test
PersonClass.class, AnimalClass.class
};
@Test
public void correctGettersSetters() {
for (Class aClass : classToTest) {
Object instance;
try {
instance = aClass.getDeclaredConstructor().newInstance();
Field[] declaredFields = aClass.getDeclaredFields();
for(Field f: declaredFields) {
// get the field getter and setter, following the Java naming convention (!)
// www.theserverside.com/feature/Java-naming-conventions-explained
String name = f.getName();
name = name.substring(0,1).toUpperCase() + name.substring(1);
String getterName = "get" + name;
String setterName = "set" + name;
Method getterMethod = aClass.getMethod(getterName);
Method setterMethod = aClass.getMethod(setterName, getterMethod.getReturnType());
// prepare a test value based on the filed type
Object testVal = null;
Class<?> fType = f.getType();
if (fType.isAssignableFrom(Integer.class)) {
testVal = 1234;
} else if (fType.isAssignableFrom(String.class)) {
testVal = "abcd";
}
// test by composing the setter and getter
setterMethod.invoke(instance, testVal);
Object result = getterMethod.invoke(instance);
System.out.printf("Testing class=%s field=%s...\n", aClass.getName(), f.getName());
assertThat(result).as("in class %s fields %s", aClass.getName(), f.getName()).isEqualTo(testVal);
}
} catch(Exception e) {
System.out.println(e.toString());
}
}
}
}
And here is one more demo available online.
Finally, a disclaimer: accessors and mutators may deserve smarter tests than what stated here – depending on a use-case.