From 524cbe1534e3b996548436dd5df5ccb8bbbae11d Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Fri, 4 Apr 2025 09:44:55 +0200 Subject: [PATCH] fix validation logic to allow for complex validations and property validations to fail at the same time --- .../validation/core/RuleValidator.java | 75 ++++++++++++++----- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/src/main/java/it/aboutbits/springboot/testing/validation/core/RuleValidator.java b/src/main/java/it/aboutbits/springboot/testing/validation/core/RuleValidator.java index 333af1c..fbd0c6f 100644 --- a/src/main/java/it/aboutbits/springboot/testing/validation/core/RuleValidator.java +++ b/src/main/java/it/aboutbits/springboot/testing/validation/core/RuleValidator.java @@ -1,11 +1,11 @@ package it.aboutbits.springboot.testing.validation.core; +import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; import jakarta.validation.Valid; import jakarta.validation.Validation; import jakarta.validation.Validator; import jakarta.validation.ValidatorFactory; -import jakarta.validation.groups.Default; import lombok.NonNull; import lombok.SneakyThrows; import org.springframework.lang.Nullable; @@ -83,6 +83,12 @@ void assertValidation(AssertionParameter

assertionParameter) { var propertiesWithRules = getPropertyNamesThatHaveRules(rules); + assertThatSuppliedParameterIsValid( + parameterUnderTest, + functionToCallWithParameter, + validator + ); + assertThatValidationIsCompliantForEachProperty( rules, parameterUnderTest, @@ -108,6 +114,52 @@ private static HashSet getPropertyNamesThatHaveRules(List rules) { return propertiesWithRules; } + private static

void assertThatSuppliedParameterIsValid( + P parameterUnderTest, + Consumer

functionToCallWithParameter, + Validator validator + ) { + if (functionToCallWithParameter != null) { + try { + functionToCallWithParameter.accept(parameterUnderTest); + } catch (ConstraintViolationException e) { + var violatingFieldMessages = getViolatingFieldMessages(e.getConstraintViolations()); + + assertThat(true) + .withFailMessage( + "The supplied parameter violates the validation rules. The supplied parameter is not valid: %s", + violatingFieldMessages.collect(Collectors.joining(" | ")) + ) + .isFalse(); + } catch (Exception ignored) { + // ignore any other exceptions + } + } else { + // Use Bean Validation to validate + var violations = new HashSet>(validator.validate(parameterUnderTest)); + + var violatingFieldMessages = getViolatingFieldMessages(violations); + + assertThat(violations) + .withFailMessage( + "The supplied parameter possibly contains invalid values: %s", + violatingFieldMessages.collect(Collectors.joining(" | ")) + ) + .isEmpty(); + } + } + + private static Stream getViolatingFieldMessages(Set> violations) { + return violations + .stream() + .map(violation -> + "%s => %s".formatted( + violation.getPropertyPath().toString(), + violation.getMessage() + ) + ); + } + private static

void assertThatValidationIsCompliantForEachProperty( List rules, P parameterUnderTest, @@ -128,32 +180,15 @@ private static

void assertThatValidationIsCompliantForEachProperty( } else { // Use Bean Validation to validate the copy - var violations = validator.validate(copy, Default.class); - - // Check if there are any violations - var violatingFieldMessages = violations - .stream() - .map(violation -> - "%s => %s".formatted( - violation.getPropertyPath().toString(), - violation.getMessage() - ) - ); + var violations = validator.validate(copy); var violatingProperties = violations.stream().map( f -> f.getPropertyPath().toString() ).collect(Collectors.toSet()); - assertThat(violatingProperties) - .withFailMessage( - "More than one property failed to validate during mutation. The supplied parameter possibly contains invalid values: %s", - violatingFieldMessages.collect(Collectors.joining(" | ")) - ) - .hasSizeLessThan(2); - assertThat(violatingProperties) .withFailMessage("Validation failed for property: " + rule.getProperty() + " [" + alteredValue + "]") - .hasSize(1); + .contains(rule.getProperty()); } }); }