no.unit.nva.hamcrest.DoesNotHaveEmptyValues Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nvatestutils Show documentation
Show all versions of nvatestutils Show documentation
A commons library for the NVA project
The newest version!
package no.unit.nva.hamcrest;
import static java.util.Objects.isNull;
import static no.unit.nva.hamcrest.PropertyValuePair.FIELD_PATH_DELIMITER;
import com.fasterxml.jackson.databind.JsonNode;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
public class DoesNotHaveEmptyValues extends BaseMatcher {
public static final String EMPTY_FIELD_ERROR = "Empty field found: ";
public static final String FIELD_DELIMITER = ",";
public static final String TEST_DESCRIPTION = "All fields of all included objects need to be non empty";
private final List emptyFields;
private Set> stopRecursionClasses;
private Set ignoreFields;
public DoesNotHaveEmptyValues() {
super();
stopRecursionClasses = classesWithNoPojoStructure();
ignoreFields = Collections.emptySet();
this.emptyFields = new ArrayList<>();
}
public static DoesNotHaveEmptyValues doesNotHaveEmptyValues() {
return new DoesNotHaveEmptyValues<>();
}
/**
* Stop the nested check for the classes in the ignore list. The fields of the specified types will be checked
* whether they are null or not, but their fields will not be checked.
*
* @param ignoreList List of classes where the nested field check will stop.
* @param the type of the object in test.
* @return a matcher.
*/
public static DoesNotHaveEmptyValues doesNotHaveEmptyValuesIgnoringClasses(Set> ignoreList) {
DoesNotHaveEmptyValues matcher = new DoesNotHaveEmptyValues<>();
initializeClassesWhereRecursiveFieldCheckingWillStop(ignoreList, matcher);
return matcher;
}
public static DoesNotHaveEmptyValues doesNotHaveEmptyValuesIgnoringFields(Set ignoreList) {
DoesNotHaveEmptyValues matcher = new DoesNotHaveEmptyValues<>();
initializeFieldNamesWhichWillBeIgnoredDuringChecking(ignoreList, matcher);
return matcher;
}
public static DoesNotHaveEmptyValues doesNotHaveEmptyValuesIgnoringFieldsAndClasses(
Set> ignoreClassesList,
Set ignoreFieldsList) {
DoesNotHaveEmptyValues matcher = new DoesNotHaveEmptyValues<>();
initializeClassesWhereRecursiveFieldCheckingWillStop(ignoreClassesList, matcher);
initializeFieldNamesWhichWillBeIgnoredDuringChecking(ignoreFieldsList, matcher);
return matcher;
}
@Override
public boolean matches(Object actual) {
return objectDoesNotHaveFieldsWithEmptyValues(PropertyValuePair.rootObject(actual));
}
public boolean objectDoesNotHaveFieldsWithEmptyValues(PropertyValuePair fieldValue) {
List fieldsToBeChecked = createListWithFieldsToBeChecked(fieldValue);
emptyFields.addAll(collectEmptyFields(fieldsToBeChecked));
return emptyFields.isEmpty();
}
@Override
public void describeTo(Description description) {
description.appendText(TEST_DESCRIPTION);
}
@Override
public void describeMismatch(Object item, Description description) {
String emptyFieldNames = emptyFields.stream()
.map(PropertyValuePair::getFieldPath)
.collect(Collectors.joining(
FIELD_DELIMITER));
description.appendText(EMPTY_FIELD_ERROR)
.appendText(emptyFieldNames);
}
private static void initializeFieldNamesWhichWillBeIgnoredDuringChecking(Set ignoreList,
DoesNotHaveEmptyValues matcher) {
matcher.ignoreFields = addFieldPathDelimiterToRootField(ignoreList);
}
private static void initializeClassesWhereRecursiveFieldCheckingWillStop(Set> ignoreList,
DoesNotHaveEmptyValues matcher) {
Set> newStopRecursionClasses = new HashSet<>();
newStopRecursionClasses.addAll(matcher.stopRecursionClasses);
newStopRecursionClasses.addAll(ignoreList);
matcher.stopRecursionClasses = newStopRecursionClasses;
}
private static Set addFieldPathDelimiterToRootField(Set ignoreList) {
return ignoreList.stream()
.map(DoesNotHaveEmptyValues::addPathDelimiterToTopLevelFields)
.collect(Collectors.toSet());
}
private static String addPathDelimiterToTopLevelFields(String f) {
if (f.startsWith(FIELD_PATH_DELIMITER)) {
return f;
} else {
return FIELD_PATH_DELIMITER + f;
}
}
private List createListWithFieldsToBeChecked(PropertyValuePair rootObject) {
List fieldsToBeChecked = new ArrayList<>();
Stack fieldsToBeVisited = initializeFieldsToBeVisited(rootObject);
while (!fieldsToBeVisited.isEmpty()) {
PropertyValuePair currentField = fieldsToBeVisited.pop();
if (currentField.shouldBeChecked(stopRecursionClasses, ignoreFields)) {
addNestedFieldsToFieldsToBeVisited(fieldsToBeVisited, currentField);
fieldsToBeChecked.add(currentField);
}
}
return fieldsToBeChecked;
}
private Stack initializeFieldsToBeVisited(PropertyValuePair rootObject) {
Stack fieldsToBeVisited = new Stack<>();
fieldsToBeVisited.add(rootObject);
return fieldsToBeVisited;
}
private void addNestedFieldsToFieldsToBeVisited(Stack fieldsToBeVisited,
PropertyValuePair currentField) {
if (currentField.isComplexObject()) {
fieldsToBeVisited.addAll(currentField.children());
} else if (currentField.isCollection()) {
addEachArrayElementAsFieldToBeVisited(fieldsToBeVisited, currentField);
}
}
private void addEachArrayElementAsFieldToBeVisited(Stack fieldsToBeVisited,
PropertyValuePair currentField) {
List collectionElements = currentField.createPropertyValuePairsForEachCollectionItem();
fieldsToBeVisited.addAll(collectionElements);
}
/*Classes that their fields do not have getters*/
private Set> classesWithNoPojoStructure() {
return Set.of(
URI.class,
URL.class
);
}
private List collectEmptyFields(List propertyValuePairs) {
return propertyValuePairs.stream()
.filter(propertyValue -> isEmpty(propertyValue.getValue()))
.collect(Collectors.toList());
}
private boolean isEmpty(Object value) {
if (isNull(value)) {
return true;
}
return
isBlankString(value)
|| isEmptyCollection(value)
|| isEmptyMap(value)
|| isEmptyJsonNode(value);
}
private boolean isEmptyMap(Object value) {
if (value instanceof Map) {
return ((Map, ?>) value).isEmpty();
}
return false;
}
private boolean isEmptyJsonNode(Object value) {
if (value instanceof JsonNode) {
return ((JsonNode) value).isEmpty();
}
return false;
}
private boolean isEmptyCollection(Object value) {
if (value instanceof Collection) {
return ((Collection>) value).isEmpty();
}
return false;
}
private boolean isBlankString(Object value) {
if (value instanceof String) {
return ((String) value).isBlank();
}
return false;
}
}