Calling the real method on a Java annotation using Mockito - java

I have a custom annotation called Matches which has a default value for message. This is essentially a class-level custom constraint validator but that's not part of the problem.
#Constraint(validatedBy = MatchesValidator.class)
#Documented
#Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Repeatable(Matches.List.class)
public #interface Matches {
String field();
String otherField();
String message() default "{com.example.api.validation.constraints.Matches.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Documented
#Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#interface List {
Matches[] value();
}
}
This is a simple constraint that will be applied on a class to validate if two fields have the same value:
#Matches(field = "password", otherField = "confirmPassword")
class UserRegistration {
private String password;
private String confirmPassword;
// Getters
}
I'm writing a test using JUnit and Mockito and and I'm trying to mock the Matches annotation:
#Test
void isValid_whenFieldsDoNotMatch_thenReturnsFalse() {
Matches invalidPropertyMatches = Mockito.mock(Matches.class);
when(invalidPropertyMatches.field()).thenReturn("password");
when(invalidPropertyMatches.field()).thenReturn("confirmPassword");
when(invalidPropertyMatches.message()).thenCallRealMethod(); // This throws
}
I want that when the Matches.message() value is called, I get back the default defined value "{com.example.api.validation.constraints.Matches.message}", so I added:
when(invalidPropertyMatches.message()).thenCallRealMethod();
However, this throws the following exception:
org.mockito.exceptions.base.MockitoException:
Cannot call abstract real method on java object!
Calling real methods is only possible when mocking non abstract method.
//correct example:
when(mockOfConcreteClass.nonAbstractMethod()).thenCallRealMethod();
It essentially says that Matches.message() is abstract and cannot be called. Are annotation properties in Java abstract? Is there a way to fix this?

Related

Custom validation with javax.validation

I'm trying to make a custom java validation annotation and returns me
Request processing failed; nested exception is javax.validation.ConstraintDeclarationException: HV000144: Cross parameter constraint com.my.company.CustomConstraint is illegally placed on field 'private java.util.List com.my.company.ElementOfTheList'."
the code is really naive
#Documented
#Retention(RUNTIME)
#Target({ FIELD, METHOD})
#Constraint(validatedBy = ConstraintValidation.class)
public #interface CustomConstraint {
String message() default "this is the default message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
#SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ConstraintValidationimplements ConstraintValidator<CustomConstraint , List<ElementOfTheList>> {
public boolean isValid(List<ElementOfTheList> value, ConstraintValidatorContext context) {
System.out.println("only a sysout to test");
return true;
}
}
And in the rest object model
#JsonProperty("ElementOfTheList")
#Valid
#NotNull(message ="not null message")
#NotEmpty(message = "not empty message")
#CustomConstraint
private List<ElementOfTheList> list = null;
change
#SupportedValidationTarget(ValidationTarget.PARAMETERS)
to
#SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT)
since you want to validate and element (here is List list)
and not the parameters of a method or a constructor

Java validator annotation for value from properties file

I am trying to write a Validator that should validate the value of a property in application.properties
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = BaseUrlStartsWithHttpsValidator.class)
public #interface CheckBaseUrlStartsWithHttps {
String message() default "Base url does not start with https:// check your configuration, "
+ "Found: ${validatedValue}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String value() default "";
}
It's a simple validation I am just checking if the String starts with https://.
and the way I am trying to use it by annotating the field with it so:
#CheckBaseUrlStartsWithHttps
#Value("${my.base.url}")
private String baseUrl;
But it seems not to do the trick I have tried changing the #Target type is it even possible to validate properties this way, I am using Spring Framework.
So figured it out my self upon reading, reading, reading and trying different things. Turns out that in the class where I am reading in the property I had to annotate the class itself with #Validated and #ConfigurationProperties(prefix = "my") then my check was working as supposed to. So the end product is:
public class BaseUrlValidator implements ConstraintValidator<CheckBaseUrl, String> {
#Override
public boolean isValid(#Nullable String value, #Nullable ConstraintValidatorContext context) {
if (value == null) {
return false;
}
return value.startsWith("https://");
}
}
and
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = BaseUrlValidator.class)
public #interface CheckBaseUrl {
String message() default "Base URL should start with https://. Found: ${validatedValue}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String value() default "";
}
and
#Service
#Validated
#ConfigurationProperties(prefix = "my")
public class MyService {
#CheckBaseUrl
#Value("${my.base.url}")
private String baseUrl;
...
}
Only thing that might be a bit annoying though is that this will make the application fail on startup if the urls is not configured correctly, which is in its own probably a good thing such that it can be fixed right away, but I would rather want it to fail on runtime when it is accessed and throw a RumetimeException instead. Anyway this seems to do the trick.

