I have a custom Hibernate Validator for my entities. One of my validators uses an Autowired Spring @Repository. The application works fine and my repository is Autowired successfully on my validator.
The problem is i can't find a way to test my validator, cause i can't inject my repository inside it.
Person.class:
@Entity
@Table(schema = "dbo", name = "Person")
@PersonNameMustBeUnique
public class Person {
@Id
@GeneratedValue
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@Column()
@NotBlank()
private String name;
//getters and setters
//...
}
PersonNameMustBeUnique.class
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { PersonNameMustBeUniqueValidator.class })
@Documented
public @interface PersonNameMustBeUnique{
String message() default "";
Class<?>[] groups() default {};
Class<? extends javax.validation.Payload>[] payload() default {};
}
The validator:
public class PersonNameMustBeUniqueValidatorimplements ConstraintValidator<PersonNameMustBeUnique, Person> {
@Autowired
private PersonRepository repository;
@Override
public void initialize(PersonNameMustBeUnique constraintAnnotation) { }
@Override
public boolean isValid(Person entidade, ConstraintValidatorContext context) {
if ( entidade == null ) {
return true;
}
context.disableDefaultConstraintViolation();
boolean isValid = nameMustBeUnique(entidade, context);
return isValid;
}
private boolean nameMustBeUnique(Person entidade, ConstraintValidatorContext context) {
//CALL REPOSITORY TO CHECK IF THE NAME IS UNIQUE
//ADD errors if not unique...
}
}
And the context file has a validator bean:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
Again, it works fine, but i don't know how to test it.
My test file is:
@RunWith(MockitoJUnitRunner.class)
public class PersonTest {
Person e;
static Validator validator;
@BeforeClass
public static void setUpClass() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@Test
public void name__must_not_be_null() {
e = new Person();
e.setName(null);
Set<ConstraintViolation<Person>> violations = validator.validate(e);
assertViolacao(violations, "name", "Name must not be null");
}
}
Recently I had the same problem with my custom validator. I needed to validate a model being passed to a controller's method (method level validation). The validator invoked but the dependencies (@Autowired) could not be injected. It took me some days searching and debugging the whole process. Finally, I could make it work. I hope my experience save some time for others with the same problem. Here is my solution:
Having a jsr-303 custom validator like this:
You should configure spring test like this:
Note that, here I am using testng, but you can use JUnit 4. The whole configuration would be the same except that you would run the test with @RunWith(SpringJUnit4ClassRunner.class) and do not extend the AbstractTestNGSpringContextTests.
Now, @ValidSample can be used in places mentioned in @Target() of the custom annotation. Attention: If you are going to use the @ValidSample annotation on method level (like validating method arguments), then you should put class level annotation @Validated on the class where its method is using your annotation, for example on a controller or on a service class.
Spring Boot 2 allows to inject Bean in custom Validator without any fuss.The Spring framework automatically detects all classes which implement the
ConstraintValidator
interface, instantiate them, and wire all dependencies.I had Similar problem , this is how i have implemented.
Step 1 Interface
Step 2 Validator
Usage
On @BeforeClass:
And in your test you need to replace the beans with your mocked bean:
The class that do all the magic: