Reputation: 15958
I have springboot application which wires up validatorFactoryBean like this
private LocalValidatorFactoryBean validatorFactoryBean;
@Autowired
public void setValidatorFactoryBean(LocalValidatorFactoryBean validatorFactoryBean) {
this.validatorFactoryBean = validatorFactoryBean;
}
I have custom validators defined on class Car and I run the validations like this.
Set<ConstraintViolation<Car>> constraintViolations = validatorFactoryBean.validate(myCar);
All of this works fine. But when I try to test this thru unit tests, on the line
validatorFactoryBean.validate(myCar)
I get an error "No target Validator set".
This is how I am setting up validatorFactoryBean in the testng unit test
@InjectMocks
LocalValidatorFactoryBean validatorFactoryBean;
I pass this in to the class I am testing by calling setValidatorFactoryBean setter.
While debugging I realized this validatorFactoryBean is not aware of all the ConstraintValidator implementations in the main code.
How do I make validatorFactoryBean aware of all the ConstraintValidator implementations?
This is how the car constraint validator looks like
public class CarConstraintImpl implements ConstraintValidator<CarConstraint, req> {
@Inject
FuelDependency fuelDependency;
public CarConstraintImpl() {
}
@Override
public boolean isValid(Request req, ConstraintValidatorContext constraintContext) {
try {
fuelDependency.check(req);
} catch (Exception e) {
//some code
}
return true;
}
}
Upvotes: 0
Views: 2281
Reputation: 44745
(Option 1) If the LocalValidatorFactoryBean
is created by a Spring container, then some additional logic is executed during the afterPropertiesSet()
phase.
You could replicate this by calling this method by yourself. For example:
@BeforeEach
void setUp() {
validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
}
(Option 2) However, this is kind of a hack, because the default LocalValidatorFactoryBean
created by Spring Boot contains extra customizers and a message interpolator. If you don't care about this, it might be cleaner to work with the underlying JSR-380 bean validator in stead. For example:
@BeforeEach
void setUp() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
Be aware that validator
will be a different type, but it also contains a validate()
method with the same signature.
(Option 3) If you do care about the additional setup Spring provides, then you could write a Spring integration test. I don't recommend using @SpringBootTest
as you would bloat your tests.
All you really need is to include the SpringExtension
and the necessary autoconfiguration:
@ExtendWith(SpringExtension.class)
@ImportAutoConfiguration(ValidationAutoConfiguration.class)
public class MyValidationTest {
@Autowired
private LocalValidatorFactoryBean validator;
}
Upvotes: 2
Reputation: 5001
I would also suggest that you use a @SpringBootTest
, as described in Ken's answer. But if for some reason you don't want to, a LocalValidatorFactoryBean
object initialized using the below code should work for tests as long as all of the custom constraint validators have a default, no-args constructor:
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
Upvotes: 1
Reputation: 90517
@InjectMocks
is just a mockito stuff and never understand spring. It just like you create a LocalValidatorFactoryBean
manually without any spring integration to it.
To configure LocalValidatorFactoryBean
properly , you can do it by using @SpringBootTest
with the @ImportAutoConfiguration
to auto-configure just the bean validation stuff.
@SpringBootTest
public class ValidationTest{
@Configuration
@ImportAutoConfiguration(ValidationAutoConfiguration.class)
@ComponentScan(basePackages = { "my.app.root.package" })
public static class Config {
}
@Autowired
LocalValidatorFactoryBean validatorFactoryBean;
@Test
void test(){
}
}
Upvotes: 2