javax.validator with spring component - java

I use javax.validation with Spring. In my test (groovy) I explicitly create validator.
import javax.validation.Validation
import javax.validation.Validator
import javax.validation.ValidatorFactory
ValidatorFactory factory = Validation.buildDefaultValidatorFactory()
Validator validator = factory.getValidator()
when:
Set<ConstraintViolation<User>> constraints = validator.validate(entity)
My validator in java
public class EntityDynamicValidator implements ConstraintValidator<SomeConstraint, Entity> {
private GroupService groupService;
// This constructor is required, see the link bellow.
public UserDynamicEnumValidator() {
}
public UserDynamicEnumValidator(final GroupService groupService) {
this.groupService = groupService;
}
#Override
public boolean isValid(final Entity entity, final ConstraintValidatorContext context) {
Something something = groupService.findByValue(entity.getValue());
// Validate all this stuff
}
}
I need to pass a Spring service to the validator.
I have a default constructor because there is an issue
Spring unit test issue with Validator

I also tried to solve this, however the tests are fully low level unit tests on my side and I wanted to avoid too much of a context. I couldn't even use your approach. The solution my side was to add a custom ConstraintValidatorBean into the mockmvc. That custom implementation could get a list of validator class Class objects and so if the factory tried to create a validator, I returned a Mockito.mock version of it instead of the real one and I could also look that up for expectation settings.

Feel free to share your knowledge. This is how I solved this task.
I created a Spring component and it has a static field (GroupService which is initialized in constructor).
#Component // Spring component.
class ServiceHolderComponent {
private static GroupService GROUP_SERVICE;
#Autowired
public ServiceHolderComponent(final GroupService groupService) {
GROUP_SERVICE = Validate.notNull(groupService); //apache lib
}
public static GroupService getGroupService() {
return GROUP_SERVICE;
}
}
And now validator with default constructor.
public class EntityDynamicValidator implements ConstraintValidator<SomeConstraint, Entity> {
private GroupService groupService;
public UserDynamicEnumValidator() {
this(ServiceHolderComponent.getGroupService());
}
public UserDynamicEnumValidator(final GroupService groupService) {
this.groupService = groupService;
}
#Override
public boolean isValid(final Entity entity, final ConstraintValidatorContext context) {
Something something = groupService.findByValue(entity.getValue());
// Validate all this stuff
}
}
Apache library https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/Validate.html
import org.apache.commons.lang3.Validate;
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>

Related

How to unit test methods together with its constraint validators (which some should be mocked out)?

My app has a service layer which is composed by CDI applications scoped beans:
#ApplicationScoped
#Transactional
public class PostService {
#Inject private PostRepository postRepo;
#Inject private UserRepository userRepo;
#Inject private SectionRepository sectionRepo;
#Inject private LoggedInUser loggedInUser;
public PostDto getPost(#PostExists int id){
Post p = postRepo.findById(id);
//create post DTO from p
return post;
}
public void delete(#PostExists int id){
postRepo.remove(postRepo.findById(id));
}
public int newPost(#NotBlank #Max(255) String title,
#Max(2000) String body,
#SectionExists String sectionName){
User user = userRepo.getByName(loggedInUser.getUsername());
Section section = sectionRepo.getByName(sectionName);
Post post = new Post();
post.setTitle(title);
post.setContent(body == null || body.isBlank() ? "" : body);
post.setAuthor(user);
post.setSection(section);
post.setType(TEXT);
return postRepo.insert(post).getId();
}
}
When a method gets called, an interceptor (in my case BValInterceptor.class from Apache BVal) checks if the method contract is respected by checking the annotations and validating the parameters accordingly.
As you can see, there are some custom constraints like #SectionExists, #PostExists that may hit the database:
public class SectionExistsValidator implements ConstraintValidator<SectionExists, String> {
#Inject SectionRepository sectionRepo;
#Override
public void initialize(SectionExists constraintAnnotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return (sectionRepo.getByName(value) != null);
}
}
public class PostExistsValidator implements ConstraintValidator<PostExists, Integer> {
#Inject PostRepository postRepo;
#Override
public void initialize(PostExists constraintAnnotation) {}
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return (postRepo.findById(value) != null);
}
}
What I'd like to do is to unit test my business methods (getpost, delete, newPost) together with its validators. The validators that may hit the database should be mocked (or their dependency should be mocked).
How can I achieve this? How could I make injections (and mock injections) work for validators in unit tests?
Here what I'm using:
TomEE 8.0.8
Apache BVal for Bean Validation JSR 303/JSR380 (included in TomEE)
Apache OpenWebBeans for CDI (included in TomEE)
JUnit 5
Mockito
I can use OpenEJB's ApplicationComposer or Arquillian to run an embedded container. However, I've never used Arquillian.
In the end I went for this really cool library (cdimock) that does exactly what i needed: put the mocks in a custom CDI scope so that the same mock instances can be injected in other beans inside the test case. Such thing can also be achievable with cdi-unit #Produces #Mock annotations (Although i haven't tried it personally since it only supports Weld)
This is my test class' code:
#RunWithApplicationComposer(mode = ExtensionMode.PER_EACH)
#ExtendWith({MockitoExtension.class, CdiMocking.class})
#MockitoSettings(strictness = LENIENT)
#Classes(cdi = true,
value={PostService.class},
cdiInterceptors = BValInterceptor.class,
cdiStereotypes = CdiMock.class)
public class PostServiceTest {
#Mock SectionRepository sectionRepository;
#Mock PostRepository postRepository;
#Mock UserRepository userRepository;
#Inject PostService service;
#BeforeEach
void setUp() {}
#AfterEach
void tearDown() {}
#Test
public void noSectionFoundNewPost(){
String sectionName = "idontexist";
when(sectionRepository.getByName(sectionName)).thenReturn(null);
assertThrows(ConstraintViolationException.class,
() -> service.newPost("title", "body", sectionName));
}
}
In the code i'm using OpenEJB's Application Composer but i can easily switch to any embedded CDI container

