Relocate projects to spring-boot-project

Move projects to better reflect the way that Spring Boot is released.

The following projects are under `spring-boot-project`:

  - `spring-boot`
  - `spring-boot-autoconfigure`
  - `spring-boot-tools`
  - `spring-boot-starters`
  - `spring-boot-actuator`
  - `spring-boot-actuator-autoconfigure`
  - `spring-boot-test`
  - `spring-boot-test-autoconfigure`
  - `spring-boot-devtools`
  - `spring-boot-cli`
  - `spring-boot-docs`

See gh-9316
This commit is contained in:
Phillip Webb
2017-09-19 14:29:46 -07:00
parent 0419d42b7c
commit 0ba4830b4f
4023 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2012-2016 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.test.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* Annotation that can be used to override
* {@link EnableAutoConfiguration @EnableAutoConfiguration}. Often used in combination
* with {@link ImportAutoConfiguration} to limit the auto-configuration classes that are
* loaded.
*
* @author Phillip Webb
* @since 1.4.0
* @see EnableAutoConfiguration#ENABLED_OVERRIDE_PROPERTY
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OverrideAutoConfiguration {
/**
* The value of the {@link EnableAutoConfiguration#ENABLED_OVERRIDE_PROPERTY enabled
* override property}.
* @return the override value
*/
boolean enabled();
}

View File

