/* * 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(); } } }