Spring RabbitMQ: implement additional validations without custom annotations

In a Spring RabbitMQ project I am looking for a way to programmatically validate an object that has JSR303 annotations (like #NotNull, #Size, etc) while at the same time requires some custom validation logic. I would normally use a ConstraintValidator in combination with a custom Annotation, but the use of custom Annotations is not an option in this case.
I have the following (simplified) class, which is generated by Swagger and therefore cannot be edited:
#ApiModel(description="User")
public class User {
private String name;
#NotNull
#Size(min = 1, max = 6)
public String getName() {
return this.name;
}
...
}
The additional validation logic is encapsulated in a validator:
#Component
public class UserValidator implements org.springframework.validation.Validator {
#Override
public boolean supports(Class<?> aClass) {
return User.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
User user = (User) o;
...
if(!valid) {
errors.reject("some rejection");
}
}
}
The service in which the validation occurs:
#Service
#RequiredArgsConstructor
public class SomeService {
private final javax.validation.Validator validator; // might as well be org.springframework.validation.Validator if that works better
public void someMethod(User user) {
if (!validator.validate(user).isEmpty()) {
// handle invalid user
}
...
}
}
However, the UserValidator is not being invoked. Is there some way to make Spring aware of the UserValidator? I have read some topics on using an InitBinder, however as this is not a web MVC project but a rabbitMQ project I'm not sure whether this can be used.
It is not clear from your description how this is relevant to Spring AMQP, but if you want to use a validator on the listener method level, you should configure it respectively:
#Configuration
#EnableRabbit
public class Config implements RabbitListenerConfigurer {
...
#Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setValidator(new MyValidator());
}
}
See docs for more info: https://docs.spring.io/spring-amqp/docs/current/reference/html/#rabbit-validation

Java: Use a Spring Autowired library with Guice program

