/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.validation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.validation.annotation.Validated;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ValidationAutoConfiguration}.
*
* @author Stephane Nicoll
* @author Phillip Webb
*/
public class ValidationAutoConfigurationTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
private AnnotationConfigApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void validationAutoConfigurationShouldConfigureDefaultValidator() {
load(Config.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
assertThat(springValidatorNames).containsExactly("defaultValidator");
Validator jsrValidator = this.context.getBean(Validator.class);
org.springframework.validation.Validator springValidator = this.context
.getBean(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
assertThat(jsrValidator).isEqualTo(springValidator);
assertThat(isPrimaryBean("defaultValidator")).isTrue();
}
@Test
public void validationAutoConfigurationWhenUserProvidesValidatorShouldBackOff() {
load(UserDefinedValidatorConfig.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidatorNames).containsExactly("customValidator");
assertThat(springValidatorNames).containsExactly("customValidator");
org.springframework.validation.Validator springValidator = this.context
.getBean(org.springframework.validation.Validator.class);
Validator jsrValidator = this.context.getBean(Validator.class);
assertThat(jsrValidator).isInstanceOf(OptionalValidatorFactoryBean.class);
assertThat(jsrValidator).isEqualTo(springValidator);
assertThat(isPrimaryBean("customValidator")).isFalse();
}
@Test
public void validationAutoConfigurationWhenUserProvidesDefaultValidatorShouldNotEnablePrimary() {
load(UserDefinedDefaultValidatorConfig.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
assertThat(springValidatorNames).containsExactly("defaultValidator");
assertThat(isPrimaryBean("defaultValidator")).isFalse();
}
@Test
public void validationAutoConfigurationWhenUserProvidesJsrValidatorShouldBackOff() {
load(UserDefinedJsrValidatorConfig.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidatorNames).containsExactly("customValidator");
assertThat(springValidatorNames).isEmpty();
assertThat(isPrimaryBean("customValidator")).isFalse();
}
@Test
public void validationAutoConfigurationWhenUserProvidesSpringValidatorShouldCreateJsrValidator() {
load(UserDefinedSpringValidatorConfig.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
assertThat(springValidatorNames).containsExactly("customValidator",
"anotherCustomValidator", "defaultValidator");
Validator jsrValidator = this.context.getBean(Validator.class);
org.springframework.validation.Validator springValidator = this.context
.getBean(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
assertThat(jsrValidator).isEqualTo(springValidator);
assertThat(isPrimaryBean("defaultValidator")).isTrue();
}
@Test
public void validationAutoConfigurationWhenUserProvidesPrimarySpringValidatorShouldRemovePrimaryFlag() {
load(UserDefinedPrimarySpringValidatorConfig.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
assertThat(springValidatorNames).containsExactly("customValidator",
"anotherCustomValidator", "defaultValidator");
Validator jsrValidator = this.context.getBean(Validator.class);
org.springframework.validation.Validator springValidator = this.context
.getBean(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
assertThat(springValidator)
.isEqualTo(this.context.getBean("anotherCustomValidator"));
assertThat(isPrimaryBean("defaultValidator")).isFalse();
}
@Test
public void validationIsEnabled() {
load(SampleService.class);
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
SampleService service = this.context.getBean(SampleService.class);
service.doSomething("Valid");
this.thrown.expect(ConstraintViolationException.class);
service.doSomething("KO");
}
@Test
public void validationUsesCglibProxy() {
load(DefaultAnotherSampleService.class);
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
DefaultAnotherSampleService service = this.context
.getBean(DefaultAnotherSampleService.class);
service.doSomething(42);
this.thrown.expect(ConstraintViolationException.class);
service.doSomething(2);
}
@Test
public void validationCanBeConfiguredToUseJdkProxy() {
load(AnotherSampleServiceConfiguration.class,
"spring.aop.proxy-target-class=false");
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
assertThat(this.context.getBeansOfType(DefaultAnotherSampleService.class))
.isEmpty();
AnotherSampleService service = this.context.getBean(AnotherSampleService.class);
service.doSomething(42);
this.thrown.expect(ConstraintViolationException.class);
service.doSomething(2);
}
@Test
public void userDefinedMethodValidationPostProcessorTakesPrecedence() {
load(SampleConfiguration.class);
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
Object userMethodValidationPostProcessor = this.context
.getBean("testMethodValidationPostProcessor");
assertThat(this.context.getBean(MethodValidationPostProcessor.class))
.isSameAs(userMethodValidationPostProcessor);
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class))
.hasSize(1);
assertThat(this.context.getBean(Validator.class))
.isNotSameAs(new DirectFieldAccessor(userMethodValidationPostProcessor)
.getPropertyValue("validator"));
}
private boolean isPrimaryBean(String beanName) {
return this.context.getBeanDefinition(beanName).isPrimary();
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(ctx, environment);
if (config != null) {
ctx.register(config);
}
ctx.register(ValidationAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
}
@Configuration
static class Config {
}
@Configuration
static class UserDefinedValidatorConfig {
@Bean
public OptionalValidatorFactoryBean customValidator() {
return new OptionalValidatorFactoryBean();
}
}
@Configuration
static class UserDefinedDefaultValidatorConfig {
@Bean
public OptionalValidatorFactoryBean defaultValidator() {
return new OptionalValidatorFactoryBean();
}
}
@Configuration
static class UserDefinedJsrValidatorConfig {
@Bean
public Validator customValidator() {
return mock(Validator.class);
}
}
@Configuration
static class UserDefinedSpringValidatorConfig {
@Bean
public org.springframework.validation.Validator customValidator() {
return mock(org.springframework.validation.Validator.class);
}
@Bean
public org.springframework.validation.Validator anotherCustomValidator() {
return mock(org.springframework.validation.Validator.class);
}
}
@Configuration
static class UserDefinedPrimarySpringValidatorConfig {
@Bean
public org.springframework.validation.Validator customValidator() {
return mock(org.springframework.validation.Validator.class);
}
@Bean
@Primary
public org.springframework.validation.Validator anotherCustomValidator() {
return mock(org.springframework.validation.Validator.class);
}
}
@Validated
static class SampleService {
public void doSomething(@Size(min = 3, max = 10) String name) {
}
}
interface AnotherSampleService {
void doSomething(@Min(42) Integer counter);
}
@Validated
static class DefaultAnotherSampleService implements AnotherSampleService {
@Override
public void doSomething(Integer counter) {
}
}
@Configuration
static class AnotherSampleServiceConfiguration {
@Bean
public AnotherSampleService anotherSampleService() {
return new DefaultAnotherSampleService();
}
}
@Configuration
static class SampleConfiguration {
@Bean
public MethodValidationPostProcessor testMethodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
}