@@ -0,0 +1,76 @@
/*
* 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.test.autoconfigure;
import java.util.List;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.MergedContextConfiguration;
/**
* {@link ContextCustomizerFactory} to support
* {@link OverrideAutoConfiguration @OverrideAutoConfiguration}.
*
* @author Phillip Webb
*/
class OverrideAutoConfigurationContextCustomizerFactory
implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configurationAttributes) {
OverrideAutoConfiguration annotation = AnnotatedElementUtils
.findMergedAnnotation(testClass, OverrideAutoConfiguration.class);
if (annotation != null && !annotation.enabled()) {
return new DisableAutoConfigurationContextCustomizer();
}
return null;
}
/**
* {@link ContextCustomizer} to disable full auto-configuration.
*/
private static class DisableAutoConfigurationContextCustomizer
implements ContextCustomizer {
@Override
public void customizeContext(ConfigurableApplicationContext context,
MergedContextConfiguration mergedConfig) {
TestPropertyValues
.of(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY + "=false")
.applyTo(context);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj != null && obj.getClass() == getClass());
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.test.autoconfigure;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportMessage;
import org.springframework.boot.test.context.DefaultTestExecutionListenersPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* Alternative {@link DependencyInjectionTestExecutionListener} prints the
* {@link ConditionEvaluationReport} when the context cannot be prepared.
*
* @author Phillip Webb
* @since 1.4.1
*/
public class SpringBootDependencyInjectionTestExecutionListener
extends DependencyInjectionTestExecutionListener {
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
try {
super.prepareTestInstance(testContext);
}
catch (Exception ex) {
outputConditionEvaluationReport(testContext);
throw ex;
}
}
private void outputConditionEvaluationReport(TestContext testContext) {
try {
ApplicationContext context = testContext.getApplicationContext();
if (context instanceof ConfigurableApplicationContext) {
ConditionEvaluationReport report = ConditionEvaluationReport
.get(((ConfigurableApplicationContext) context).getBeanFactory());
System.err.println(new ConditionEvaluationReportMessage(report));
}
}
catch (Exception ex) {
// Allow original failure to be reported
}
}
static class PostProcessor implements DefaultTestExecutionListenersPostProcessor {
@Override
public Set<Class<? extends TestExecutionListener>> postProcessDefaultTestExecutionListeners(
Set<Class<? extends TestExecutionListener>> listeners) {
Set<Class<? extends TestExecutionListener>> updated = new LinkedHashSet<>(
listeners.size());
for (Class<? extends TestExecutionListener> listener : listeners) {
updated.add(
listener.equals(DependencyInjectionTestExecutionListener.class)
? SpringBootDependencyInjectionTestExecutionListener.class
: listener);
}
return updated;
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.core;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheType;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.cache.support.NoOpCacheManager;
/**
* Annotation that can be applied to a test class to enable and configure
* auto-configuration of caching. By default this annotation installs a
* {@link NoOpCacheManager}.
*
* @author Phillip Webb
* @since 1.4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureCache {
@PropertyMapping("spring.cache.type")
CacheType cacheProvider() default CacheType.NONE;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2016 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.
*/
/**
* Auto-configuration for core parts common to most Spring Boot applications.
*/
package org.springframework.boot.test.autoconfigure.core;

View File

@@ -0,0 +1,44 @@
/*
* 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.test.autoconfigure.data.ldap;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical Data LDAP tests.
* Most tests should consider using {@link DataLdapTest @DataLdapTest} rather than using
* this annotation directly.
*
* @author Eddú Meléndez
* @since 2.0.0
* @see DataLdapTest
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureDataLdap {
}

View File

@@ -0,0 +1,93 @@
/*
* 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.test.autoconfigure.data.ldap;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical LDAP test. Can be used when a test focuses <strong>only</strong> on LDAP
* components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to LDAP tests.
* <p>
* By default, tests annotated with {@code @DataLdapTest} will use an embedded in-memory
* LDAP process (if available).
*
* @author Eddú Meléndez
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataLdapTypeExcludeFilter.class)
@AutoConfigureCache
@AutoConfigureDataLdap
@ImportAutoConfiguration
public @interface DataLdapTest {
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default no beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,73 @@
/*
* 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.test.autoconfigure.data.ldap;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link DataLdapTest @DataLdapTest}.
*
* @author Eddú Meléndez
*/
class DataLdapTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private final DataLdapTest annotation;
DataLdapTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
DataLdapTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
default:
throw new IllegalStateException("Unsupported type " + type);
}
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.test.autoconfigure.data.mongo;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical Data MongoDB
* tests. Most tests should consider using {@link DataMongoTest @DataMongoTest} rather
* than using this annotation directly.
*
* @author Michael Simons
* @since 1.5.0
* @see DataMongoTest
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureDataMongo {
}

View File

@@ -0,0 +1,94 @@
/*
* 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.test.autoconfigure.data.mongo;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical MongoDB test. Can be used when a test focuses <strong>only</strong> on
* MongoDB components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to MongoDB tests.
* <p>
* By default, tests annotated with {@code @DataMongoTest} will use an embedded in-memory
* MongoDB process (if available).
*
* @author Michael Simons
* @author Stephane Nicoll
* @since 1.5.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataMongoTypeExcludeFilter.class)
@AutoConfigureCache
@AutoConfigureDataMongo
@ImportAutoConfiguration
public @interface DataMongoTest {
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default no beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,73 @@
/*
* 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.test.autoconfigure.data.mongo;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link DataMongoTest @DataMongoTest}.
*
* @author Michael Simons
*/
class DataMongoTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private final DataMongoTest annotation;
DataMongoTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
DataMongoTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(final FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
default:
throw new IllegalStateException("Unsupported type " + type);
}
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.test.autoconfigure.data.neo4j;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical Data Neo4j
* tests. Most tests should consider using {@link DataNeo4jTest @DataNeo4jTest} rather
* than using this annotation directly.
*
* @author Eddú Meléndez
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureDataNeo4j {
}

View File

@@ -0,0 +1,97 @@
/*
* 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.test.autoconfigure.data.neo4j;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
import org.springframework.transaction.annotation.Transactional;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical Neo4j test. Can be used when a test focuses <strong>only</strong> on
* Neo4j components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to Neo4j tests.
* <p>
* By default, tests annotated with {@code @DataNeo4jTest} will use an embedded in-memory
* Neo4j process (if available). They will also be transactional with the usual
* test-related semantics (i.e. rollback by default).
*
* @author Eddú Meléndez
* @author Stephane Nicoll
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataNeo4jTypeExcludeFilter.class)
@Transactional
@AutoConfigureCache
@AutoConfigureDataNeo4j
@ImportAutoConfiguration
public @interface DataNeo4jTest {
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default no beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,72 @@
/*
* 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.test.autoconfigure.data.neo4j;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link DataNeo4jTest @DataNeo4jTest}.
*
* @author Eddú Meléndez
*/
class DataNeo4jTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private final DataNeo4jTest annotation;
DataNeo4jTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
DataNeo4jTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
default:
throw new IllegalStateException("Unsupported type " + type);
}
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.test.autoconfigure.data.redis;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical Data redis
* tests. Most tests should consider using {@link DataRedisTest @DataRedisTest} rather
* than using this annotation directly.
*
* @author Jayaram Pradhan
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureDataRedis {
}

View File

@@ -0,0 +1,90 @@
/*
* 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.test.autoconfigure.data.redis;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical Data Redis test. Can be used when a test focuses <strong>only</strong> on
* Redis components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to Redis tests.
*
* @author Jayaram Pradhan
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataRedisTypeExcludeFilter.class)
@AutoConfigureCache
@AutoConfigureDataRedis
@ImportAutoConfiguration
public @interface DataRedisTest {
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default no beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,73 @@
/*
* 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.test.autoconfigure.data.redis;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link DataRedisTest @DataRedisTest}.
*
* @author Jayaram Pradhan
*/
class DataRedisTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private final DataRedisTest annotation;
DataRedisTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
DataRedisTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
default:
throw new IllegalStateException("Unsupported type " + type);
}
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.test.autoconfigure.filter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Set;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ObjectUtils;
/**
* Abstract base class for a {@link TypeExcludeFilter} that can be customized using an
* annotation.
*
* @author Phillip Webb
* @since 1.4.0
*/
public abstract class AnnotationCustomizableTypeExcludeFilter extends TypeExcludeFilter
implements BeanClassLoaderAware {
private ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (hasAnnotation()) {
return !(include(metadataReader, metadataReaderFactory)
&& !exclude(metadataReader, metadataReaderFactory));
}
return false;
}
protected boolean include(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (new FilterAnnotations(this.classLoader, getFilters(FilterType.INCLUDE))
.anyMatches(metadataReader, metadataReaderFactory)) {
return true;
}
if (isUseDefaultFilters()
&& defaultInclude(metadataReader, metadataReaderFactory)) {
return true;
}
return false;
}
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
for (Class<?> include : getDefaultIncludes()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, include)) {
return true;
}
}
for (Class<?> component : getComponentIncludes()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, component)) {
return true;
}
}
return false;
}
protected boolean exclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return new FilterAnnotations(this.classLoader, getFilters(FilterType.EXCLUDE))
.anyMatches(metadataReader, metadataReaderFactory);
}
@SuppressWarnings("unchecked")
protected final boolean isTypeOrAnnotated(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory, Class<?> type)
throws IOException {
AnnotationTypeFilter annotationFilter = new AnnotationTypeFilter(
(Class<? extends Annotation>) type);
AssignableTypeFilter typeFilter = new AssignableTypeFilter(type);
return annotationFilter.match(metadataReader, metadataReaderFactory)
|| typeFilter.match(metadataReader, metadataReaderFactory);
}
protected abstract boolean hasAnnotation();
protected abstract Filter[] getFilters(FilterType type);
protected abstract boolean isUseDefaultFilters();
protected abstract Set<Class<?>> getDefaultIncludes();
protected abstract Set<Class<?>> getComponentIncludes();
protected enum FilterType {
INCLUDE, EXCLUDE
}
@Override
public int hashCode() {
final int prime = 31;
int result = 0;
result = prime * result + Boolean.hashCode(hasAnnotation());
for (FilterType filterType : FilterType.values()) {
result = prime * result
+ ObjectUtils.nullSafeHashCode(getFilters(filterType));
}
result = prime * result + Boolean.hashCode(isUseDefaultFilters());
result = prime * result + ObjectUtils.nullSafeHashCode(getDefaultIncludes());
result = prime * result + ObjectUtils.nullSafeHashCode(getComponentIncludes());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
AnnotationCustomizableTypeExcludeFilter other = (AnnotationCustomizableTypeExcludeFilter) obj;
boolean result = true;
result = result && hasAnnotation() == other.hasAnnotation();
for (FilterType filterType : FilterType.values()) {
result &= ObjectUtils.nullSafeEquals(getFilters(filterType),
other.getFilters(filterType));
}
result = result && isUseDefaultFilters() == other.isUseDefaultFilters();
result = result && ObjectUtils.nullSafeEquals(getDefaultIncludes(),
other.getDefaultIncludes());
result = result && ObjectUtils.nullSafeEquals(getComponentIncludes(),
other.getComponentIncludes());
return result;
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.test.autoconfigure.filter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AspectJTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.RegexPatternTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
/**
* Utility to load {@link TypeFilter TypeFilters} from {@link Filter @Filter} annotations.
*
* @author Phillip Webb
* @since 1.4.0
*/
public class FilterAnnotations implements Iterable<TypeFilter> {
private final ClassLoader classLoader;
private final List<TypeFilter> filters;
public FilterAnnotations(ClassLoader classLoader, Filter[] filters) {
Assert.notNull(filters, "Filters must not be null");
this.classLoader = classLoader;
this.filters = createTypeFilters(filters);
}
private List<TypeFilter> createTypeFilters(Filter[] filters) {
List<TypeFilter> typeFilters = new ArrayList<>();
for (Filter filter : filters) {
for (Class<?> filterClass : filter.classes()) {
typeFilters.add(createTypeFilter(filter.type(), filterClass));
}
for (String pattern : filter.pattern()) {
typeFilters.add(createTypeFilter(filter.type(), pattern));
}
}
return Collections.unmodifiableList(typeFilters);
}
@SuppressWarnings("unchecked")
private TypeFilter createTypeFilter(FilterType filterType, Class<?> filterClass) {
switch (filterType) {
case ANNOTATION:
Assert.isAssignable(Annotation.class, filterClass,
"An error occurred while processing a ANNOTATION type filter: ");
return new AnnotationTypeFilter((Class<Annotation>) filterClass);
case ASSIGNABLE_TYPE:
return new AssignableTypeFilter(filterClass);
case CUSTOM:
Assert.isAssignable(TypeFilter.class, filterClass,
"An error occurred while processing a CUSTOM type filter: ");
return BeanUtils.instantiateClass(filterClass, TypeFilter.class);
}
throw new IllegalArgumentException(
"Filter type not supported with Class value: " + filterType);
}
private TypeFilter createTypeFilter(FilterType filterType, String pattern) {
switch (filterType) {
case ASPECTJ:
return new AspectJTypeFilter(pattern, this.classLoader);
case REGEX:
return new RegexPatternTypeFilter(Pattern.compile(pattern));
}
throw new IllegalArgumentException(
"Filter type not supported with String pattern: " + filterType);
}
@Override
public Iterator<TypeFilter> iterator() {
return this.filters.iterator();
}
public boolean anyMatches(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
for (TypeFilter filter : this) {
if (filter.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.filter;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.TypeExcludeFilter;
/**
* Annotation that can be on tests to define a set of {@link TypeExcludeFilter} classes
* that should be applied to {@link SpringBootApplication @SpringBootApplication}
* component scanning.
*
* @author Phillip Webb
* @since 1.4.0
* @see TypeExcludeFilter
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TypeExcludeFilters {
/**
* Specifies {@link TypeExcludeFilter} classes that should be applied to
* {@link SpringBootApplication @SpringBootApplication} component scanning. Classes
* specified here can either have a no-arg constructor or accept a single
* {@code Class<?>} argument if they need access to the {@code testClass}.
* @see TypeExcludeFilter
* @return the type exclude filters to apply
*/
Class<? extends TypeExcludeFilter>[] value();
}

View File

@@ -0,0 +1,123 @@
/*
* 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.test.autoconfigure.filter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.ReflectionUtils;
/**
* {@link ContextCustomizer} to support {@link TypeExcludeFilters @TypeExcludeFilters}.
*
* @author Phillip Webb
* @see TypeExcludeFilters
*/
class TypeExcludeFiltersContextCustomizer implements ContextCustomizer {
private static final String EXCLUDE_FILTER_BEAN_NAME = TypeExcludeFilters.class
.getName();
private final Set<TypeExcludeFilter> filters;
TypeExcludeFiltersContextCustomizer(Class<?> testClass,
Set<Class<? extends TypeExcludeFilter>> filterClasses) {
this.filters = instantiateTypeExcludeFilters(testClass, filterClasses);
}
private Set<TypeExcludeFilter> instantiateTypeExcludeFilters(Class<?> testClass,
Set<Class<? extends TypeExcludeFilter>> filterClasses) {
Set<TypeExcludeFilter> filters = new LinkedHashSet<>();
for (Class<? extends TypeExcludeFilter> filterClass : filterClasses) {
filters.add(instantiateTypeExcludeFilter(testClass, filterClass));
}
return Collections.unmodifiableSet(filters);
}
private TypeExcludeFilter instantiateTypeExcludeFilter(Class<?> testClass,
Class<?> filterClass) {
try {
Constructor<?> constructor = getTypeExcludeFilterConstructor(filterClass);
ReflectionUtils.makeAccessible(constructor);
if (constructor.getParameterCount() == 1) {
return (TypeExcludeFilter) constructor.newInstance(testClass);
}
return (TypeExcludeFilter) constructor.newInstance();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create filter for " + filterClass,
ex);
}
}
@Override
public int hashCode() {
return this.filters.hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj != null && getClass().equals(obj.getClass()) && this.filters
.equals(((TypeExcludeFiltersContextCustomizer) obj).filters));
}
@Override
public void customizeContext(ConfigurableApplicationContext context,
MergedContextConfiguration mergedContextConfiguration) {
if (!this.filters.isEmpty()) {
context.getBeanFactory().registerSingleton(EXCLUDE_FILTER_BEAN_NAME,
createDelegatingTypeExcludeFilter());
}
}
private TypeExcludeFilter createDelegatingTypeExcludeFilter() {
return new TypeExcludeFilter() {
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
for (TypeExcludeFilter filter : TypeExcludeFiltersContextCustomizer.this.filters) {
if (filter.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
return false;
}
};
}
private Constructor<?> getTypeExcludeFilterConstructor(Class<?> type)
throws NoSuchMethodException {
try {
return type.getDeclaredConstructor(Class.class);
}
catch (Exception ex) {
return type.getDeclaredConstructor();
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.test.autoconfigure.filter;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
/**
* {@link ContextCustomizerFactory} to support
* {@link TypeExcludeFilters @TypeExcludeFilters}.
*
* @author Phillip Webb
* @see TypeExcludeFiltersContextCustomizer
*/
class TypeExcludeFiltersContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configurationAttributes) {
TypeExcludeFilters annotation = AnnotatedElementUtils
.findMergedAnnotation(testClass, TypeExcludeFilters.class);
if (annotation != null) {
Set<Class<? extends TypeExcludeFilter>> filterClasses = new LinkedHashSet<>(
Arrays.asList(annotation.value()));
return new TypeExcludeFiltersContextCustomizer(testClass, filterClasses);
}
return null;
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2012-2016 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.
*/
/**
* Helper utilities for using {@link org.springframework.boot.context.TypeExcludeFilter}
* with auto-configured tests.
*/
package org.springframework.boot.test.autoconfigure.filter;

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.jdbc;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical jdbc tests. Most
* tests should consider using {@link JdbcTest @JdbcTest} rather than using this
* annotation directly.
*
* @author Stephane Nicoll
* @since 1.5.0
* @see JdbcTest
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ImportAutoConfiguration
public @interface AutoConfigureJdbc {
}

View File

@@ -0,0 +1,84 @@
/*
* 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.test.autoconfigure.jdbc;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.boot.test.autoconfigure.properties.SkipPropertyMapping;
/**
* Annotation that can be applied to a test class to configure a test database to use
* instead of any application defined or auto-configured {@link DataSource}.
*
* @author Phillip Webb
* @see TestDatabaseAutoConfiguration
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping("spring.test.database")
public @interface AutoConfigureTestDatabase {
/**
* Determines what type of existing DataSource beans can be replaced.
* @return the type of existing DataSource to replace
*/
@PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE)
Replace replace() default Replace.ANY;
/**
* The type of connection to be established when {@link #replace() replacing} the data
* source. By default will attempt to detect the connection based on the classpath.
* @return the type of connection to use
*/
EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE;
/**
* What the test database can replace.
*/
enum Replace {
/**
* Replace any DataSource bean (auto-configured or manually defined).
*/
ANY,
/**
* Only replace auto-configured DataSource.
*/
AUTO_CONFIGURED,
/**
* Don't replace the application default DataSource.
*/
NONE
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.jdbc;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
import org.springframework.transaction.annotation.Transactional;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical jdbc test. Can be used when a test focuses <strong>only</strong> on
* jdbc-based components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to jdbc tests.
* <p>
* By default, tests annotated with {@code @JdbcTest} will use an embedded in-memory
* database (replacing any explicit or usually auto-configured DataSource). The
* {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase} annotation can be used to
* override these settings.
* <p>
* If you are looking to load your full application configuration, but use an embedded
* database, you should consider {@link SpringBootTest @SpringBootTest} combined with
* {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase} rather than this
* annotation.
*
* @author Stephane Nicoll
* @see AutoConfigureJdbc
* @see AutoConfigureTestDatabase
* @see AutoConfigureCache
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(JdbcTypeExcludeFilter.class)
@Transactional
@AutoConfigureCache
@AutoConfigureJdbc
@AutoConfigureTestDatabase
@ImportAutoConfiguration
public @interface JdbcTest {
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default no beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
ComponentScan.Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
ComponentScan.Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,72 @@
/*
* 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.test.autoconfigure.jdbc;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link JdbcTest @JdbcTest}.
*
* @author Stephane Nicoll
*/
class JdbcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private final JdbcTest annotation;
JdbcTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
JdbcTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected ComponentScan.Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
}
throw new IllegalStateException("Unsupported type " + type);
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,206 @@
/*
* 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.test.autoconfigure.jdbc;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Auto-configuration for a test database.
*
* @author Phillip Webb
* @since 1.4.0
* @see AutoConfigureTestDatabase
*/
@Configuration
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class TestDatabaseAutoConfiguration {
private final Environment environment;
TestDatabaseAutoConfiguration(Environment environment) {
this.environment = environment;
}
@Bean
@ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "AUTO_CONFIGURED")
@ConditionalOnMissingBean
public DataSource dataSource() {
return new EmbeddedDataSourceFactory(this.environment).getEmbeddedDatabase();
}
@Bean
@ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "ANY", matchIfMissing = true)
public static EmbeddedDataSourceBeanFactoryPostProcessor embeddedDataSourceBeanFactoryPostProcessor() {
return new EmbeddedDataSourceBeanFactoryPostProcessor();
}
@Order(Ordered.LOWEST_PRECEDENCE)
private static class EmbeddedDataSourceBeanFactoryPostProcessor
implements BeanDefinitionRegistryPostProcessor {
private static final Log logger = LogFactory
.getLog(EmbeddedDataSourceBeanFactoryPostProcessor.class);
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, registry,
"Test Database Auto-configuration can only be "
+ "used with a ConfigurableListableBeanFactory");
process(registry, (ConfigurableListableBeanFactory) registry);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
}
private void process(BeanDefinitionRegistry registry,
ConfigurableListableBeanFactory beanFactory) {
BeanDefinitionHolder holder = getDataSourceBeanDefinition(beanFactory);
if (holder != null) {
String beanName = holder.getBeanName();
boolean primary = holder.getBeanDefinition().isPrimary();
logger.info("Replacing '" + beanName + "' DataSource bean with "
+ (primary ? "primary " : "") + "embedded version");
registry.registerBeanDefinition(beanName,
createEmbeddedBeanDefinition(primary));
}
}
private BeanDefinition createEmbeddedBeanDefinition(boolean primary) {
BeanDefinition beanDefinition = new RootBeanDefinition(
EmbeddedDataSourceFactoryBean.class);
beanDefinition.setPrimary(primary);
return beanDefinition;
}
private BeanDefinitionHolder getDataSourceBeanDefinition(
ConfigurableListableBeanFactory beanFactory) {
String[] beanNames = beanFactory.getBeanNamesForType(DataSource.class);
if (ObjectUtils.isEmpty(beanNames)) {
logger.warn("No DataSource beans found, "
+ "embedded version will not be used");
return null;
}
if (beanNames.length == 1) {
String beanName = beanNames[0];
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
return new BeanDefinitionHolder(beanDefinition, beanName);
}
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (beanDefinition.isPrimary()) {
return new BeanDefinitionHolder(beanDefinition, beanName);
}
}
logger.warn("No primary DataSource found, "
+ "embedded version will not be used");
return null;
}
}
private static class EmbeddedDataSourceFactoryBean
implements FactoryBean<DataSource>, EnvironmentAware, InitializingBean {
private EmbeddedDataSourceFactory factory;
private EmbeddedDatabase embeddedDatabase;
@Override
public void setEnvironment(Environment environment) {
this.factory = new EmbeddedDataSourceFactory(environment);
}
@Override
public void afterPropertiesSet() throws Exception {
this.embeddedDatabase = this.factory.getEmbeddedDatabase();
}
@Override
public DataSource getObject() throws Exception {
return this.embeddedDatabase;
}
@Override
public Class<?> getObjectType() {
return EmbeddedDatabase.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
private static class EmbeddedDataSourceFactory {
private final Environment environment;
EmbeddedDataSourceFactory(Environment environment) {
this.environment = environment;
}
public EmbeddedDatabase getEmbeddedDatabase() {
EmbeddedDatabaseConnection connection = this.environment.getProperty(
"spring.test.database.connection", EmbeddedDatabaseConnection.class,
EmbeddedDatabaseConnection.NONE);
if (EmbeddedDatabaseConnection.NONE.equals(connection)) {
connection = EmbeddedDatabaseConnection.get(getClass().getClassLoader());
}
Assert.state(connection != EmbeddedDatabaseConnection.NONE,
"Failed to replace DataSource with an embedded database for tests. If "
+ "you want an embedded database please put a supported one "
+ "on the classpath or tune the replace attribute of "
+ "@AutoconfigureTestDatabase.");
return new EmbeddedDatabaseBuilder().generateUniqueName(true)
.setType(connection.getType()).build();
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.test.autoconfigure.jooq;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical jOOQ tests. Most
* tests should consider using {@link JooqTest @JooqTest} rather than using this
* annotation directly.
*
* @author Michael Simons
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ImportAutoConfiguration
public @interface AutoConfigureJooq {
}

View File

@@ -0,0 +1,97 @@
/*
* 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.test.autoconfigure.jooq;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
import org.springframework.transaction.annotation.Transactional;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical jOOQ test. Can be used when a test focuses <strong>only</strong> on
* jOOQ-based components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to jOOQ tests.
* <p>
* By default, tests annotated with {@code @JooqTest} use the configured database. If you
* want to replace any explicit or usually auto-configured DataSource by an embedded
* in-memory database, the {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase}
* annotation can be used to override these settings.
*
* @author Michael Simons
* @author Stephane Nicoll
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(JooqTypeExcludeFilter.class)
@Transactional
@AutoConfigureJooq
@ImportAutoConfiguration
public @interface JooqTest {
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default no beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,73 @@
/*
* 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.test.autoconfigure.jooq;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link JooqTest @JooqTest}.
*
* @author Michael Simons
*/
class JooqTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private final JooqTest annotation;
JooqTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
JooqTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(final FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
default:
throw new IllegalStateException("Unsupported type " + type);
}
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Auto-configuration for jOOQ tests.
*/
package org.springframework.boot.test.autoconfigure.jooq;

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.json;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical JSON tests. Most
* tests should consider using {@link JsonTest @JsonTest} rather than using this
* annotation directly.
*
* @author Phillip Webb
* @since 1.4.0
* @see JsonTest
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureJson {
}

View File

@@ -0,0 +1,55 @@
/*
* 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.test.autoconfigure.json;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonbTester;
/**
* Annotation that can be applied to a test class to enable and configure
* auto-configuration of JSON testers.
*
* @author Phillip Webb
* @since 1.4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping("spring.test.jsontesters")
public @interface AutoConfigureJsonTesters {
/**
* If {@link BasicJsonTester}, {@link JacksonTester}, {@link JsonbTester} and
* {@link GsonTester} beans should be registered. Defaults to {@code true}
* @return if tester support is enabled
*/
boolean enabled() default true;
}

View File

@@ -0,0 +1,85 @@
/*
* 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.test.autoconfigure.json;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import com.fasterxml.jackson.databind.Module;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link JsonTest @JsonTest}.
*
* @author Phillip Webb
*/
class JsonExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private static final Set<Class<?>> DEFAULT_INCLUDES;
static {
Set<Class<?>> includes = new LinkedHashSet<>();
includes.add(Module.class);
includes.add(JsonComponent.class);
DEFAULT_INCLUDES = Collections.unmodifiableSet(includes);
}
private final JsonTest annotation;
JsonExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
JsonTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
}
throw new IllegalStateException("Unsupported type " + type);
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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.test.autoconfigure.json;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonbTester;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical JSON test. Can be used when a test focuses <strong>only</strong> on JSON
* serialization.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to JSON tests (i.e. {@code @JsonComponent}, Jackson
* {@code Module})
* <p>
* By default, tests annotated with {@code JsonTest} will also initialize
* {@link JacksonTester}, {@link JsonbTester} and {@link GsonTester} fields. More
* fine-grained control can be provided via the
* {@link AutoConfigureJsonTesters @AutoConfigureJsonTesters} annotation.
*
* @author Phillip Webb
* @see AutoConfigureJson
* @see AutoConfigureJsonTesters
* @see AutoConfigureCache
* @since 1.4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(JsonExcludeFilter.class)
@AutoConfigureCache
@AutoConfigureJson
@AutoConfigureJsonTesters
@ImportAutoConfiguration
public @interface JsonTest {
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default only
* {@code @JsonComponent} and {@code Module} beans are included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,192 @@
/*
* 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.test.autoconfigure.json;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import javax.json.bind.Jsonb;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration;
import org.springframework.boot.test.json.AbstractJsonMarshalTester;
import org.springframework.boot.test.json.BasicJsonTester;
import org.springframework.boot.test.json.GsonTester;
import org.springframework.boot.test.json.JacksonTester;
import org.springframework.boot.test.json.JsonbTester;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.ResolvableType;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ReflectionUtils;
/**
* Auto-configuration for Json testers.
*
* @author Phillip Webb
* @author Eddú Meléndez
* @see AutoConfigureJsonTesters
* @since 1.4.0
*/
@Configuration
@ConditionalOnClass(name = "org.assertj.core.api.Assert")
@ConditionalOnProperty("spring.test.jsontesters.enabled")
@AutoConfigureAfter({ JacksonAutoConfiguration.class, GsonAutoConfiguration.class,
JsonbAutoConfiguration.class })
public class JsonTestersAutoConfiguration {
@Bean
public static JsonMarshalTestersBeanPostProcessor jsonMarshalTestersBeanPostProcessor() {
return new JsonMarshalTestersBeanPostProcessor();
}
@Bean
@Scope("prototype")
public FactoryBean<BasicJsonTester> basicJsonTesterFactoryBean() {
return new JsonTesterFactoryBean<BasicJsonTester, Void>(BasicJsonTester.class,
null);
}
@ConditionalOnClass(ObjectMapper.class)
private static class JacksonJsonTestersConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnBean(ObjectMapper.class)
public FactoryBean<JacksonTester<?>> jacksonTesterFactoryBean(
ObjectMapper mapper) {
return new JsonTesterFactoryBean<>(JacksonTester.class, mapper);
}
}
@ConditionalOnClass(Gson.class)
private static class GsonJsonTestersConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnBean(Gson.class)
public FactoryBean<GsonTester<?>> gsonTesterFactoryBean(Gson gson) {
return new JsonTesterFactoryBean<>(GsonTester.class, gson);
}
}
@ConditionalOnClass(Jsonb.class)
private static class JsonbJsonTesterConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnBean(Jsonb.class)
public FactoryBean<JsonbTester<?>> jsonbTesterFactoryBean(Jsonb jsonb) {
return new JsonTesterFactoryBean<>(JsonbTester.class, jsonb);
}
}
/**
* {@link FactoryBean} used to create JSON Tester instances.
*/
private static class JsonTesterFactoryBean<T, M> implements FactoryBean<T> {
private final Class<?> objectType;
private final M marshaller;
JsonTesterFactoryBean(Class<?> objectType, M marshaller) {
this.objectType = objectType;
this.marshaller = marshaller;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
@SuppressWarnings("unchecked")
public T getObject() throws Exception {
if (this.marshaller == null) {
Constructor<?> constructor = this.objectType.getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
return (T) BeanUtils.instantiateClass(constructor);
}
Constructor<?>[] constructors = this.objectType.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
if (constructor.getParameterCount() == 1
&& constructor.getParameterTypes()[0]
.isInstance(this.marshaller)) {
ReflectionUtils.makeAccessible(constructor);
return (T) BeanUtils.instantiateClass(constructor, this.marshaller);
}
}
throw new IllegalStateException(
this.objectType + " does not have a usable constructor");
}
@Override
public Class<?> getObjectType() {
return this.objectType;
}
}
/**
* {@link BeanPostProcessor} used to initialize JSON testers.
*/
private static class JsonMarshalTestersBeanPostProcessor
extends InstantiationAwareBeanPostProcessorAdapter {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(),
(field) -> processField(bean, field));
return bean;
}
private void processField(Object bean, Field field) {
if (AbstractJsonMarshalTester.class.isAssignableFrom(field.getType())
|| BasicJsonTester.class.isAssignableFrom(field.getType())) {
ResolvableType type = ResolvableType.forField(field).getGeneric();
ReflectionUtils.makeAccessible(field);
Object tester = ReflectionUtils.getField(field, bean);
if (tester != null) {
ReflectionTestUtils.invokeMethod(tester, "initialize",
bean.getClass(), type);
}
}
}
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2016 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.
*/
/**
* Auto-configuration for JSON tests.
*/
package org.springframework.boot.test.autoconfigure.json;

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.orm.jpa;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical Data JPA tests.
* Most tests should consider using {@link DataJpaTest @DataJpaTest} rather than using
* this annotation directly.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.4.0
* @see DataJpaTest
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureDataJpa {
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.orm.jpa;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* Annotation that can be applied to a test class to enable auto-configuration of a
* {@link TestEntityManager}.
*
* @author Phillip Webb
* @see TestEntityManagerAutoConfiguration
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureTestEntityManager {
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.orm.jpa;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
import org.springframework.transaction.annotation.Transactional;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical JPA test. Can be used when a test focuses <strong>only</strong> on JPA
* components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to JPA tests.
* <p>
* By default, tests annotated with {@code @DataJpaTest} will use an embedded in-memory
* database (replacing any explicit or usually auto-configured DataSource). The
* {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase} annotation can be used to
* override these settings.
* <p>
* If you are looking to load your full application configuration, but use an embedded
* database, you should consider {@link SpringBootTest @SpringBootTest} combined with
* {@link AutoConfigureTestDatabase @AutoConfigureTestDatabase} rather than this
* annotation.
*
* @author Phillip Webb
* @see AutoConfigureDataJpa
* @see AutoConfigureTestDatabase
* @see AutoConfigureTestEntityManager
* @see AutoConfigureCache
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataJpaTypeExcludeFilter.class)
@Transactional
@AutoConfigureCache
@AutoConfigureDataJpa
@AutoConfigureTestDatabase
@AutoConfigureTestEntityManager
@ImportAutoConfiguration
public @interface DataJpaTest {
/**
* If SQL output should be logged.
* @return if SQL is logged
*/
@PropertyMapping("spring.jpa.show-sql")
boolean showSql() default true;
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default no beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,72 @@
/*
* 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.test.autoconfigure.orm.jpa;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
/**
* {@link TypeExcludeFilter} for {@link DataJpaTest @DataJpaTest}.
*
* @author Phillip Webb
*/
class DataJpaTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private final DataJpaTest annotation;
DataJpaTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
DataJpaTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
}
throw new IllegalStateException("Unsupported type " + type);
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return Collections.emptySet();
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return Collections.emptySet();
}
}

View File

@@ -0,0 +1,243 @@
/*
* 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.test.autoconfigure.orm.jpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnitUtil;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.util.Assert;
/**
* Alternative to {@link EntityManager} for use in JPA tests. Provides a subset of
* {@link EntityManager} methods that are useful for tests as well as helper methods for
* common testing tasks such as {@link #persistFlushFind(Object) persist/flush/find}.
*
* @author Phillip Webb
* @since 1.4.0
*/
public class TestEntityManager {
private final EntityManagerFactory entityManagerFactory;
/**
* Create a new {@link TestEntityManager} instance for the given
* {@link EntityManagerFactory}.
* @param entityManagerFactory the source entity manager factory
*/
public TestEntityManager(EntityManagerFactory entityManagerFactory) {
Assert.notNull(entityManagerFactory, "EntityManagerFactory must not be null");
this.entityManagerFactory = entityManagerFactory;
}
/**
* Make an instance managed and persistent then return it's ID. Delegates to
* {@link EntityManager#persist(Object)} then {@link #getId(Object)}.
* <p>
* Helpful when setting up test data in a test: <pre class="code">
* Object entityId = this.testEntityManager.persist(new MyEntity("Spring"));
* </pre>
* @param entity the source entity
* @return the ID of the newly persisted entity
*/
public Object persistAndGetId(Object entity) {
persist(entity);
return getId(entity);
}
/**
* Make an instance managed and persistent then return it's ID. Delegates to
* {@link EntityManager#persist(Object)} then {@link #getId(Object, Class)}.
* <p>
* Helpful when setting up test data in a test: <pre class="code">
* Long entityId = this.testEntityManager.persist(new MyEntity("Spring"), Long.class);
* </pre>
* @param <T> the ID type
* @param entity the source entity
* @param idType the ID type
* @return the ID of the newly persisted entity
*/
public <T> T persistAndGetId(Object entity, Class<T> idType) {
persist(entity);
return getId(entity, idType);
}
/**
* Make an instance managed and persistent. Delegates to
* {@link EntityManager#persist(Object)} then returns the original source entity.
* <p>
* Helpful when setting up test data in a test: <pre class="code">
* MyEntity entity = this.testEntityManager.persist(new MyEntity("Spring"));
* </pre>
* @param <E> the entity type
* @param entity the entity to persist
* @return the persisted entity
*/
public <E> E persist(E entity) {
getEntityManager().persist(entity);
return entity;
}
/**
* Make an instance managed and persistent, synchronize the persistence context to the
* underlying database and finally find the persisted entity by its ID. Delegates to
* {@link #persistAndFlush(Object)} then {@link #find(Class, Object)} with the
* {@link #getId(Object) entity ID}.
* <p>
* Helpful when ensuring that entity data is actually written and read from the
* underlying database correctly.
* @param <E> the entity type
* @param entity the entity to persist
* @return the entity found using the ID of the persisted entity
*/
@SuppressWarnings("unchecked")
public <E> E persistFlushFind(E entity) {
EntityManager entityManager = getEntityManager();
persistAndFlush(entity);
Object id = getId(entity);
entityManager.detach(entity);
return (E) entityManager.find(entity.getClass(), id);
}
/**
* Make an instance managed and persistent then synchronize the persistence context to
* the underlying database. Delegates to {@link EntityManager#persist(Object)} then
* {@link #flush()} and finally returns the original source entity.
* <p>
* Helpful when setting up test data in a test: <pre class="code">
* MyEntity entity = this.testEntityManager.persistAndFlush(new MyEntity("Spring"));
* </pre>
* @param <E> the entity type
* @param entity the entity to persist
* @return the persisted entity
*/
public <E> E persistAndFlush(E entity) {
persist(entity);
flush();
return entity;
}
/**
* Merge the state of the given entity into the current persistence context. Delegates
* to {@link EntityManager#merge(Object)}
* @param <E> the entity type
* @param entity the entity to merge
* @return the merged entity
*/
public <E> E merge(E entity) {
return getEntityManager().merge(entity);
}
/**
* Remove the entity instance. Delegates to {@link EntityManager#remove(Object)}
* @param entity the entity to remove
*/
public void remove(Object entity) {
getEntityManager().remove(entity);
}
/**
* Find by primary key. Delegates to {@link EntityManager#find(Class, Object)}.
* @param <E> the entity type
* @param entityClass the entity class
* @param primaryKey the entity primary key
* @return the found entity or {@code null} if the entity does not exist
* @see #getId(Object)
*/
public <E> E find(Class<E> entityClass, Object primaryKey) {
return getEntityManager().find(entityClass, primaryKey);
}
/**
* Synchronize the persistence context to the underlying database. Delegates to
* {@link EntityManager#flush()}.
*/
public void flush() {
getEntityManager().flush();
}
/**
* Refresh the state of the instance from the database, overwriting changes made to
* the entity, if any. Delegates to {@link EntityManager#refresh(Object)}.
* @param <E> the entity type
* @param entity the entity to refresh
* @return the refreshed entity
*/
public <E> E refresh(E entity) {
getEntityManager().refresh(entity);
return entity;
}
/**
* Clear the persistence context, causing all managed entities to become detached.
* Delegates to {@link EntityManager#clear()}
*/
public void clear() {
getEntityManager().clear();
}
/**
* Remove the given entity from the persistence context, causing a managed entity to
* become detached. Delegates to {@link EntityManager#detach(Object)}.
* @param entity the entity to detach.
*/
public void detach(Object entity) {
getEntityManager().detach(entity);
}
/**
* Return the ID of the given entity. Delegates to
* {@link PersistenceUnitUtil#getIdentifier(Object)}.
* @param entity the source entity
* @return the ID of the entity or {@code null}
* @see #getId(Object, Class)
*/
public Object getId(Object entity) {
return this.entityManagerFactory.getPersistenceUnitUtil().getIdentifier(entity);
}
/**
* Return the ID of the given entity cast to a specific type. Delegates to
* {@link PersistenceUnitUtil#getIdentifier(Object)}.
* @param <T> the ID type
* @param entity the source entity
* @param idType the expected ID type
* @return the ID of the entity or {@code null}
* @see #getId(Object)
*/
@SuppressWarnings("unchecked")
public <T> T getId(Object entity, Class<T> idType) {
Object id = getId(entity);
Assert.isInstanceOf(idType, id, "ID mismatch:");
return (T) id;
}
/**
* Return the underlying {@link EntityManager} that's actually used to perform all
* operations.
* @return the entity manager
*/
public final EntityManager getEntityManager() {
EntityManager manager = EntityManagerFactoryUtils
.getTransactionalEntityManager(this.entityManagerFactory);
Assert.state(manager != null, "No transactional EntityManager found");
return manager;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.orm.jpa;
import javax.persistence.EntityManagerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto-configuration for {@link TestEntityManager}.
*
* @author Phillip Webb
* @since 1.4.0
* @see AutoConfigureTestEntityManager
*/
@Configuration
@ConditionalOnClass({ EntityManagerFactory.class })
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class TestEntityManagerAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TestEntityManager testEntityManager(
EntityManagerFactory entityManagerFactory) {
return new TestEntityManager(entityManagerFactory);
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2016 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.
*/
/**
* Auto-configuration for Data JPA tests.
*/
package org.springframework.boot.test.autoconfigure.orm.jpa;

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2016 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.
*/
/**
* Test auto-configuration support.
*/
package org.springframework.boot.test.autoconfigure;

View File

@@ -0,0 +1,219 @@
/*
* 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.test.autoconfigure.properties;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* {@link EnumerablePropertySource} to adapt annotations marked with
* {@link PropertyMapping @PropertyMapping}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.4.0
*/
public class AnnotationsPropertySource extends EnumerablePropertySource<Class<?>> {
private static final Pattern CAMEL_CASE_PATTERN = Pattern.compile("([^A-Z-])([A-Z])");
private final Map<String, Object> properties;
public AnnotationsPropertySource(Class<?> source) {
this("Annotations", source);
}
public AnnotationsPropertySource(String name, Class<?> source) {
super(name, source);
this.properties = getProperties(source);
}
private Map<String, Object> getProperties(Class<?> source) {
Map<String, Object> properties = new LinkedHashMap<>();
collectProperties(source, source, properties, new HashSet<>());
return Collections.unmodifiableMap(properties);
}
private void collectProperties(Class<?> root, Class<?> source,
Map<String, Object> properties, Set<Class<?>> seen) {
if (source != null && seen.add(source)) {
for (Annotation annotation : getMergedAnnotations(root, source)) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
PropertyMapping typeMapping = annotation.annotationType()
.getAnnotation(PropertyMapping.class);
for (Method attribute : annotation.annotationType()
.getDeclaredMethods()) {
collectProperties(annotation, attribute, typeMapping, properties);
}
collectProperties(root, annotation.annotationType(), properties,
seen);
}
}
collectProperties(root, source.getSuperclass(), properties, seen);
}
}
private List<Annotation> getMergedAnnotations(Class<?> root, Class<?> source) {
List<Annotation> mergedAnnotations = new ArrayList<>();
Annotation[] annotations = AnnotationUtils.getAnnotations(source);
if (annotations != null) {
for (Annotation annotation : annotations) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
mergedAnnotations
.add(findMergedAnnotation(root, annotation.annotationType()));
}
}
}
return mergedAnnotations;
}
private Annotation findMergedAnnotation(Class<?> source,
Class<? extends Annotation> annotationType) {
if (source == null) {
return null;
}
Annotation mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(source,
annotationType);
return mergedAnnotation != null ? mergedAnnotation
: findMergedAnnotation(source.getSuperclass(), annotationType);
}
private void collectProperties(Annotation annotation, Method attribute,
PropertyMapping typeMapping, Map<String, Object> properties) {
PropertyMapping attributeMapping = AnnotationUtils.getAnnotation(attribute,
PropertyMapping.class);
SkipPropertyMapping skip = getMappingType(typeMapping, attributeMapping);
if (skip == SkipPropertyMapping.YES) {
return;
}
String name = getName(typeMapping, attributeMapping, attribute);
ReflectionUtils.makeAccessible(attribute);
Object value = ReflectionUtils.invokeMethod(attribute, annotation);
if (skip == SkipPropertyMapping.ON_DEFAULT_VALUE) {
Object defaultValue = AnnotationUtils.getDefaultValue(annotation,
attribute.getName());
if (ObjectUtils.nullSafeEquals(value, defaultValue)) {
return;
}
}
putProperties(name, value, properties);
}
private SkipPropertyMapping getMappingType(PropertyMapping typeMapping,
PropertyMapping attributeMapping) {
if (attributeMapping != null) {
return attributeMapping.skip();
}
if (typeMapping != null) {
return typeMapping.skip();
}
return SkipPropertyMapping.YES;
}
private String getName(PropertyMapping typeMapping, PropertyMapping attributeMapping,
Method attribute) {
String prefix = (typeMapping == null ? "" : typeMapping.value());
String name = (attributeMapping == null ? "" : attributeMapping.value());
if (!StringUtils.hasText(name)) {
name = toKebabCase(attribute.getName());
}
return dotAppend(prefix, name);
}
private String toKebabCase(String name) {
Matcher matcher = CAMEL_CASE_PATTERN.matcher(name);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(result,
matcher.group(1) + '-' + StringUtils.uncapitalize(matcher.group(2)));
}
matcher.appendTail(result);
return result.toString().toLowerCase();
}
private String dotAppend(String prefix, String postfix) {
if (StringUtils.hasText(prefix)) {
return (prefix.endsWith(".") ? prefix + postfix : prefix + "." + postfix);
}
return postfix;
}
private void putProperties(String name, Object value,
Map<String, Object> properties) {
if (ObjectUtils.isArray(value)) {
Object[] array = ObjectUtils.toObjectArray(value);
for (int i = 0; i < array.length; i++) {
properties.put(name + "[" + i + "]", array[i]);
}
}
else {
properties.put(name, value);
}
}
@Override
public boolean containsProperty(String name) {
return this.properties.containsKey(name);
}
@Override
public Object getProperty(String name) {
return this.properties.get(name);
}
@Override
public String[] getPropertyNames() {
return StringUtils.toStringArray(this.properties.keySet());
}
public boolean isEmpty() {
return this.properties.isEmpty();
}
@Override
public int hashCode() {
return this.properties.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return this.properties.equals(((AnnotationsPropertySource) obj).properties);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.properties;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.TestPropertySource;
/**
* Indicates that attributes from a test annotation should be mapped into a
* {@link PropertySource}. Can be used at the type level, or on individual attributes. For
* example, the following annotation declaration: <pre class="code">
* &#064;Retention(RUNTIME)
* &#064;PropertyMapping("my.example")
* public &#064;interface Example {
*
* String name();
*
* }
* </pre> When used on a test class as follows: <pre class="code">
* &#064;Example(name="Spring")
* public class MyTest {
* }
* </pre> will result in a {@literal my.example.name} property being added with the value
* {@literal "Spring"}.
* <p>
*
* @author Phillip Webb
* @since 1.4.0
* @see AnnotationsPropertySource
* @see TestPropertySource
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface PropertyMapping {
/**
* Defines the property mapping. When used at the type-level, this value will be used
* as a prefix for all mapped attributes. When used on an attribute, the value
* overrides the generated (kebab case) name.
* @return the property mapping
*/
String value() default "";
/**
* Determines if mapping should be skipped. When specified at the type-level indicates
* if skipping should occur by default or not. When used at the attribute-level,
* overrides the type-level default.
* @return if mapping should be skipped
*/
SkipPropertyMapping skip() default SkipPropertyMapping.NO;
}

