I have tried this once and then used the reflection
to test whether some annotations are present or not on the fields.
There are possibilities to use some other Utils to get the classes and fields but in this example we are using pure java functionalities.
This test will also test all the fields of even all the nested classes of the Model
class.
class ModelTest {
private final Class<?> modelUnderTest = Model.class;
private final List<Class<?>> allClasses = new ArrayList<>();
private static boolean isStatic(int modifiers) {
return Modifier.isStatic(modifiers);
}
private static boolean isNotStaticClass(Class<?> clazz) {
return !isStatic(clazz.getModifiers());
}
@BeforeEach
void setUp() {
allClasses.add(modelUnderTest);
Arrays.stream(modelUnderTest.getDeclaredClasses())
// Skipping with Builder as in reality model was using the @lombok.Builder annotation
.filter(c -> !c.getName().contains("Builder"))
.forEach(allClasses::add);
}
@Test
void shouldBeAnnotatedWithApiModel() {
Assert.assertTrue(modelUnderTest.isAnnotationPresent(ApiModel.class));
}
@Test
void shouldHaveAllFieldsMarkedWithApiModelProperty() {
forAllFields(
field -> {
if (!field.isAnnotationPresent(ApiModelProperty.class)) {
Assert.fail("Annotation ApiModelPropertyAnnotation missing on " + field);
} else {
var annotation = field.getAnnotation(ApiModelProperty.class);
assertThat(annotation.required()).isTrue();
}
});
}
@Test
void stringsFieldsShouldBeMarkedWithNotBlank() {
forAllFields(
field -> {
if (field.getType().equals(String.class)
&& !field.isAnnotationPresent(NotBlank.class)) {
Assert.fail("Annotation NotBlank missing on " + field);
}
});
}
@Test
void numbersFieldsShouldBeMarkedWithPositive() {
forAllFields(
field -> {
if (field.getType().isAssignableFrom(Number.class)
&& !field.isAnnotationPresent(Positive.class)) {
Assert.fail("Annotation Positive missing on " + field);
}
});
}
private void forAllFields(Consumer<Field> consumer) {
for (var toTest : allClasses) {
for (Field field : toTest.getDeclaredFields()) {
if (isStatic(field.getModifiers())) {
continue;
}
consumer.accept(field);
}
}
}
}