JSR-303 custom annotation

I'm trying to implement a custom annotation to validate my fields. The idea is that the validation fails whenever the annotated field is null. Something like this.
#RequiredProperty
public abstract Person getPerson();
Now if this returns a null Person, I'd like the validation to fail (ideally with a custom message "Person field is null").
I tried to do it like this.
#Documented
#Constraint(validatedBy = RequiredPropertyValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE})
#ReportAsSingleViolation
public #interface RequiredProperty {
String message() default "{javax.validation.constraints.RequiredProperty.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And validator.
public class RequiredPropertyValidator implements ConstraintValidator<RequiredProperty, Object> {
#Override
public void initialize(RequiredProperty constraintAnnotation) {
}
#Override
public boolean isValid(Object property, ConstraintValidatorContext context) {
return property != null;
}
}
However this won't work. It doesn't validate at all. Object property is never even passed to the isValid method. Any advice on how to get it working?
UPDATE
Removing the empty initialize method got it working. However, I'm not sure how to create a custom error message that the "Person" field is null. Is that possible?
I created a custom message in .properties file, but this is just a static message, and I'd like to capture the actual field from runtime.

How to define an error message inside a custom #annotation

I'm building my own #annotation that valids many fields of a class (so it's a class level annotation and not a field level annotation).
When there is an error I add a ConstraintViolation and I print an error message taken from .properties file. The error is something like :
The field {1} must be less than the field {2}
What I need is the way to fill the variables {1} and {2} . And I have to do it inside the method isValid(), since is there that I dinamically define what are values to show inside the error message in place of {1} and {2}
This is my annotations:
#EsempioAnnotationClassLevel(dateFromNomeCampo={"dataDiNascitaFrom","dataLavoroFrom",...})
This is my interface:
#Constraint(validatedBy = EsempioAnnotationClassLevelValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface EsempioAnnotationClassLevel {
String[] dateFromNomeCampo();
String message() default "Errore FatherSearchInterface;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
this is my class that implements ConstraintValidator:
public class EsempioAnnotationClassLevelValidator implements ConstraintValidator<EsempioAnnotationClassLevel, Object>{
...
public boolean isValid(Object object, ConstraintValidatorContext cxt) {
...
cxt.buildConstraintViolationWithTemplate("errorMessage").addNode("field").addConstraintViolation();
...
}
...
}
Could you please check if the following question can help as reference?
Can you change an annotation message at run time?

Java Bean validation: Combine two contstraints with OR on one field

I want to validate a field 'foo' against either of two constraints, i.e. something like this
#ConstraintA OR #ConstraintB
private String foo;
Is this possible?
This is possible with Hibernate Validator, but only using a Hibernate Validator specific extension. Using it is not standard conform to Bean Validation.
You will have to use boolean composition of constraints as described here - http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-boolean-constraint-composition
You will need a "wrapper" constraint. Something like this:
#ConstraintComposition(OR)
#PConstraintA
#ConstraintB
#ReportAsSingleViolation
#Target({ METHOD, FIELD })
#Retention(RUNTIME)
#Constraint(validatedBy = { })
public #interface ConstraintAOrB {
String message() default "{com.acme.ConstraintAOrB.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}

Categories