View File

@@ -0,0 +1,133 @@
/*
* 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.test.autoconfigure.properties;
import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.ClassUtils;
/**
* {@link ContextCustomizer} to map annotation attributes to {@link Environment}
* properties.
*
* @author Phillip Webb
*/
class PropertyMappingContextCustomizer implements ContextCustomizer {
private final AnnotationsPropertySource propertySource;
PropertyMappingContextCustomizer(AnnotationsPropertySource propertySource) {
this.propertySource = propertySource;
}
@Override
public void customizeContext(ConfigurableApplicationContext context,
MergedContextConfiguration mergedContextConfiguration) {
if (!this.propertySource.isEmpty()) {
context.getEnvironment().getPropertySources().addFirst(this.propertySource);
}
context.getBeanFactory().registerSingleton(
PropertyMappingCheckBeanPostProcessor.class.getName(),
new PropertyMappingCheckBeanPostProcessor());
}
@Override
public int hashCode() {
return this.propertySource.hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj != null && getClass().equals(obj.getClass()) && this.propertySource
.equals(((PropertyMappingContextCustomizer) obj).propertySource));
}
/**
* {@link BeanPostProcessor} to check that {@link PropertyMapping} is only used on
* test classes.
*/
static class PropertyMappingCheckBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
Class<?> beanClass = bean.getClass();
Set<Class<?>> components = new LinkedHashSet<>();
Set<Class<?>> propertyMappings = new LinkedHashSet<>();
while (beanClass != null) {
Annotation[] annotations = AnnotationUtils.getAnnotations(beanClass);
if (annotations != null) {
for (Annotation annotation : annotations) {
if (isAnnotated(annotation, Component.class)) {
components.add(annotation.annotationType());
}
if (isAnnotated(annotation, PropertyMapping.class)) {
propertyMappings.add(annotation.annotationType());
}
}
}
beanClass = beanClass.getSuperclass();
}
if (!components.isEmpty() && !propertyMappings.isEmpty()) {
throw new IllegalStateException("The @PropertyMapping "
+ getAnnotationsDescription(propertyMappings)
+ " cannot be used in combination with the @Component "
+ getAnnotationsDescription(components));
}
return bean;
}
private boolean isAnnotated(Annotation element,
Class<? extends Annotation> annotationType) {
try {
return element.annotationType().equals(annotationType) || AnnotationUtils
.findAnnotation(element.annotationType(), annotationType) != null;
}
catch (Throwable ex) {
return false;
}
}
private String getAnnotationsDescription(Set<Class<?>> annotations) {
StringBuilder result = new StringBuilder();
for (Class<?> annotation : annotations) {
result.append(result.length() == 0 ? "" : ", ");
result.append("@" + ClassUtils.getShortName(annotation));
}
result.insert(0, annotations.size() == 1 ? "annotation " : "annotations ");
return result.toString();
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.properties;
import java.util.List;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
/**
* {@link ContextCustomizerFactory} to map annotation attributes to {@link Environment}
* properties.
*
* @author Phillip Webb
*/
class PropertyMappingContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configurationAttributes) {
AnnotationsPropertySource propertySource = new AnnotationsPropertySource(
testClass);
return new PropertyMappingContextCustomizer(propertySource);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.properties;
/**
* Enum used to control when {@link PropertyMapping} is skipped.
*
* @author Phillip Webb
* @since 1.4.0
*/
public enum SkipPropertyMapping {
/**
* Skip mapping the property.
*/
YES,
/**
* Skip mapping the property when the default attribute value is specified.
*/
ON_DEFAULT_VALUE,
/**
* Don't skip mapping the property.
*/
NO,
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2016 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.
*/
/**
* Support for mapping annotation attribute values in the Spring {@code Environment}.
*/
package org.springframework.boot.test.autoconfigure.properties;

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.restdocs;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AliasFor;
/**
* Annotation that can be applied to a test class to enable and configure
* auto-configuration of Spring REST Docs. Allows configuration of the output directory
* and the host, scheme, and port of generated URIs. When further configuration is
* required a {@link RestDocsMockMvcConfigurationCustomizer} bean can be used.
*
* @author Andy Wilkinson
* @since 1.4.0
* @see RestDocsAutoConfiguration
* @see RestDocsMockMvcConfigurationCustomizer
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@Import(RestDocumentationContextProviderRegistrar.class)
@PropertyMapping("spring.test.restdocs")
public @interface AutoConfigureRestDocs {
/**
* The output directory to which generated snippets will be written. A alias for
* {@link #outputDir}.
* @return the output directory
*/
@AliasFor("outputDir")
String value() default "";
/**
* The output directory to which generated snippets will be written. A alias for
* {@link #value}.
* @return the output directory
*/
@AliasFor("value")
String outputDir() default "";
/**
* The scheme (typically {@code http} or {@code https}) to be used in documented URIs.
* Defaults to {@code http}.
* @return the scheme
*/
String uriScheme() default "http";
/**
* The host to be used in documented URIs. Defaults to {@code localhost}.
* @return the host
*/
String uriHost() default "localhost";
/**
* The port to be used in documented URIs. Defaults to {@code 8080}.
* @return the port
*/
int uriPort() default 8080;
}

View File

@@ -0,0 +1,111 @@
/*
* 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.test.autoconfigure.restdocs;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentation;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring REST Docs.
*
* @author Andy Wilkinson
* @author Eddú Meléndez
* @since 1.4.0
*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnWebApplication
public class RestDocsAutoConfiguration {
@Configuration
@ConditionalOnClass(MockMvcRestDocumentation.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
static class RestDocsMockMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean(MockMvcRestDocumentationConfigurer.class)
public MockMvcRestDocumentationConfigurer restDocsMockMvcConfigurer(
ObjectProvider<RestDocsMockMvcConfigurationCustomizer> configurationCustomizerProvider,
RestDocumentationContextProvider contextProvider) {
MockMvcRestDocumentationConfigurer configurer = MockMvcRestDocumentation
.documentationConfiguration(contextProvider);
RestDocsMockMvcConfigurationCustomizer configurationCustomizer = configurationCustomizerProvider
.getIfAvailable();
if (configurationCustomizer != null) {
configurationCustomizer.customize(configurer);
}
return configurer;
}
@Bean
@ConfigurationProperties(prefix = "spring.test.restdocs")
public RestDocsMockMvcBuilderCustomizer restDocumentationConfigurer(
MockMvcRestDocumentationConfigurer configurer,
ObjectProvider<RestDocumentationResultHandler> resultHandler) {
return new RestDocsMockMvcBuilderCustomizer(configurer,
resultHandler.getIfAvailable());
}
}
@Configuration
@ConditionalOnClass({ RequestSpecification.class,
RestAssuredRestDocumentation.class })
static class RestDocsRestAssuredAutoConfiguration {
@Bean
@ConditionalOnMissingBean(RequestSpecification.class)
public RequestSpecification restDocsRestAssuredConfigurer(
ObjectProvider<RestDocsRestAssuredConfigurationCustomizer> configurationCustomizerProvider,
RestDocumentationContextProvider contextProvider) {
RestAssuredRestDocumentationConfigurer configurer = RestAssuredRestDocumentation
.documentationConfiguration(contextProvider);
RestDocsRestAssuredConfigurationCustomizer configurationCustomizer = configurationCustomizerProvider
.getIfAvailable();
if (configurationCustomizer != null) {
configurationCustomizer.customize(configurer);
}
return new RequestSpecBuilder().addFilter(configurer).build();
}
@Bean
@ConfigurationProperties(prefix = "spring.test.restdocs")
public RestDocsRestAssuredBuilderCustomizer restAssuredBuilderCustomizer(
RequestSpecification configurer) {
return new RestDocsRestAssuredBuilderCustomizer(configurer);
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.restdocs;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.test.autoconfigure.web.servlet.MockMvcBuilderCustomizer;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
import org.springframework.util.StringUtils;
/**
* A {@link MockMvcBuilderCustomizer} that configures Spring REST Docs.
*
* @author Andy Wilkinson
*/
class RestDocsMockMvcBuilderCustomizer
implements InitializingBean, MockMvcBuilderCustomizer {
private final MockMvcRestDocumentationConfigurer delegate;
private final RestDocumentationResultHandler resultHandler;
private String uriScheme;
private String uriHost;
private Integer uriPort;
RestDocsMockMvcBuilderCustomizer(MockMvcRestDocumentationConfigurer delegate,
RestDocumentationResultHandler resultHandler) {
this.delegate = delegate;
this.resultHandler = resultHandler;
}
public String getUriScheme() {
return this.uriScheme;
}
public void setUriScheme(String uriScheme) {
this.uriScheme = uriScheme;
}
public String getUriHost() {
return this.uriHost;
}
public void setUriHost(String uriHost) {
this.uriHost = uriHost;
}
public Integer getUriPort() {
return this.uriPort;
}
public void setUriPort(Integer uriPort) {
this.uriPort = uriPort;
}
@Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.hasText(this.uriScheme)) {
this.delegate.uris().withScheme(this.uriScheme);
}
if (StringUtils.hasText(this.uriHost)) {
this.delegate.uris().withHost(this.uriHost);
}
if (this.uriPort != null) {
this.delegate.uris().withPort(this.uriPort);
}
}
@Override
public void customize(ConfigurableMockMvcBuilder<?> builder) {
builder.apply(this.delegate);
if (this.resultHandler != null) {
builder.alwaysDo(this.resultHandler);
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.test.autoconfigure.restdocs;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
/**
* A customizer for {@link MockMvcRestDocumentationConfigurer}. If a
* {@code RestDocsMockMvcConfigurationCustomizer} bean is found in the application context
* it will be {@link #customize called} to customize the
* {@code MockMvcRestDocumentationConfigurer} before it is applied. Intended for use only
* when the attributes on {@link AutoConfigureRestDocs} do not provide sufficient
* customization.
*
* @author Andy Wilkinson
* @since 1.4.0
*/
@FunctionalInterface
public interface RestDocsMockMvcConfigurationCustomizer {
/**
* Customize the given {@code configurer}.
* @param configurer the configurer
*/
void customize(MockMvcRestDocumentationConfigurer configurer);
}

View File

@@ -0,0 +1,77 @@
/*
* 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.test.autoconfigure.restdocs;
import io.restassured.specification.RequestSpecification;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
/**
* A customizer that configures Spring REST Docs with REST Assured.
*
* @author Eddú Meléndez
*/
class RestDocsRestAssuredBuilderCustomizer implements InitializingBean {
private final RequestSpecification delegate;
private String uriScheme;
private String uriHost;
private Integer uriPort;
RestDocsRestAssuredBuilderCustomizer(RequestSpecification delegate) {
this.delegate = delegate;
}
public String getUriScheme() {
return this.uriScheme;
}
public void setUriScheme(String uriScheme) {
this.uriScheme = uriScheme;
}
public String getUriHost() {
return this.uriHost;
}
public void setUriHost(String uriHost) {
this.uriHost = uriHost;
}
public Integer getUriPort() {
return this.uriPort;
}
public void setUriPort(Integer uriPort) {
this.uriPort = uriPort;
}
@Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.hasText(this.uriScheme) && StringUtils.hasText(this.uriHost)) {
this.delegate.baseUri(this.uriScheme + "://" + this.uriHost);
}
if (this.uriPort != null) {
this.delegate.port(this.uriPort);
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.test.autoconfigure.restdocs;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
/**
* A customizer for {@link RestAssuredRestDocumentationConfigurer}. If a
* {@code RestDocsRestAssuredConfigurationCustomizer} bean is found in the application
* context it will be {@link #customize called} to customize the
* {@code RestAssuredRestDocumentationConfigurer} before it is applied. Intended for use
* only when the attributes on {@link AutoConfigureRestDocs} do not provide sufficient
* customization.
*
* @author Eddú Meléndez
* @since 2.0.0
*/
@FunctionalInterface
public interface RestDocsRestAssuredConfigurationCustomizer {
/**
* Customize the given {@code configurer}.
* @param configurer the configurer
*/
void customize(RestAssuredRestDocumentationConfigurer configurer);
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.restdocs;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.restdocs.ManualRestDocumentation;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.util.ClassUtils;
/**
* A {@link TestExecutionListener} for Spring REST Docs that removes the need for a
* <code>@Rule</code> when using JUnit or manual before and after test calls when using
* TestNG.
*
* @author Andy Wilkinson
* @since 1.4.0
*/
public class RestDocsTestExecutionListener extends AbstractTestExecutionListener {
private static final String REST_DOCS_CLASS = "org.springframework.restdocs.ManualRestDocumentation";
@Override
public void beforeTestMethod(TestContext testContext) throws Exception {
if (restDocsIsPresent()) {
new DocumentationHandler().beforeTestMethod(testContext);
}
}
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
if (restDocsIsPresent()) {
new DocumentationHandler().afterTestMethod(testContext);
}
}
private boolean restDocsIsPresent() {
return ClassUtils.isPresent(REST_DOCS_CLASS, getClass().getClassLoader());
}
private static class DocumentationHandler {
private void beforeTestMethod(TestContext testContext) throws Exception {
ManualRestDocumentation restDocumentation = findManualRestDocumentation(
testContext);
if (restDocumentation != null) {
restDocumentation.beforeTest(testContext.getTestClass(),
testContext.getTestMethod().getName());
}
}
private void afterTestMethod(TestContext testContext) {
ManualRestDocumentation restDocumentation = findManualRestDocumentation(
testContext);
if (restDocumentation != null) {
restDocumentation.afterTest();
}
}
private ManualRestDocumentation findManualRestDocumentation(
TestContext testContext) {
try {
return testContext.getApplicationContext()
.getBean(ManualRestDocumentation.class);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.test.autoconfigure.restdocs;
import java.util.Map;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.restdocs.ManualRestDocumentation;
import org.springframework.util.StringUtils;
/**
* {@link ImportBeanDefinitionRegistrar} used by {@link AutoConfigureRestDocs}.
*
* @author Andy Wilkinson
* @see AutoConfigureRestDocs
*/
class RestDocumentationContextProviderRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
Map<String, Object> annotationAttributes = importingClassMetadata
.getAnnotationAttributes(AutoConfigureRestDocs.class.getName());
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(ManualRestDocumentation.class);
String outputDir = (String) annotationAttributes.get("outputDir");
if (StringUtils.hasText(outputDir)) {
definitionBuilder.addConstructorArgValue(outputDir);
}
registry.registerBeanDefinition(ManualRestDocumentation.class.getName(),
definitionBuilder.getBeanDefinition());
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.client;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.boot.test.web.client.MockServerRestTemplateCustomizer;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.test.web.client.MockRestServiceServer;
/**
* Annotation that can be applied to a test class to enable and configure
* auto-configuration of a single {@link MockRestServiceServer}. Only useful when a single
* call is made to {@link RestTemplateBuilder}. If multiple
* {@link org.springframework.web.client.RestTemplate RestTemplates} are in use, inject
* {@link MockServerRestTemplateCustomizer} and use
* {@link MockServerRestTemplateCustomizer#getServer(org.springframework.web.client.RestTemplate)
* getServer(RestTemplate)} or bind a {@link MockRestServiceServer} directly.
*
* @author Phillip Webb
* @since 1.4.0
* @see MockServerRestTemplateCustomizer
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping("spring.test.webclient.mockrestserviceserver")
public @interface AutoConfigureMockRestServiceServer {
/**
* If {@link MockServerRestTemplateCustomizer} should be enabled and
* {@link MockRestServiceServer} beans should be registered. Defaults to {@code true}
* @return if mock support is enabled
*/
boolean enabled() default true;
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.client;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.web.client.RestTemplate;
/**
* Annotation that can be applied to a test class to enable and configure
* auto-configuration of web clients.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping("spring.test.webclient")
public @interface AutoConfigureWebClient {
/**
* If a {@link RestTemplate} bean should be registered. Defaults to {@code false} with
* the assumption that the {@link RestTemplateBuilder} will be used.
* @return if a {@link RestTemplate} bean should be added.
*/
boolean registerRestTemplate() default false;
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.client;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Map;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.test.web.client.MockServerRestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.test.web.client.ExpectedCount;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.client.RequestExpectationManager;
import org.springframework.test.web.client.RequestMatcher;
import org.springframework.test.web.client.ResponseActions;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
/**
* Auto-configuration for {@link MockRestServiceServer} support.
*
* @author Phillip Webb
* @since 1.4.0
* @see AutoConfigureMockRestServiceServer
*/
@Configuration
@ConditionalOnProperty(prefix = "spring.test.webclient.mockrestserviceserver", name = "enabled")
public class MockRestServiceServerAutoConfiguration {
@Bean
public MockServerRestTemplateCustomizer mockServerRestTemplateCustomizer() {
return new MockServerRestTemplateCustomizer();
}
@Bean
public MockRestServiceServer mockRestServiceServer(
MockServerRestTemplateCustomizer customizer) {
try {
return createDeferredMockRestServiceServer(customizer);
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
private MockRestServiceServer createDeferredMockRestServiceServer(
MockServerRestTemplateCustomizer customizer) throws Exception {
Constructor<MockRestServiceServer> constructor = MockRestServiceServer.class
.getDeclaredConstructor(RequestExpectationManager.class);
constructor.setAccessible(true);
return constructor.newInstance(new DeferredRequestExpectationManager(customizer));
}
/**
* {@link RequestExpectationManager} with the injected {@link MockRestServiceServer}
* so that the bean can be created before the
* {@link MockServerRestTemplateCustomizer#customize(RestTemplate)
* MockServerRestTemplateCustomizer} has been called.
*/
private static class DeferredRequestExpectationManager
implements RequestExpectationManager {
private MockServerRestTemplateCustomizer customizer;
DeferredRequestExpectationManager(MockServerRestTemplateCustomizer customizer) {
this.customizer = customizer;
}
@Override
public ResponseActions expectRequest(ExpectedCount count,
RequestMatcher requestMatcher) {
return getDelegate().expectRequest(count, requestMatcher);
}
@Override
public ClientHttpResponse validateRequest(ClientHttpRequest request)
throws IOException {
return getDelegate().validateRequest(request);
}
@Override
public void verify() {
getDelegate().verify();
}
@Override
public void reset() {
Map<RestTemplate, RequestExpectationManager> expectationManagers = this.customizer
.getExpectationManagers();
if (expectationManagers.size() == 1) {
getDelegate().reset();
}
}
private RequestExpectationManager getDelegate() {
Map<RestTemplate, RequestExpectationManager> expectationManagers = this.customizer
.getExpectationManagers();
Assert.state(!expectationManagers.isEmpty(),
"Unable to use auto-configured MockRestServiceServer since "
+ "MockServerRestTemplateCustomizer has not been bound to "
+ "a RestTemplate");
Assert.state(expectationManagers.size() == 1,
"Unable to use auto-configured MockRestServiceServer since "
+ "MockServerRestTemplateCustomizer has been bound to "
+ "more than one RestTemplate");
return expectationManagers.values().iterator().next();
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.client;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.web.client.MockRestServiceServer;
/**
* {@link TestExecutionListener} to reset {@link MockRestServiceServer} beans.
*
* @author Phillip Webb
*/
class MockRestServiceServerResetTestExecutionListener
extends AbstractTestExecutionListener {
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
ApplicationContext applicationContext = testContext.getApplicationContext();
String[] names = applicationContext
.getBeanNamesForType(MockRestServiceServer.class, false, false);
for (String name : names) {
applicationContext.getBean(name, MockRestServiceServer.class).reset();
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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.test.autoconfigure.web.client;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.ClassUtils;
/**
* {@link TypeExcludeFilter} for {@link RestClientTest @RestClientTest}.
*
* @author Stephane Nicoll
*/
class RestClientExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private static final String DATABIND_MODULE_CLASS_NAME = "com.fasterxml.jackson.databind.Module";
private static final Set<Class<?>> DEFAULT_INCLUDES;
static {
Set<Class<?>> includes = new LinkedHashSet<>();
if (ClassUtils.isPresent(DATABIND_MODULE_CLASS_NAME,
RestClientExcludeFilter.class.getClassLoader())) {
try {
includes.add(Class.forName(DATABIND_MODULE_CLASS_NAME, true,
RestClientExcludeFilter.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Failed to load " + DATABIND_MODULE_CLASS_NAME, ex);
}
includes.add(JsonComponent.class);
}
DEFAULT_INCLUDES = Collections.unmodifiableSet(includes);
}
private final RestClientTest annotation;
RestClientExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
RestClientTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
}
throw new IllegalStateException("Unsupported type " + type);
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return new LinkedHashSet<>(Arrays.asList(this.annotation.components()));
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.client;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical Spring rest client test. Can be used when a test focuses
* <strong>only</strong> on beans that use {@link RestTemplateBuilder}.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to rest client tests (i.e. Jackson or GSON auto-configuration
* and {@code @JsonComponent} beans, but not regular {@link Component @Component} beans).
* <p>
* By default, tests annotated with {@code RestClientTest} will also auto-configure a
* {@link MockRestServiceServer}. For more fine-grained control the
* {@link AutoConfigureMockRestServiceServer @AutoConfigureMockRestServiceServer}
* annotation can be used.
* <p>
* If you are testing a bean that doesn't use {@link RestTemplateBuilder} but instead
* injects a {@link RestTemplate} directly, you can add
* {@code @AutoConfigureWebClient(registerRestTemplate=true)}.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 1.4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(RestClientExcludeFilter.class)
@AutoConfigureCache
@AutoConfigureWebClient
@AutoConfigureMockRestServiceServer
@ImportAutoConfiguration
public @interface RestClientTest {
/**
* Specifies the components to test. This is an alias of {@link #components()} which
* can be used for brevity if no other attributes are defined. See
* {@link #components()} for details.
* @see #components()
* @return the components to test
*/
@AliasFor("components")
Class<?>[] value() default {};
/**
* Specifies the components to test. May be left blank if components will be manually
* imported or created directly.
* @see #value()
* @return the components to test
*/
@AliasFor("value")
Class<?>[] components() default {};
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default only
* {@code @JsonComponent} and {@code Module} beans are included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
ComponentScan.Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
ComponentScan.Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,45 @@
/*
* 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.test.autoconfigure.web.client;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* Auto-configuration for a web-client {@link RestTemplate}. Used when
* {@link AutoConfigureWebClient#registerRestTemplate()} is {@code true}.
*
* @author Phillip Webb
* @since 1.4.0
* @see AutoConfigureMockRestServiceServer
*/
@Configuration
@ConditionalOnProperty(prefix = "spring.test.webclient", name = "register-rest-template")
@AutoConfigureAfter(RestTemplateAutoConfiguration.class)
public class WebClientRestTemplateAutoConfiguration {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2016 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.
*/
/**
* Auto-configuration for web clients.
*/
package org.springframework.boot.test.autoconfigure.web.client;

View File

@@ -0,0 +1,44 @@
/*
* 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.test.autoconfigure.web.reactive;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical Spring WebFlux
* tests. Most tests should consider using {@link WebFluxTest @WebFluxTest} rather than
* using this annotation directly.
*
* @author Stephane Nicoll
* @since 2.0.0
* @see WebFluxTest
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureWebFlux {
}

View File

@@ -0,0 +1,43 @@
/*
* 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.test.autoconfigure.web.reactive;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* Annotation that can be applied to a test class to enable a {@link WebTestClient}.
*
* @author Stephane Nicoll
* @since 2.0.0
* @see WebTestClientAutoConfiguration
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureWebTestClient {
}

View File

@@ -0,0 +1,132 @@
/*
* 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.test.autoconfigure.web.reactive;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical Spring WebFlux test. Can be used when a test focuses
* <strong>only</strong> on Spring WebFlux components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to WebFlux tests (i.e. {@code @Controller},
* {@code @ControllerAdvice}, {@code @JsonComponent} and {@code WebFluxConfigurer} beans
* but not {@code @Component}, {@code @Service} or {@code @Repository} beans).
* <p>
* By default, tests annotated with {@code @WebFluxTest} will also auto-configure a
* {@link WebTestClient}. For more fine-grained control of WebTestClient the
* {@link AutoConfigureWebTestClient @AutoConfigureWebTestClient} annotation can be used.
* <p>
* Typically {@code @WebFluxTest} is used in combination with {@link MockBean @MockBean}
* or {@link Import @Import} to create any collaborators required by your
* {@code @Controller} beans.
* <p>
* If you are looking to load your full application configuration and use WebTestClient,
* you should consider {@link SpringBootTest @SpringBootTest} combined with
* {@link AutoConfigureWebTestClient @AutoConfigureWebTestClient} rather than this
* annotation.
*
* @author Stephane Nicoll
* @since 2.0.0
* @see AutoConfigureWebFlux
* @see AutoConfigureWebTestClient
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(WebFluxTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(WebFluxTypeExcludeFilter.class)
@AutoConfigureCache
@AutoConfigureWebFlux
@AutoConfigureWebTestClient
@ImportAutoConfiguration
public @interface WebFluxTest {
/**
* Specifies the controllers to test. This is an alias of {@link #controllers()} which
* can be used for brevity if no other attributes are defined. See
* {@link #controllers()} for details.
* @see #controllers()
* @return the controllers to test
*/
@AliasFor("controllers")
Class<?>[] value() default {};
/**
* Specifies the controllers to test. May be left blank if all {@code @Controller}
* beans should be added to the application context.
* @see #value()
* @return the controllers to test
*/
@AliasFor("value")
Class<?>[] controllers() default {};
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default only
* {@code @Controller} (when no explicit {@link #controllers() controllers} are
* defined), {@code @ControllerAdvice} and {@code WebFluxConfigurer} beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
ComponentScan.Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
ComponentScan.Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,38 @@
/*
* 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.test.autoconfigure.web.reactive;
import org.springframework.boot.test.context.ReactiveWebMergedContextConfiguration;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.TestContextBootstrapper;
/**
* {@link TestContextBootstrapper} for {@link WebFluxTest @WebFluxTest} support.
*
* @author Stephane Nicoll
*/
class WebFluxTestContextBootstrapper extends SpringBootTestContextBootstrapper {
@Override
protected MergedContextConfiguration processMergedContextConfiguration(
MergedContextConfiguration mergedConfig) {
return new ReactiveWebMergedContextConfiguration(
super.processMergedContextConfiguration(mergedConfig));
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.test.autoconfigure.web.reactive;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.reactive.config.WebFluxConfigurer;
/**
* {@link TypeExcludeFilter} for {@link WebFluxTest @WebFluxTest}.
*
* @author Stephane Nicoll
*/
class WebFluxTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private static final Set<Class<?>> DEFAULT_INCLUDES;
static {
Set<Class<?>> includes = new LinkedHashSet<>();
includes.add(ControllerAdvice.class);
includes.add(JsonComponent.class);
includes.add(WebFluxConfigurer.class);
DEFAULT_INCLUDES = Collections.unmodifiableSet(includes);
}
private static final Set<Class<?>> DEFAULT_INCLUDES_AND_CONTROLLER;
static {
Set<Class<?>> includes = new LinkedHashSet<>(DEFAULT_INCLUDES);
includes.add(Controller.class);
DEFAULT_INCLUDES_AND_CONTROLLER = Collections.unmodifiableSet(includes);
}
private final WebFluxTest annotation;
WebFluxTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
WebFluxTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected ComponentScan.Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
}
throw new IllegalStateException("Unsupported type " + type);
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
if (ObjectUtils.isEmpty(this.annotation.controllers())) {
return DEFAULT_INCLUDES_AND_CONTROLLER;
}
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return new LinkedHashSet<>(Arrays.asList(this.annotation.controllers()));
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.test.autoconfigure.web.reactive;
import java.util.Collection;
import java.util.function.Consumer;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.CollectionUtils;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
/**
* Auto-configuration for {@link WebTestClient}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass({ WebClient.class, WebTestClient.class })
@AutoConfigureAfter(CodecsAutoConfiguration.class)
public class WebTestClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public WebTestClient webTestClient(ApplicationContext applicationContext) {
WebTestClient.Builder builder = WebTestClient
.bindToApplicationContext(applicationContext).configureClient();
customizeWebTestClientCodecs(builder, applicationContext);
return builder.build();
}
private void customizeWebTestClientCodecs(WebTestClient.Builder builder,
ApplicationContext applicationContext) {
Collection<CodecCustomizer> customizers = applicationContext
.getBeansOfType(CodecCustomizer.class).values();
if (!CollectionUtils.isEmpty(customizers)) {
builder.exchangeStrategies(ExchangeStrategies.builder()
.codecs(applyCustomizers(customizers)).build());
}
}
private Consumer<ClientCodecConfigurer> applyCustomizers(
Collection<CodecCustomizer> customizers) {
return (codecs) -> customizers
.forEach((customizer) -> customizer.customize(codecs));
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.gargoylesoftware.htmlunit.WebClient;
import org.openqa.selenium.WebDriver;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
import org.springframework.boot.test.autoconfigure.properties.SkipPropertyMapping;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
/**
* Annotation that can be applied to a test class to enable and configure
* auto-configuration of {@link MockMvc}.
*
* @author Phillip Webb
* @since 1.4.0
* @see MockMvcAutoConfiguration
* @see SpringBootMockMvcBuilderCustomizer
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping("spring.test.mockmvc")
public @interface AutoConfigureMockMvc {
/**
* If filters from the application context should be registered with MockMVC. Defaults
* to {@code true}.
* @return if filters should be added
*/
boolean addFilters() default true;
/**
* How {@link MvcResult} information should be printed after each MockMVC invocation.
* @return how information is printed
*/
@PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE)
MockMvcPrint print() default MockMvcPrint.DEFAULT;
/**
* If {@link MvcResult} information should be printed only if the test fails.
* @return {@code true} if printing only occurs on failure
*/
boolean printOnlyOnFailure() default true;
/**
* If a {@link WebClient} should be auto-configured when HtmlUnit is on the classpath.
* Defaults to {@code true}.
* @return if a {@link WebClient} is auto-configured
*/
@PropertyMapping("webclient.enabled")
boolean webClientEnabled() default true;
/**
* If a {@link WebDriver} should be auto-configured when Selenium is on the classpath.
* Defaults to {@code true}.
* @return if a {@link WebDriver} is auto-configured
*/
@PropertyMapping("webdriver.enabled")
boolean webDriverEnabled() default true;
/**
* If Spring Security's {@link MockMvc} support should be auto-configured when it is
* on the classpath. Defaults to {@code true}.
* @return if Spring Security's MockMvc support is auto-configured
*/
boolean secure() default true;
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
/**
* {@link ImportAutoConfiguration Auto-configuration imports} for typical Spring MVC
* tests. Most tests should consider using {@link WebMvcTest @WebMvcTest} rather than
* using this annotation directly.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.4.0
* @see WebMvcTest
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureWebMvc {
}

View File

@@ -0,0 +1,110 @@
/*
* 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.test.autoconfigure.web.servlet;
import java.util.List;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.web.servlet.DispatcherServletCustomizer;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Auto-configuration for {@link MockMvc}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
* @see AutoConfigureWebMvc
* @since 1.4.0
*/
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
public class MockMvcAutoConfiguration {
private final WebApplicationContext context;
private final WebMvcProperties webMvcProperties;
MockMvcAutoConfiguration(WebApplicationContext context,
WebMvcProperties webMvcProperties) {
this.context = context;
this.webMvcProperties = webMvcProperties;
}
@Bean
@ConditionalOnMissingBean(MockMvcBuilder.class)
public DefaultMockMvcBuilder mockMvcBuilder(
List<MockMvcBuilderCustomizer> customizers) {
DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.context);
builder.addDispatcherServletCustomizer(
new MockMvcDispatcherServletCustomizer(this.webMvcProperties));
for (MockMvcBuilderCustomizer customizer : customizers) {
customizer.customize(builder);
}
return builder;
}
@Bean
@ConfigurationProperties(prefix = "spring.test.mockmvc")
public SpringBootMockMvcBuilderCustomizer springBootMockMvcBuilderCustomizer() {
return new SpringBootMockMvcBuilderCustomizer(this.context);
}
@Bean
@ConditionalOnMissingBean
public MockMvc mockMvc(MockMvcBuilder builder) {
return builder.build();
}
private static class MockMvcDispatcherServletCustomizer
implements DispatcherServletCustomizer {
private final WebMvcProperties webMvcProperties;
MockMvcDispatcherServletCustomizer(WebMvcProperties webMvcProperties) {
this.webMvcProperties = webMvcProperties;
}
@Override
public void customize(DispatcherServlet dispatcherServlet) {
dispatcherServlet.setDispatchOptionsRequest(
this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(
this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(
this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.test.autoconfigure.web.servlet;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
/**
* A customizer for a {@link ConfigurableMockMvcBuilder}. Any
* {@code MockMvcBuilderCustomizer} beans found in the application context will be
* {@link #customize called} to customize the auto-configured {@link MockMvcBuilder}.
*
* @author Andy Wilkinson
* @since 1.4.0
* @see MockMvcAutoConfiguration
*/
@FunctionalInterface
public interface MockMvcBuilderCustomizer {
/**
* Customize the given {@code builder}.
* @param builder the builder
*/
void customize(ConfigurableMockMvcBuilder<?> builder);
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
/**
* MVC print options specified from {@link AutoConfigureMockMvc}.
*
* @author Phillip Webb
* @since 1.4.0
*/
public enum MockMvcPrint {
/**
* Use the default print setting ({@code MockMvcPrint.SYSTEM_OUT} unless explicitly
* overridden).
*/
DEFAULT,
/**
* Log MVC interactions at the {@code DEBUG} level.
*/
LOG_DEBUG,
/**
* Print MVC interactions to {@code System.out}.
*/
SYSTEM_OUT,
/**
* Print MVC interactions to {@code System.err}.
*/
SYSTEM_ERR,
/**
* Do not print MVC interactions.
*/
NONE
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import org.springframework.boot.test.autoconfigure.web.servlet.SpringBootMockMvcBuilderCustomizer.DeferredLinesWriter;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
/**
* {@link TestExecutionListener} used to print MVC lines only on failure.
*
* @author Phillip Webb
*/
class MockMvcPrintOnlyOnFailureTestExecutionListener
extends AbstractTestExecutionListener {
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
if (testContext.getTestException() != null) {
DeferredLinesWriter writer = DeferredLinesWriter
.get(testContext.getApplicationContext());
if (writer != null) {
writer.writeDeferredResult();
}
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.test.autoconfigure.web.servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Auto-configuration for Spring Security's testing support.
*
* @author Andy Wilkinson
* @since 1.4.0
*/
@Configuration
@ConditionalOnProperty(prefix = "spring.test.mockmvc", name = "secure", havingValue = "true", matchIfMissing = true)
@Import({ SecurityAutoConfiguration.class, MockMvcSecurityConfiguration.class })
public class MockMvcSecurityAutoConfiguration {
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.test.context.TestSecurityContextHolder;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter;
import org.springframework.web.context.WebApplicationContext;
/**
* Configuration for Spring Security's MockMvc integration.
*
* @author Andy Wilkinson
*/
@Configuration
@ConditionalOnClass(SecurityMockMvcRequestPostProcessors.class)
class MockMvcSecurityConfiguration {
private static final String DEFAULT_SECURITY_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
@ConditionalOnBean(name = DEFAULT_SECURITY_FILTER_NAME)
public SecurityMockMvcBuilderCustomizer securityMockMvcBuilderCustomizer() {
return new SecurityMockMvcBuilderCustomizer();
}
/**
* {@link MockMvcBuilderCustomizer} that ensures that requests run with the user in
* the {@link TestSecurityContextHolder}.
*
* @see SecurityMockMvcRequestPostProcessors#testSecurityContext
*/
class SecurityMockMvcBuilderCustomizer implements MockMvcBuilderCustomizer {
@Override
public void customize(ConfigurableMockMvcBuilder<?> builder) {
builder.apply(new MockMvcConfigurerAdapter() {
@Override
public RequestPostProcessor beforeMockMvcCreated(
ConfigurableMockMvcBuilder<?> builder,
WebApplicationContext context) {
return SecurityMockMvcRequestPostProcessors.testSecurityContext();
}
});
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import com.gargoylesoftware.htmlunit.WebClient;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.test.web.htmlunit.LocalHostWebClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder;
/**
* Auto-configuration for HtmlUnit {@link WebClient} MockMVC integration.
*
* @author Phillip Webb
* @since 1.4.0
*/
@Configuration
@ConditionalOnClass(WebClient.class)
@AutoConfigureAfter(MockMvcAutoConfiguration.class)
@ConditionalOnProperty(prefix = "spring.test.mockmvc.webclient", name = "enabled", matchIfMissing = true)
public class MockMvcWebClientAutoConfiguration {
private final Environment environment;
MockMvcWebClientAutoConfiguration(Environment environment) {
this.environment = environment;
}
@Bean
@ConditionalOnMissingBean({ WebClient.class, MockMvcWebClientBuilder.class })
@ConditionalOnBean(MockMvc.class)
public MockMvcWebClientBuilder mockMvcWebClientBuilder(MockMvc mockMvc) {
return MockMvcWebClientBuilder.mockMvcSetup(mockMvc)
.withDelegate(new LocalHostWebClient(this.environment));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(MockMvcWebClientBuilder.class)
public WebClient htmlUnitWebClient(MockMvcWebClientBuilder builder) {
return builder.build();
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.test.web.htmlunit.webdriver.LocalHostWebConnectionHtmlUnitDriver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder;
/**
* Auto-configuration for Selenium {@link WebDriver} MockMVC integration.
*
* @author Phillip Webb
* @since 1.4.0
*/
@Configuration
@ConditionalOnClass(HtmlUnitDriver.class)
@AutoConfigureAfter(MockMvcAutoConfiguration.class)
@ConditionalOnProperty(prefix = "spring.test.mockmvc.webdriver", name = "enabled", matchIfMissing = true)
public class MockMvcWebDriverAutoConfiguration {
private final Environment environment;
MockMvcWebDriverAutoConfiguration(Environment environment) {
this.environment = environment;
}
@Bean
@ConditionalOnMissingBean({ WebDriver.class, MockMvcHtmlUnitDriverBuilder.class })
@ConditionalOnBean(MockMvc.class)
public MockMvcHtmlUnitDriverBuilder mockMvcHtmlUnitDriverBuilder(MockMvc mockMvc) {
return MockMvcHtmlUnitDriverBuilder.mockMvcSetup(mockMvc)
.withDelegate(new LocalHostWebConnectionHtmlUnitDriver(this.environment,
BrowserVersion.CHROME));
}
@Bean
@ConditionalOnMissingBean(WebDriver.class)
@ConditionalOnBean(MockMvcHtmlUnitDriverBuilder.class)
public HtmlUnitDriver htmlUnitDriver(MockMvcHtmlUnitDriverBuilder builder) {
return builder.build();
}
}

View File

@@ -0,0 +1,321 @@
/*
* 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.test.autoconfigure.web.servlet;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.Filter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.test.web.servlet.result.PrintingResultHandler;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.WebApplicationContext;
/**
* {@link MockMvcBuilderCustomizer} for a typical Spring Boot application. Usually applied
* automatically via {@link AutoConfigureMockMvc @AutoConfigureMockMvc}, but may also be
* used directly.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.4.0
*/
public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomizer {
private final WebApplicationContext context;
private boolean addFilters = true;
private MockMvcPrint print = MockMvcPrint.DEFAULT;
private boolean printOnlyOnFailure = true;
/**
* Create a new {@link SpringBootMockMvcBuilderCustomizer} instance.
* @param context the source application context
*/
public SpringBootMockMvcBuilderCustomizer(WebApplicationContext context) {
Assert.notNull(context, "Context must not be null");
this.context = context;
}
@Override
public void customize(ConfigurableMockMvcBuilder<?> builder) {
if (this.addFilters) {
addFilters(builder);
}
ResultHandler printHandler = getPrintHandler();
if (printHandler != null) {
builder.alwaysDo(printHandler);
}
}
private ResultHandler getPrintHandler() {
LinesWriter writer = getLinesWriter();
if (writer == null) {
return null;
}
if (this.printOnlyOnFailure) {
writer = new DeferredLinesWriter(this.context, writer);
}
return new LinesWritingResultHandler(writer);
}
private LinesWriter getLinesWriter() {
if (this.print == MockMvcPrint.NONE) {
return null;
}
if (this.print == MockMvcPrint.LOG_DEBUG) {
return new LoggingLinesWriter();
}
return new SystemLinesWriter(this.print);
}
private void addFilters(ConfigurableMockMvcBuilder<?> builder) {
ServletContextInitializerBeans Initializers = new ServletContextInitializerBeans(
this.context);
for (ServletContextInitializer initializer : Initializers) {
if (initializer instanceof FilterRegistrationBean) {
addFilter(builder, (FilterRegistrationBean<?>) initializer);
}
if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
addFilter(builder, (DelegatingFilterProxyRegistrationBean) initializer);
}
}
}
private void addFilter(ConfigurableMockMvcBuilder<?> builder,
FilterRegistrationBean<?> registration) {
addFilter(builder, registration.getFilter(), registration.getUrlPatterns());
}
private void addFilter(ConfigurableMockMvcBuilder<?> builder,
DelegatingFilterProxyRegistrationBean registration) {
addFilter(builder, registration.getFilter(), registration.getUrlPatterns());
}
private void addFilter(ConfigurableMockMvcBuilder<?> builder, Filter filter,
Collection<String> urls) {
if (urls.isEmpty()) {
builder.addFilters(filter);
}
else {
builder.addFilter(filter, urls.toArray(new String[urls.size()]));
}
}
public void setAddFilters(boolean addFilters) {
this.addFilters = addFilters;
}
public boolean isAddFilters() {
return this.addFilters;
}
public void setPrint(MockMvcPrint print) {
this.print = print;
}
public MockMvcPrint getPrint() {
return this.print;
}
public void setPrintOnlyOnFailure(boolean printOnlyOnFailure) {
this.printOnlyOnFailure = printOnlyOnFailure;
}
public boolean isPrintOnlyOnFailure() {
return this.printOnlyOnFailure;
}
/**
* {@link ResultHandler} that prints {@link MvcResult} details to a given
* {@link LinesWriter}.
*/
private static class LinesWritingResultHandler implements ResultHandler {
private final LinesWriter writer;
LinesWritingResultHandler(LinesWriter writer) {
this.writer = writer;
}
@Override
public void handle(MvcResult result) throws Exception {
LinesPrintingResultHandler delegate = new LinesPrintingResultHandler();
delegate.handle(result);
delegate.write(this.writer);
}
private static class LinesPrintingResultHandler extends PrintingResultHandler {
protected LinesPrintingResultHandler() {
super(new Printer());
}
public void write(LinesWriter writer) {
writer.write(((Printer) getPrinter()).getLines());
}
private static class Printer implements ResultValuePrinter {
private final List<String> lines = new ArrayList<>();
@Override
public void printHeading(String heading) {
this.lines.add("");
this.lines.add(String.format("%s:", heading));
}
@Override
public void printValue(String label, Object value) {
if (value != null && value.getClass().isArray()) {
value = CollectionUtils.arrayToList(value);
}
this.lines.add(String.format("%17s = %s", label, value));
}
public List<String> getLines() {
return this.lines;
}
}
}
}
/**
* Strategy interface to write MVC result lines.
*/
interface LinesWriter {
void write(List<String> lines);
}
/**
* {@link LinesWriter} used to defer writing until errors are detected.
*
* @see MockMvcPrintOnlyOnFailureTestExecutionListener
*/
static class DeferredLinesWriter implements LinesWriter {
private static final String BEAN_NAME = DeferredLinesWriter.class.getName();
private final LinesWriter delegate;
private final List<String> lines = new ArrayList<>();
DeferredLinesWriter(WebApplicationContext context, LinesWriter delegate) {
Assert.state(context instanceof ConfigurableApplicationContext,
"A ConfigurableApplicationContext is required for printOnlyOnFailure");
((ConfigurableApplicationContext) context).getBeanFactory()
.registerSingleton(BEAN_NAME, this);
this.delegate = delegate;
}
@Override
public void write(List<String> lines) {
this.lines.addAll(lines);
}
public void writeDeferredResult() {
this.delegate.write(this.lines);
}
public static DeferredLinesWriter get(ApplicationContext applicationContext) {
try {
return applicationContext.getBean(BEAN_NAME, DeferredLinesWriter.class);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
}
/**
* {@link LinesWriter} to output results to the log.
*/
private static class LoggingLinesWriter implements LinesWriter {
private static final Log logger = LogFactory
.getLog("org.springframework.test.web.servlet.result");
@Override
public void write(List<String> lines) {
if (logger.isDebugEnabled()) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
for (String line : lines) {
printWriter.println(line);
}
logger.debug("MvcResult details:\n" + stringWriter);
}
}
}
/**
* {@link LinesWriter} to output results to {@code System.out} or {@code System.err}.
*/
private static class SystemLinesWriter implements LinesWriter {
private final MockMvcPrint print;
SystemLinesWriter(MockMvcPrint print) {
this.print = print;
}
@Override
public void write(List<String> lines) {
PrintStream printStream = getPrintStream();
for (String line : lines) {
printStream.println(line);
}
}
private PrintStream getPrintStream() {
if (this.print == MockMvcPrint.SYSTEM_ERR) {
return System.err;
}
return System.out;
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import java.util.List;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.MergedContextConfiguration;
/**
* {@link ContextCustomizerFactory} to register a {@link WebDriverScope} and configure
* appropriate bean definitions to use it. Expects the scope to be reset with a
* {@link WebDriverTestExecutionListener}.
*
* @author Phillip Webb
* @see WebDriverTestExecutionListener
* @see WebDriverScope
*/
class WebDriverContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
return new Customizer();
}
private static class Customizer implements ContextCustomizer {
@Override
public void customizeContext(ConfigurableApplicationContext context,
MergedContextConfiguration mergedConfig) {
WebDriverScope.registerWith(context);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || !obj.getClass().equals(getClass())) {
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,147 @@
/*
* 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.test.autoconfigure.web.servlet;
import java.util.HashMap;
import java.util.Map;
import org.openqa.selenium.WebDriver;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* A special scope used for {@link WebDriver} beans. Usually registered by a
* {@link WebDriverContextCustomizerFactory} and reset by a
* {@link WebDriverTestExecutionListener}.
*
* @author Phillip Webb
* @see WebDriverContextCustomizerFactory
* @see WebDriverTestExecutionListener
*/
class WebDriverScope implements Scope {
public static final String NAME = "webDriver";
private static final String WEB_DRIVER_CLASS = "org.openqa.selenium.WebDriver";
private static final String[] BEAN_CLASSES = { WEB_DRIVER_CLASS,
"org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder" };
private final Map<String, Object> instances = new HashMap<>();
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
synchronized (this.instances) {
Object instance = this.instances.get(name);
if (instance == null) {
instance = objectFactory.getObject();
this.instances.put(name, instance);
}
return instance;
}
}
@Override
public Object remove(String name) {
synchronized (this.instances) {
return this.instances.remove(name);
}
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
/**
* Reset all instances in the scope.
* @return {@code true} if items were reset
*/
public boolean reset() {
boolean reset = false;
synchronized (this.instances) {
for (Object instance : this.instances.values()) {
reset = true;
if (instance instanceof WebDriver) {
((WebDriver) instance).quit();
}
}
this.instances.clear();
}
return reset;
}
/**
* Register this scope with the specified context and reassign appropriate bean
* definitions to used it.
* @param context the application context
*/
public static void registerWith(ConfigurableApplicationContext context) {
if (!ClassUtils.isPresent(WEB_DRIVER_CLASS, null)) {
return;
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory.getRegisteredScope(NAME) == null) {
beanFactory.registerScope(NAME, new WebDriverScope());
}
context.addBeanFactoryPostProcessor(WebDriverScope::postProcessBeanFactory);
}
private static void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) {
for (String beanClass : BEAN_CLASSES) {
for (String beanName : beanFactory
.getBeanNamesForType(ClassUtils.resolveClassName(beanClass, null))) {
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
if (!StringUtils.hasLength(definition.getScope())) {
definition.setScope(NAME);
}
}
}
}
/**
* Return the {@link WebDriverScope} being used by the specified context (if any).
* @param context the application context
* @return the web driver scope or {@code null}
*/
public static WebDriverScope getFrom(ApplicationContext context) {
if (context instanceof ConfigurableApplicationContext) {
Scope scope = ((ConfigurableApplicationContext) context).getBeanFactory()
.getRegisteredScope(NAME);
return (scope instanceof WebDriverScope ? (WebDriverScope) scope : null);
}
return null;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* {@link TestExecutionListener} to reset the {@link WebDriverScope}.
*
* @author Phillip Webb
* @see WebDriverContextCustomizerFactory
* @see WebDriverScope
*/
class WebDriverTestExecutionListener extends AbstractTestExecutionListener {
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
WebDriverScope scope = WebDriverScope
.getFrom(testContext.getApplicationContext());
if (scope != null && scope.reset()) {
testContext.setAttribute(
DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE,
Boolean.TRUE);
}
}
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
import org.springframework.boot.test.autoconfigure.filter.TypeExcludeFilters;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.web.servlet.MockMvc;
/**
* Annotation that can be used in combination with {@code @RunWith(SpringRunner.class)}
* for a typical Spring MVC test. Can be used when a test focuses <strong>only</strong> on
* Spring MVC components.
* <p>
* Using this annotation will disable full auto-configuration and instead apply only
* configuration relevant to MVC tests (i.e. {@code @Controller},
* {@code @ControllerAdvice}, {@code @JsonComponent} {@code Filter},
* {@code WebMvcConfigurer} and {@code HandlerMethodArgumentResolver} beans but not
* {@code @Component}, {@code @Service} or {@code @Repository} beans).
* <p>
* By default, tests annotated with {@code @WebMvcTest} will also auto-configure Spring
* Security and {@link MockMvc} (include support for HtmlUnit WebClient and Selenium
* WebDriver). For more fine-grained control of MockMVC the
* {@link AutoConfigureMockMvc @AutoConfigureMockMvc} annotation can be used.
* <p>
* Typically {@code @WebMvcTest} is used in combination with {@link MockBean @MockBean} or
* {@link Import @Import} to create any collaborators required by your {@code @Controller}
* beans.
* <p>
* If you are looking to load your full application configuration and use MockMVC, you
* should consider {@link SpringBootTest @SpringBootTest} combined with
* {@link AutoConfigureMockMvc @AutoConfigureMockMvc} rather than this annotation.
*
* @author Phillip Webb
* @see AutoConfigureWebMvc
* @see AutoConfigureMockMvc
* @see AutoConfigureCache
* @since 1.4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(WebMvcTestContextBootstrapper.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(WebMvcTypeExcludeFilter.class)
@AutoConfigureCache
@AutoConfigureWebMvc
@AutoConfigureMockMvc
@ImportAutoConfiguration
public @interface WebMvcTest {
/**
* Specifies the controllers to test. This is an alias of {@link #controllers()} which
* can be used for brevity if no other attributes are defined. See
* {@link #controllers()} for details.
* @see #controllers()
* @return the controllers to test
*/
@AliasFor("controllers")
Class<?>[] value() default {};
/**
* Specifies the controllers to test. May be left blank if all {@code @Controller}
* beans should be added to the application context.
* @see #value()
* @return the controllers to test
*/
@AliasFor("value")
Class<?>[] controllers() default {};
/**
* Determines if default filtering should be used with
* {@link SpringBootApplication @SpringBootApplication}. By default only
* {@code @Controller} (when no explicit {@link #controllers() controllers} are
* defined), {@code @ControllerAdvice} and {@code WebMvcConfigurer} beans are
* included.
* @see #includeFilters()
* @see #excludeFilters()
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the
* application context.
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be
* added to the application context.
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* If Spring Security's {@link MockMvc} support should be auto-configured when it is
* on the classpath. Defaults to {@code true}.
* @return if Spring Security's MockMvc support is auto-configured
*/
@AliasFor(annotation = AutoConfigureMockMvc.class, attribute = "secure")
boolean secure() default true;
/**
* Auto-configuration exclusions that should be applied for this test.
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2012-2016 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.test.autoconfigure.web.servlet;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.TestContextBootstrapper;
import org.springframework.test.context.web.WebMergedContextConfiguration;
/**
* {@link TestContextBootstrapper} for {@link WebMvcTest @WebMvcTest} support.
*
* @author Phillip Webb
*/
class WebMvcTestContextBootstrapper extends SpringBootTestContextBootstrapper {
@Override
protected MergedContextConfiguration processMergedContextConfiguration(
MergedContextConfiguration mergedConfig) {
return new WebMergedContextConfiguration(
super.processMergedContextConfiguration(mergedConfig), "");
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.test.autoconfigure.web.servlet;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.test.autoconfigure.filter.AnnotationCustomizableTypeExcludeFilter;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* {@link TypeExcludeFilter} for {@link WebMvcTest @WebMvcTest}.
*
* @author Phillip Webb
*/
class WebMvcTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
private static final Set<Class<?>> DEFAULT_INCLUDES;
static {
Set<Class<?>> includes = new LinkedHashSet<>();
includes.add(ControllerAdvice.class);
includes.add(JsonComponent.class);
includes.add(WebMvcConfigurer.class);
includes.add(javax.servlet.Filter.class);
includes.add(FilterRegistrationBean.class);
includes.add(DelegatingFilterProxyRegistrationBean.class);
includes.add(HandlerMethodArgumentResolver.class);
includes.add(HttpMessageConverter.class);
includes.add(ErrorAttributes.class);
DEFAULT_INCLUDES = Collections.unmodifiableSet(includes);
}
private static final Set<Class<?>> DEFAULT_INCLUDES_AND_CONTROLLER;
static {
Set<Class<?>> includes = new LinkedHashSet<>(DEFAULT_INCLUDES);
includes.add(Controller.class);
DEFAULT_INCLUDES_AND_CONTROLLER = Collections.unmodifiableSet(includes);
}
private final WebMvcTest annotation;
WebMvcTypeExcludeFilter(Class<?> testClass) {
this.annotation = AnnotatedElementUtils.getMergedAnnotation(testClass,
WebMvcTest.class);
}
@Override
protected boolean hasAnnotation() {
return this.annotation != null;
}
@Override
protected Filter[] getFilters(FilterType type) {
switch (type) {
case INCLUDE:
return this.annotation.includeFilters();
case EXCLUDE:
return this.annotation.excludeFilters();
}
throw new IllegalStateException("Unsupported type " + type);
}
@Override
protected boolean isUseDefaultFilters() {
return this.annotation.useDefaultFilters();
}
@Override
protected Set<Class<?>> getDefaultIncludes() {
if (ObjectUtils.isEmpty(this.annotation.controllers())) {
return DEFAULT_INCLUDES_AND_CONTROLLER;
}
return DEFAULT_INCLUDES;
}
@Override
protected Set<Class<?>> getComponentIncludes() {
return new LinkedHashSet<>(Arrays.asList(this.annotation.controllers()));
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-2016 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.
*/
/**
* Auto-configuration for Spring MVC tests.
*/
package org.springframework.boot.test.autoconfigure.web.servlet;

View File

@@ -0,0 +1,16 @@
{
"properties": [
{
"name": "spring.test.database.replace",
"type": "org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase$Replace",
"description": "Type of existing DataSource to replace.",
"defaultValue": "any"
},
{
"name": "spring.test.mockmvc.print",
"type": "org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrint",
"description": "MVC Print option.",
"defaultValue": "default"
}
]
}

View File

@@ -0,0 +1,152 @@
# AutoConfigureCache auto-configuration imports
org.springframework.boot.test.autoconfigure.core.AutoConfigureCache=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
# AutoConfigureDataJpa auto-configuration imports
org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa=\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
# AutoConfigureDataLdap auto-configuration imports
org.springframework.boot.test.autoconfigure.data.ldap.AutoConfigureDataLdap=\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration
# AutoConfigureDataMongo auto-configuration imports
org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo=\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration
# AutoConfigureDataNeo4j auto-configuration imports
org.springframework.boot.test.autoconfigure.data.neo4j.AutoConfigureDataNeo4j=\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
# AutoConfigureDataRedis auto-configuration imports
org.springframework.boot.test.autoconfigure.data.redis.AutoConfigureDataRedis=\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration
# AutoConfigureJdbc auto-configuration imports
org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc=\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
# AutoConfigureTestDatabase auto-configuration imports
org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase=\
org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
# AutoConfigureJooq auto-configuration imports
org.springframework.boot.test.autoconfigure.jooq.AutoConfigureJooq=\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
# AutoConfigureJson auto-configuration imports
org.springframework.boot.test.autoconfigure.json.AutoConfigureJson=\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration
# AutoConfigureJsonTesters auto-configuration imports
org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters=\
org.springframework.boot.test.autoconfigure.json.JsonTestersAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration
# AutoConfigureWebClient auto-configuration imports
org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient=\
org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration
# AutoConfigureWebFlux auto-configuration imports
org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebFlux=\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration
# AutoConfigureMockMvc auto-configuration imports
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc=\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityAutoConfiguration,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration
# AutoConfigureMockRestServiceServer
org.springframework.boot.test.autoconfigure.web.client.AutoConfigureMockRestServiceServer=\
org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerAutoConfiguration
# AutoConfigureRestDocs auto-configuration imports
org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs=\
org.springframework.boot.test.autoconfigure.restdocs.RestDocsAutoConfiguration
# AutoConfigureTestEntityManager auto-configuration imports
org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestEntityManager=\
org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration
# AutoConfigureWebClient auto-configuration imports
org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient=\
org.springframework.boot.test.autoconfigure.web.client.WebClientRestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
# AutoConfigureWebMvc auto-configuration imports
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc=\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
# DefaultTestExecutionListenersPostProcessors
org.springframework.boot.test.context.DefaultTestExecutionListenersPostProcessor=\
org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener$PostProcessor
# Spring Test ContextCustomizerFactories
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizerFactory,\
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory
# Test Execution Listeners
org.springframework.test.context.TestExecutionListener=\
org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener,\
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2012-2016 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.test.autoconfigure;
import org.assertj.core.api.Condition;
import org.assertj.core.description.TextDescription;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.context.ApplicationContext;
/**
* AssertJ {@link Condition} that checks that an auto-configuration has been imported.
*
* @author Andy Wilkinson
*/
public final class AutoConfigurationImportedCondition
extends Condition<ApplicationContext> {
private final Class<?> autoConfigurationClass;
private AutoConfigurationImportedCondition(Class<?> autoConfigurationClass) {
super(new TextDescription("%s imported", autoConfigurationClass.getName()));
this.autoConfigurationClass = autoConfigurationClass;
}
@Override
public boolean matches(ApplicationContext context) {
ConditionEvaluationReport report = ConditionEvaluationReport
.get((ConfigurableListableBeanFactory) context
.getAutowireCapableBeanFactory());
return report.getConditionAndOutcomesBySource().keySet()
.contains(this.autoConfigurationClass.getName());
}
/**
* Returns a {@link Condition} that verifies that the given
* {@code autoConfigurationClass} has been imported.
* @param autoConfigurationClass the auto-configuration class
* @return the condition
*/
public static AutoConfigurationImportedCondition importedAutoConfiguration(
Class<?> autoConfigurationClass) {
return new AutoConfigurationImportedCondition(autoConfigurationClass);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012-2016 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.test.autoconfigure;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Example {@link SpringBootApplication @SpringBootApplication} for use with
* {@link OverrideAutoConfiguration} tests.
*
* @author Phillip Webb
*/
@SpringBootConfiguration
@EnableAutoConfiguration
public class ExampleSpringBootApplication {
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2012-2016 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.test.autoconfigure;
import org.springframework.boot.test.context.TestConfiguration;
/**
* Example {@link TestConfiguration @TestConfiguration} for
* {@link OverrideAutoConfiguration} tests.
*
* @author Phillip Webb
*/
@TestConfiguration
public class ExampleTestConfig {
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2012-2016 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.test.autoconfigure;
import org.junit.Test;
import org.springframework.test.context.ContextCustomizer;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OverrideAutoConfigurationContextCustomizerFactory}.
*
* @author pwebb
*/
public class OverrideAutoConfigurationContextCustomizerFactoryTests {
private OverrideAutoConfigurationContextCustomizerFactory factory = new OverrideAutoConfigurationContextCustomizerFactory();
@Test
public void getContextCustomizerWhenHasNoAnnotationShouldReturnNull()
throws Exception {
ContextCustomizer customizer = this.factory
.createContextCustomizer(NoAnnotation.class, null);
assertThat(customizer).isNull();
}
@Test
public void getContextCustomizerWhenHasAnnotationEnabledTrueShouldReturnNull()
throws Exception {
ContextCustomizer customizer = this.factory
.createContextCustomizer(WithAnnotationEnabledTrue.class, null);
assertThat(customizer).isNull();
}
@Test
public void getContextCustomizerWhenHasAnnotationEnabledFalseShouldReturnCustomizer()
throws Exception {
ContextCustomizer customizer = this.factory
.createContextCustomizer(WithAnnotationEnabledFalse.class, null);
assertThat(customizer).isNotNull();
}
@Test
public void hashCodeAndEquals() throws Exception {
ContextCustomizer customizer1 = this.factory
.createContextCustomizer(WithAnnotationEnabledFalse.class, null);
ContextCustomizer customizer2 = this.factory
.createContextCustomizer(WithSameAnnotation.class, null);
assertThat(customizer1.hashCode()).isEqualTo(customizer2.hashCode());
assertThat(customizer1).isEqualTo(customizer1).isEqualTo(customizer2);
}
static class NoAnnotation {
}
@OverrideAutoConfiguration(enabled = true)
static class WithAnnotationEnabledTrue {
}
@OverrideAutoConfiguration(enabled = false)
static class WithAnnotationEnabledFalse {
}
@OverrideAutoConfiguration(enabled = false)
static class WithSameAnnotation {
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2012-2016 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.test.autoconfigure;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link OverrideAutoConfiguration} when {@code enabled} is
* {@code false}.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
@OverrideAutoConfiguration(enabled = false)
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@ImportAutoConfiguration(ExampleTestConfig.class)
public class OverrideAutoConfigurationEnabledFalseIntegrationTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Autowired
private ApplicationContext context;
@Test
public void disabledAutoConfiguration() throws Exception {
ApplicationContext context = this.context;
assertThat(context.getBean(ExampleTestConfig.class)).isNotNull();
this.thrown.expect(NoSuchBeanDefinitionException.class);
context.getBean(ConfigurationPropertiesBindingPostProcessor.class);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2012-2016 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.test.autoconfigure;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link OverrideAutoConfiguration} when {@code enabled} is
* {@code true}.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
@OverrideAutoConfiguration(enabled = true)
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@ImportAutoConfiguration(ExampleTestConfig.class)
public class OverrideAutoConfigurationEnabledTrueIntegrationTest {
@Autowired
private ApplicationContext context;
@Test
public void autoConfiguredContext() throws Exception {
ApplicationContext context = this.context;
assertThat(context.getBean(ExampleSpringBootApplication.class)).isNotNull();
assertThat(context.getBean(ConfigurationPropertiesBindingPostProcessor.class))
.isNotNull();
}
}

Some files were not shown because too many files have changed in this diff Show More