I have a server that I've been developing. It uses Guice for it DI. There is a team library that I need to use. It uses #Autowired for its DI.
My code
import team.requiredlibrary.NeededClass
public class MyClass extends NeededClass {
#Inject
public MyClass() {
}
}
Imported library
public class NeededClass {
#Autowired
private NestedClass1 nestedClass1;
#Autowired
private NestedClass2 nestedClass2;
}
When this runs, nestedClass1 and nestedClass2 are null.
What are my options here. Is there a way to get Guice to recognize #Autowired? If it was an option to update the team library replacing #Autowired with #Inject, would the work be that simple and be worth the effort? Assuming the worst case, would I be stuck replacing Guice with Spring in my project for DI?
Spring supports #Inject, so if it is possible to replace #Autowired by #Inject in your team library everything should be fine.
Otherwise you can create a custom injector for guice, see this link: https://github.com/google/guice/wiki/CustomInjections
You can create a custom injector for #Autowired and use the method getProvider(Class) of type TypeEncounter of your TypeListener to get a provider that you can use to retrieve the required type.
Of course you have to configure Guice correctly to be able to inject those types.
The issue with this approach: You maybe will not cover everything that Spring supports, e. g. #Qualifier annotations.
Here is a working example:
public class AutowiredTypeListener implements TypeListener {
public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
Class<?> clazz = typeLiteral.getRawType();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
Autowired annotation = field.getAnnotation(Autowired.class);
if (annotation != null) {
typeEncounter.register(new AutowiredMembersInjector<I>(field,
typeEncounter.getProvider(field.getType())));
}
}
clazz = clazz.getSuperclass();
}
}
}
public class AutowiredMembersInjector<T> implements MembersInjector<T> {
private final Field field;
private final Provider<?> instanceProvider;
public AutowiredMembersInjector(Field field, Provider<?> instanceProvider) {
this.field = field;
this.instanceProvider = instanceProvider;
field.setAccessible(true);
}
public void injectMembers(T t) {
try {
field.set(t, instanceProvider.get());
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
And then just add bindListener(Matchers.any(), new AutowiredTypeListener()); inside your Guice module.
What I directly noticed is that #Autowired supports optional injections, this seems to be impossible, because as soon as a provider was created using the typeEncounter it is required to have a binding of the class registered.

Autowired gives Null value in Custom Constraint validator

I am totally new to Spring and I have looked in to a few answers on SO for the asked problem. Here are the links:
Spring 3.1 Autowiring does not work inside custom constraint validator
Autowiring a service into a validator
Autowired Repository is Null in Custom Constraint Validator
I have a Spring project in which I want to use Hibernate Validator for an object validation. Based on what I read online and a few forums I tried to inject validator as follows:
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean().getValidator();
}
But wherever I was using
#Autowired
Validator validator;
It was taking Spring's validator implementation instead of the Hibernate's validator. I couldn't figure out how to exactly inject Hibernate validator and simply Autowire it across other classes so I used a cheap trick, now my Java Config looks like this
#Bean
public Validator validator() {
// ValidatorImpl is Hibernate's implementation of the Validator
return new ValidatorImpl();
}
(I would really appreciate if someone can actually point me into the right direction on how to avoid getting Hibernate Validator in this Hacky way)
But lets come to the main issue here:
Here is custom validation definition
#Target( { METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER } )
#Retention(RUNTIME)
#Constraint(validatedBy = EmployeeValidator.class)
#Documented
public #interface EmployeeValidation {
String message() default "{constraints.employeeConstraints}";
public abstract Class<?>[] groups() default {};
public abstract Class<? extends Payload>[] payload() default {};
}
My Custom Validator
public class EmployeeValidator implements ConstraintValidator<EmployeeValidation , Object> {
#Autowired
private EmployeeService employeeService;
#Override
public void initialize(EmployeeValidation constraintAnnotation) {
//do Something
}
#Override
public boolean isValid(String type, ConstraintValidatorContext context) {
return false;
}
}
In the above Custom Constraint Validator I get the employeeService null. I know that any implementations of ConstraintValidator are not instantiated when Spring is starting up but I thought adding the ValidatorImpl() will actually fix that. But it didn't.
Now I am stuck with a really hacky workaround and I do not want to continue with a code like that. Can someone please help me with my situation.
P.S. These are my imports in the Java Config file:
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
I hope the solution will help someone:
#Bean
public Validator validator () {
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure().constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
Initializing the validator with SpringConstraintValidatorFactory so that injection works and providing the validator implementation to be Hibernate.class works in the following manner:
Your objects will be validated by the library of your choice
Your custom validators will be able to use Spring's functionality while having validation to be executed by Hibernate.
How it works:
Hibernate's ConstraintValidatorFactory does not initialize any ConstraintValidators unless they are called but SpringConstraintValidatorFactory does by giving AutowireCapableBeanFactory to it.
EDIT
As mentioned in one of the comments by #shabyasaschi To inject autowireCapableBeanFactory you can change the method signature as:
Validator validator(final AutowireCapableBeanFactory autowireCapableBeanFactory) {
or add getter and setter for it in the config file as follows:
public AutowireCapableBeanFactory getAutowireCapableBeanFactory() {
return autowireCapableBeanFactory;
}
public void setAutowireCapableBeanFactory(AutowireCapableBeanFactory autowireCapableBeanFactory) {
this.autowireCapableBeanFactory = autowireCapableBeanFactory;
}
You can fix this with two aproaches:
Try to inject Services on your validator using Spring.
Initialize it manually overriding Validator's initialize method.
I had the same problem time ago and finally i decided to use second option avoiding tons of problems.
As you point you must define one initialize method on your validator and there you can use a ServiceUtils to get the service bean you need:
#Autowired
private EmployeeService employeeService;
#Override
public void initialize(EmployeeValidation constraintAnnotation) {
//Use an utility service to get Spring beans
employeeService = ServiceUtils.getEmployeeService();
}
And ServiceUtils is a normal Spring bean with a static reference to itself used in the static methods.
#Component
public class ServiceUtils {
private static ServiceUtils instance;
#Autowired
private EmployeeService employeeService;
/* Post constructor */
#PostConstruct
public void fillInstance() {
instance = this;
}
/*static methods */
public static EmployeeService getEmployeeService) {
return instance.employeeService;
}
}
So you are using Spring to inject the services you need but not in the usual way.
Hope this helps.
In your bean definition
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean().getValidator();
}
What's the type of Validator in the method definition? You should make sure it returns javax.validation.Validator, not Validator from Spring.
Letting Spring bootstrap the validator will it also cause to pass a SpringConstraintValidatorFactory to Hibernate Validator which will enable dependency injection within constraint validators.
There is nothing wrong with your code It depends how are you creating your ValidatorFactory.
Create a bean and let Spring handle it.
#Bean
public ValidatorFactory validatorFactory(){
return Validation.buildDefaultValidatorFactory();
}
Think about it. There should have been no issue with using the #Autowired inside a Constraint validator class. This means that something is wrong.
This issue has been reported on various platform and I have not seen a good solution. I have seen some workaround though.
Here is what I found.
You may notice that the validation is happening twice. the first time it should work but the second time you got a null related error message. The problem should be that the entity or the class that you is being validated is being used twice in your controller. For example, you may want validate the entity class and try to save it at the same time in the same method in the controller method. when you try to save the entity, it will try to validate the object again and this time the #Autowired object will be null.
Here is what you can do for this scenario
You can use dto to carry the validation annotation and copy the property of the dto class to your entity class before you save it into the database. your scenario may be different but the solution approach should be the same.
Below is an illustration of code that works
public ResponseEntity<InstitutionModel> create(#Valid #RequestBody InstitutionDto institutiondto) {
Institution institution = new Institution();
BeanUtils.copyProperties(institutiondto, institution);
return Optional.of(this.institutionService.save(institution)).map(institutionModelAssembler::toModel)
.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
private XXXService xxxService = SpringContextHolder.getBean(XXXService.class);
Thats worked for me. For guys who search at now
public class EmployeeValidator implements ConstraintValidator<EmployeeValidation , Object> {
private EmployeeService employeeService;
public EmployeeValidator(EmployeeService employeeService){
this.employeeService = employeeService;
}
...
}

NoSuchBeanDefinitionException for dependencies of mocked beans

I am attempting to use mocks in my integration test and am not having much luck. I am using Spring 3.1.1 and Mockito 1.9.0, and the situation is as follows:
#Component
public class ClassToTest {
#Resource
private Dependency dependency;
}
and
#Component
public class Dependency {
#Resource
private NestedDependency nestedDependency;
}
Now, I want to do an integration test of ClassToTest using Spring's JavaConfig. This is what I have attempted, and it doesn't work:
#Test
#ContextConfiguration
public class ClassToTestIntegrationTest {
#Resource
private ClassToTest classToTest;
#Resource
private Dependency mockDependency;
#Test
public void someTest() {
verify(mockDependency).doStuff();
// other Mockito magic...
}
#Configuration
static class Config {
#Bean
public ClassToTest classToTest() {
return new ClassToTest();
}
#Bean
public Dependency dependency() {
return Mockito.mock(Dependency.class);
}
}
}
I have simplified my setup to make the question easier to understand. In reality I have more dependencies and only want to mock some of them - the others are real, based on config imported from my prod #Configuration classes.
What ends up happening is I get a NoSuchBeanDefinitionException saying that there are no beans of type NestedDependency in the application context. I don't understand this - I thought Spring would receive Mockito's mocked instance of Dependency and not even look at autowiring it. Since this isn't working I end up having to mock my entire object graph - which completely defeats the point of mocking!
Thanks in advance for any help!
I had the same problem and I found another solution.
When Spring instantiate all your beans, it will check if it's a Mockito Mock and in this case, I return false for injection property. To use it, just inject it in a Spring context
Code below:
public class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
private static final MockUtil mockUtil = new MockUtil();
public MockBeanFactory() {
super();
}
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockUtil.isMock(bean);
}
}
What Mockito does when mocking classes is it creates a subclass using cglib having some fancy name like: Dependency$EnhancerByMockito (IIRC). As you probably know, subclasses inherit fields from their parent:
#Component
public class Dependency {
#Resource
private NestedDependency nestedDependency;
}
public class Dependency$EnhancerByMockito extends Dependency{
//...
}
This means Spring still sees the field in base class when presented with mock. What you can do:
Use interfaces, which will cause Mockito to employ dynamic proxies rather than CGLIB-generated classes
Mock NestedDependency - I know it will just cascade the problem one level further
Disable #Resource annotation scanning for tests

Categories

Resources