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:
@@ -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;
|
||||
|
||||
import org.junit.AssumptionViolatedException;
|
||||
|
||||
import org.springframework.boot.system.JavaVersion;
|
||||
|
||||
/**
|
||||
* Provides utility methods that allow JUnit tests to {@link org.junit.Assume} certain
|
||||
* conditions hold {@code true}. If the assumption fails, it means the test should be
|
||||
* skipped.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public abstract class Assume {
|
||||
|
||||
/**
|
||||
* Assume that the specified {@link JavaVersion} is the one currently available.
|
||||
* @param version the expected Java version
|
||||
* @throws AssumptionViolatedException if the assumption fails
|
||||
*/
|
||||
public static void javaVersion(JavaVersion version) {
|
||||
JavaVersion current = JavaVersion.getJavaVersion();
|
||||
org.junit.Assume.assumeTrue(
|
||||
String.format("This test should run on %s (got %s)", version, current),
|
||||
current == version);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
import org.springframework.boot.context.config.ConfigFileApplicationListener;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContextInitializer} that can be used with the
|
||||
* {@link ContextConfiguration#initializers()} to trigger loading of
|
||||
* {@literal application.properties}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see ConfigFileApplicationListener
|
||||
*/
|
||||
public class ConfigFileApplicationContextInitializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
@Override
|
||||
public void initialize(final ConfigurableApplicationContext applicationContext) {
|
||||
new ConfigFileApplicationListener() {
|
||||
public void apply() {
|
||||
addPropertySources(applicationContext.getEnvironment(),
|
||||
applicationContext);
|
||||
addPostProcessors(applicationContext);
|
||||
}
|
||||
}.apply();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
|
||||
/**
|
||||
* Callback interface trigger from {@link SpringBootTestContextBootstrapper} that can be
|
||||
* used to post-process the list of default {@link TestExecutionListener} classes to be
|
||||
* used by a test. Can be used to add or remove existing listener classes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.1
|
||||
* @see SpringBootTest
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DefaultTestExecutionListenersPostProcessor {
|
||||
|
||||
/**
|
||||
* Post process the list of default {@link TestExecutionListener} classes to be used.
|
||||
* @param listeners the source listeners
|
||||
* @return the actual listeners that should be used
|
||||
*/
|
||||
Set<Class<? extends TestExecutionListener>> postProcessDefaultTestExecutionListeners(
|
||||
Set<Class<? extends TestExecutionListener>> listeners);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
/**
|
||||
* Test {@link URLClassLoader} that hides configurable classes.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class HideClassesClassLoader extends URLClassLoader {
|
||||
|
||||
private final Class<?>[] hiddenClasses;
|
||||
|
||||
public HideClassesClassLoader(Class<?>... hiddenClasses) {
|
||||
super(new URL[0], HideClassesClassLoader.class.getClassLoader());
|
||||
this.hiddenClasses = hiddenClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
for (Class<?> hiddenClass : this.hiddenClasses) {
|
||||
if (name.equals(hiddenClass.getName())) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
/**
|
||||
* Test {@link URLClassLoader} that hides configurable packages. No class in one of those
|
||||
* packages or sub-packages are visible.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class HidePackagesClassLoader extends URLClassLoader {
|
||||
|
||||
private final String[] hiddenPackages;
|
||||
|
||||
/**
|
||||
* Create a new instance with the packages to hide.
|
||||
* @param hiddenPackages the packages to hide
|
||||
*/
|
||||
public HidePackagesClassLoader(String... hiddenPackages) {
|
||||
super(new URL[0], HidePackagesClassLoader.class.getClassLoader());
|
||||
this.hiddenPackages = hiddenPackages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
for (String hiddenPackage : this.hiddenPackages) {
|
||||
if (name.startsWith(hiddenPackage)) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.boot.context.annotation.DeterminableImports;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizer} to allow {@code @Import} annotations to be used directly on
|
||||
* test classes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @see ImportsContextCustomizerFactory
|
||||
*/
|
||||
class ImportsContextCustomizer implements ContextCustomizer {
|
||||
|
||||
static final String TEST_CLASS_ATTRIBUTE = "testClass";
|
||||
|
||||
private final Class<?> testClass;
|
||||
|
||||
private final ContextCustomizerKey key;
|
||||
|
||||
ImportsContextCustomizer(Class<?> testClass) {
|
||||
this.testClass = testClass;
|
||||
this.key = new ContextCustomizerKey(testClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedContextConfiguration) {
|
||||
BeanDefinitionRegistry registry = getBeanDefinitionRegistry(context);
|
||||
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(
|
||||
registry);
|
||||
registerCleanupPostProcessor(registry, reader);
|
||||
registerImportsConfiguration(registry, reader);
|
||||
}
|
||||
|
||||
private void registerCleanupPostProcessor(BeanDefinitionRegistry registry,
|
||||
AnnotatedBeanDefinitionReader reader) {
|
||||
BeanDefinition definition = registerBean(registry, reader,
|
||||
ImportsCleanupPostProcessor.BEAN_NAME, ImportsCleanupPostProcessor.class);
|
||||
definition.getConstructorArgumentValues().addIndexedArgumentValue(0,
|
||||
this.testClass);
|
||||
}
|
||||
|
||||
private void registerImportsConfiguration(BeanDefinitionRegistry registry,
|
||||
AnnotatedBeanDefinitionReader reader) {
|
||||
BeanDefinition definition = registerBean(registry, reader,
|
||||
ImportsConfiguration.BEAN_NAME, ImportsConfiguration.class);
|
||||
definition.setAttribute(TEST_CLASS_ATTRIBUTE, this.testClass);
|
||||
}
|
||||
|
||||
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
|
||||
if (context instanceof BeanDefinitionRegistry) {
|
||||
return (BeanDefinitionRegistry) context;
|
||||
}
|
||||
if (context instanceof AbstractApplicationContext) {
|
||||
return (BeanDefinitionRegistry) ((AbstractApplicationContext) context)
|
||||
.getBeanFactory();
|
||||
}
|
||||
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private BeanDefinition registerBean(BeanDefinitionRegistry registry,
|
||||
AnnotatedBeanDefinitionReader reader, String beanName, Class<?> type) {
|
||||
reader.registerBean(type, beanName);
|
||||
BeanDefinition definition = registry.getBeanDefinition(beanName);
|
||||
return definition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.key.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
// ImportSelectors are flexible so the only safe cache key is the test class
|
||||
ImportsContextCustomizer other = (ImportsContextCustomizer) obj;
|
||||
return this.key.equals(other.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("key", this.key).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Configuration} registered to trigger the {@link ImportsSelector}.
|
||||
*/
|
||||
@Configuration
|
||||
@Import(ImportsSelector.class)
|
||||
static class ImportsConfiguration {
|
||||
|
||||
static final String BEAN_NAME = ImportsConfiguration.class.getName();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ImportSelector} that returns the original test class so that direct
|
||||
* {@code @Import} annotations are processed.
|
||||
*/
|
||||
static class ImportsSelector implements ImportSelector, BeanFactoryAware {
|
||||
|
||||
private static final String[] NO_IMPORTS = {};
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
BeanDefinition definition = this.beanFactory
|
||||
.getBeanDefinition(ImportsConfiguration.BEAN_NAME);
|
||||
Object testClass = (definition == null ? null
|
||||
: definition.getAttribute(TEST_CLASS_ATTRIBUTE));
|
||||
return (testClass == null ? NO_IMPORTS
|
||||
: new String[] { ((Class<?>) testClass).getName() });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionRegistryPostProcessor} to cleanup temporary configuration
|
||||
* added to load imports.
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
static class ImportsCleanupPostProcessor
|
||||
implements BeanDefinitionRegistryPostProcessor {
|
||||
|
||||
static final String BEAN_NAME = ImportsCleanupPostProcessor.class.getName();
|
||||
|
||||
private final Class<?> testClass;
|
||||
|
||||
ImportsCleanupPostProcessor(Class<?> testClass) {
|
||||
this.testClass = testClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
|
||||
throws BeansException {
|
||||
try {
|
||||
String[] names = registry.getBeanDefinitionNames();
|
||||
for (String name : names) {
|
||||
BeanDefinition definition = registry.getBeanDefinition(name);
|
||||
if (this.testClass.getName().equals(definition.getBeanClassName())) {
|
||||
registry.removeBeanDefinition(name);
|
||||
}
|
||||
}
|
||||
registry.removeBeanDefinition(ImportsConfiguration.BEAN_NAME);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The key used to ensure correct application context caching. Keys are generated
|
||||
* based on <em>all</em> the annotations used with the test that aren't core Java or
|
||||
* Kotlin annotations. We must use something broader than just {@link Import @Import}
|
||||
* annotations since an {@code @Import} may use an {@link ImportSelector} which could
|
||||
* make decisions based on anything available from {@link AnnotationMetadata}.
|
||||
*/
|
||||
static class ContextCustomizerKey {
|
||||
|
||||
private static final Class<?>[] NO_IMPORTS = {};
|
||||
|
||||
private static final Set<AnnotationFilter> annotationFilters;
|
||||
|
||||
static {
|
||||
Set<AnnotationFilter> filters = new HashSet<>();
|
||||
filters.add(new JavaLangAnnotationFilter());
|
||||
filters.add(new KotlinAnnotationFilter());
|
||||
filters.add(new SpockAnnotationFilter());
|
||||
annotationFilters = Collections.unmodifiableSet(filters);
|
||||
}
|
||||
|
||||
private final Set<Object> key;
|
||||
|
||||
ContextCustomizerKey(Class<?> testClass) {
|
||||
Set<Annotation> annotations = new HashSet<>();
|
||||
Set<Class<?>> seen = new HashSet<>();
|
||||
collectClassAnnotations(testClass, annotations, seen);
|
||||
Set<Object> determinedImports = determineImports(annotations, testClass);
|
||||
this.key = Collections.<Object>unmodifiableSet(
|
||||
determinedImports != null ? determinedImports : annotations);
|
||||
}
|
||||
|
||||
private void collectClassAnnotations(Class<?> classType,
|
||||
Set<Annotation> annotations, Set<Class<?>> seen) {
|
||||
if (seen.add(classType)) {
|
||||
collectElementAnnotations(classType, annotations, seen);
|
||||
for (Class<?> interfaceType : classType.getInterfaces()) {
|
||||
collectClassAnnotations(interfaceType, annotations, seen);
|
||||
}
|
||||
if (classType.getSuperclass() != null) {
|
||||
collectClassAnnotations(classType.getSuperclass(), annotations, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collectElementAnnotations(AnnotatedElement element,
|
||||
Set<Annotation> annotations, Set<Class<?>> seen) {
|
||||
for (Annotation annotation : element.getDeclaredAnnotations()) {
|
||||
if (!isIgnoredAnnotation(annotation)) {
|
||||
annotations.add(annotation);
|
||||
collectClassAnnotations(annotation.annotationType(), annotations,
|
||||
seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isIgnoredAnnotation(Annotation annotation) {
|
||||
for (AnnotationFilter annotationFilter : annotationFilters) {
|
||||
if (annotationFilter.isIgnored(annotation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<Object> determineImports(Set<Annotation> annotations,
|
||||
Class<?> testClass) {
|
||||
Set<Object> determinedImports = new LinkedHashSet<>();
|
||||
AnnotationMetadata testClassMetadata = new StandardAnnotationMetadata(
|
||||
testClass);
|
||||
for (Annotation annotation : annotations) {
|
||||
for (Class<?> source : getImports(annotation)) {
|
||||
Set<Object> determinedSourceImports = determineImports(source,
|
||||
testClassMetadata);
|
||||
if (determinedSourceImports == null) {
|
||||
return null;
|
||||
}
|
||||
determinedImports.addAll(determinedSourceImports);
|
||||
}
|
||||
}
|
||||
return determinedImports;
|
||||
}
|
||||
|
||||
private Class<?>[] getImports(Annotation annotation) {
|
||||
if (annotation instanceof Import) {
|
||||
return ((Import) annotation).value();
|
||||
}
|
||||
return NO_IMPORTS;
|
||||
}
|
||||
|
||||
private Set<Object> determineImports(Class<?> source,
|
||||
AnnotationMetadata metadata) {
|
||||
if (DeterminableImports.class.isAssignableFrom(source)) {
|
||||
// We can determine the imports
|
||||
return ((DeterminableImports) instantiate(source))
|
||||
.determineImports(metadata);
|
||||
}
|
||||
if (ImportSelector.class.isAssignableFrom(source)
|
||||
|| ImportBeanDefinitionRegistrar.class.isAssignableFrom(source)) {
|
||||
// Standard ImportSelector and ImportBeanDefinitionRegistrar could
|
||||
// use anything to determine the imports so we can't be sure
|
||||
return null;
|
||||
}
|
||||
// The source itself is the import
|
||||
return Collections.<Object>singleton(source.getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T instantiate(Class<T> source) {
|
||||
try {
|
||||
Constructor<?> constructor = source.getDeclaredConstructor();
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
return (T) constructor.newInstance();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to instantiate DeterminableImportSelector "
|
||||
+ source.getName(),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.key.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj != null && getClass().equals(obj.getClass())
|
||||
&& this.key.equals(((ContextCustomizerKey) obj).key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.key.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter used to limit considered annotations.
|
||||
*/
|
||||
private interface AnnotationFilter {
|
||||
|
||||
boolean isIgnored(Annotation annotation);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AnnotationFilter} for {@literal java.lang} annotations.
|
||||
*/
|
||||
private static final class JavaLangAnnotationFilter implements AnnotationFilter {
|
||||
|
||||
@Override
|
||||
public boolean isIgnored(Annotation annotation) {
|
||||
return AnnotationUtils.isInJavaLangAnnotationPackage(annotation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AnnotationFilter} for Kotlin annotations.
|
||||
*/
|
||||
private static final class KotlinAnnotationFilter implements AnnotationFilter {
|
||||
|
||||
@Override
|
||||
public boolean isIgnored(Annotation annotation) {
|
||||
return "kotlin.Metadata".equals(annotation.annotationType().getName())
|
||||
|| isInKotlinAnnotationPackage(annotation);
|
||||
}
|
||||
|
||||
private boolean isInKotlinAnnotationPackage(Annotation annotation) {
|
||||
return annotation.annotationType().getName().startsWith("kotlin.annotation.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AnnotationFilter} for Spock annotations.
|
||||
*/
|
||||
private static final class SpockAnnotationFilter implements AnnotationFilter {
|
||||
|
||||
@Override
|
||||
public boolean isIgnored(Annotation annotation) {
|
||||
return annotation.annotationType().getName().startsWith("org.spockframework.")
|
||||
|| annotation.annotationType().getName().startsWith("spock.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
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.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} to allow {@code @Import} annotations to be used
|
||||
* directly on test classes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ImportsContextCustomizer
|
||||
*/
|
||||
class ImportsContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
if (AnnotatedElementUtils.findMergedAnnotation(testClass, Import.class) != null) {
|
||||
assertHasNoBeanMethods(testClass);
|
||||
return new ImportsContextCustomizer(testClass);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void assertHasNoBeanMethods(Class<?> testClass) {
|
||||
ReflectionUtils.doWithMethods(testClass, this::assertHasNoBeanMethods);
|
||||
}
|
||||
|
||||
private void assertHasNoBeanMethods(Method method) {
|
||||
Assert.state(!AnnotatedElementUtils.isAnnotated(method, Bean.class),
|
||||
"Test classes cannot include @Bean methods");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
/**
|
||||
* Encapsulates the <em>merged</em> context configuration declared on a test class and all
|
||||
* of its superclasses for a reactive web application.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class ReactiveWebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
|
||||
public ReactiveWebMergedContextConfiguration(
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
super(mergedConfig);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Internal utility class to scan for a {@link SpringBootConfiguration} class.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
final class SpringBootConfigurationFinder {
|
||||
|
||||
private static final Map<String, Class<?>> cache = Collections
|
||||
.synchronizedMap(new Cache(40));
|
||||
|
||||
private final ClassPathScanningCandidateComponentProvider scanner;
|
||||
|
||||
SpringBootConfigurationFinder() {
|
||||
this.scanner = new ClassPathScanningCandidateComponentProvider(false);
|
||||
this.scanner.addIncludeFilter(
|
||||
new AnnotationTypeFilter(SpringBootConfiguration.class));
|
||||
this.scanner.setResourcePattern("*.class");
|
||||
}
|
||||
|
||||
public Class<?> findFromClass(Class<?> source) {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
return findFromPackage(ClassUtils.getPackageName(source));
|
||||
}
|
||||
|
||||
public Class<?> findFromPackage(String source) {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
Class<?> configuration = cache.get(source);
|
||||
if (configuration == null) {
|
||||
configuration = scanPackage(source);
|
||||
cache.put(source, configuration);
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
private Class<?> scanPackage(String source) {
|
||||
while (!source.isEmpty()) {
|
||||
Set<BeanDefinition> components = this.scanner.findCandidateComponents(source);
|
||||
if (!components.isEmpty()) {
|
||||
Assert.state(components.size() == 1,
|
||||
"Found multiple @SpringBootConfiguration annotated classes "
|
||||
+ components);
|
||||
return ClassUtils.resolveClassName(
|
||||
components.iterator().next().getBeanClassName(), null);
|
||||
}
|
||||
source = getParentPackage(source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getParentPackage(String sourcePackage) {
|
||||
int lastDot = sourcePackage.lastIndexOf(".");
|
||||
return (lastDot == -1 ? "" : sourcePackage.substring(0, lastDot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache implementation based on {@link LinkedHashMap}.
|
||||
*/
|
||||
private static class Cache extends LinkedHashMap<String, Class<?>> {
|
||||
|
||||
private final int maxSize;
|
||||
|
||||
Cache(int maxSize) {
|
||||
super(16, 0.75f, true);
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<String, Class<?>> eldest) {
|
||||
return size() > this.maxSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.boot.test.mock.web.SpringBootMockServletContext;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.servlet.support.ServletContextApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.SpringVersion;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.support.AbstractContextLoader;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoaderUtils;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.test.context.web.WebMergedContextConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link ContextLoader} that can be used to test Spring Boot applications (those that
|
||||
* normally startup using {@link SpringApplication}). Although this loader can be used
|
||||
* directly, most test will instead want to use it with {@link SpringBootTest}.
|
||||
* <p>
|
||||
* The loader supports both standard {@link MergedContextConfiguration} as well as
|
||||
* {@link WebMergedContextConfiguration}. If {@link WebMergedContextConfiguration} is used
|
||||
* the context will either use a mock servlet environment, or start the full embedded web
|
||||
* server.
|
||||
* <p>
|
||||
* If {@code @ActiveProfiles} are provided in the test class they will be used to create
|
||||
* the application context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Madhura Bhave
|
||||
* @see SpringBootTest
|
||||
*/
|
||||
public class SpringBootContextLoader extends AbstractContextLoader {
|
||||
|
||||
private static final Set<String> INTEGRATION_TEST_ANNOTATIONS;
|
||||
|
||||
static {
|
||||
Set<String> annotations = new LinkedHashSet<>();
|
||||
annotations.add("org.springframework.boot.test.IntegrationTest");
|
||||
annotations.add("org.springframework.boot.test.WebIntegrationTest");
|
||||
INTEGRATION_TEST_ANNOTATIONS = Collections.unmodifiableSet(annotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationContext loadContext(MergedContextConfiguration config)
|
||||
throws Exception {
|
||||
Class<?>[] configClasses = config.getClasses();
|
||||
String[] configLocations = config.getLocations();
|
||||
Assert.state(
|
||||
!ObjectUtils.isEmpty(configClasses)
|
||||
|| !ObjectUtils.isEmpty(configLocations),
|
||||
"No configuration classes "
|
||||
+ "or locations found in @SpringApplicationConfiguration. "
|
||||
+ "For default configuration detection to work you need "
|
||||
+ "Spring 4.0.3 or better (found " + SpringVersion.getVersion()
|
||||
+ ").");
|
||||
SpringApplication application = getSpringApplication();
|
||||
application.setMainApplicationClass(config.getTestClass());
|
||||
application.addPrimarySources(Arrays.asList(configClasses));
|
||||
application.getSources().addAll(Arrays.asList(configLocations));
|
||||
ConfigurableEnvironment environment = new StandardEnvironment();
|
||||
if (!ObjectUtils.isEmpty(config.getActiveProfiles())) {
|
||||
setActiveProfiles(environment, config.getActiveProfiles());
|
||||
}
|
||||
TestPropertySourceUtils.addPropertiesFilesToEnvironment(environment,
|
||||
application.getResourceLoader() == null
|
||||
? new DefaultResourceLoader(getClass().getClassLoader())
|
||||
: application.getResourceLoader(),
|
||||
config.getPropertySourceLocations());
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment,
|
||||
getInlinedProperties(config));
|
||||
application.setEnvironment(environment);
|
||||
List<ApplicationContextInitializer<?>> initializers = getInitializers(config,
|
||||
application);
|
||||
if (config instanceof WebMergedContextConfiguration) {
|
||||
application.setWebApplicationType(WebApplicationType.SERVLET);
|
||||
if (!isEmbeddedWebEnvironment(config)) {
|
||||
new WebConfigurer().configure(config, application, initializers);
|
||||
}
|
||||
}
|
||||
else if (config instanceof ReactiveWebMergedContextConfiguration) {
|
||||
application.setWebApplicationType(WebApplicationType.REACTIVE);
|
||||
if (!isEmbeddedWebEnvironment(config)) {
|
||||
new ReactiveWebConfigurer().configure(application);
|
||||
}
|
||||
}
|
||||
else {
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
}
|
||||
application.setInitializers(initializers);
|
||||
ConfigurableApplicationContext context = application.run();
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds new {@link org.springframework.boot.SpringApplication} instance. You can
|
||||
* override this method to add custom behavior
|
||||
* @return {@link org.springframework.boot.SpringApplication} instance
|
||||
*/
|
||||
protected SpringApplication getSpringApplication() {
|
||||
return new SpringApplication();
|
||||
}
|
||||
|
||||
private void setActiveProfiles(ConfigurableEnvironment environment,
|
||||
String[] profiles) {
|
||||
TestPropertyValues
|
||||
.of("spring.profiles.active="
|
||||
+ StringUtils.arrayToCommaDelimitedString(profiles))
|
||||
.applyTo(environment);
|
||||
}
|
||||
|
||||
protected String[] getInlinedProperties(MergedContextConfiguration config) {
|
||||
ArrayList<String> properties = new ArrayList<>();
|
||||
// JMX bean names will clash if the same bean is used in multiple contexts
|
||||
disableJmx(properties);
|
||||
properties.addAll(Arrays.asList(config.getPropertySourceProperties()));
|
||||
if (!isEmbeddedWebEnvironment(config) && !hasCustomServerPort(properties)) {
|
||||
properties.add("server.port=-1");
|
||||
}
|
||||
return properties.toArray(new String[properties.size()]);
|
||||
}
|
||||
|
||||
private void disableJmx(List<String> properties) {
|
||||
properties.add("spring.jmx.enabled=false");
|
||||
}
|
||||
|
||||
private boolean hasCustomServerPort(List<String> properties) {
|
||||
Binder binder = new Binder(convertToConfigurationPropertySource(properties));
|
||||
return binder.bind("server.port", Bindable.of(String.class)).isBound();
|
||||
}
|
||||
|
||||
private ConfigurationPropertySource convertToConfigurationPropertySource(
|
||||
List<String> properties) {
|
||||
String[] array = properties.toArray(new String[properties.size()]);
|
||||
return new MapConfigurationPropertySource(
|
||||
TestPropertySourceUtils.convertInlinedPropertiesToMap(array));
|
||||
}
|
||||
|
||||
private List<ApplicationContextInitializer<?>> getInitializers(
|
||||
MergedContextConfiguration config, SpringApplication application) {
|
||||
List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
|
||||
for (ContextCustomizer contextCustomizer : config.getContextCustomizers()) {
|
||||
initializers.add(new ContextCustomizerAdapter(contextCustomizer, config));
|
||||
}
|
||||
initializers.addAll(application.getInitializers());
|
||||
for (Class<? extends ApplicationContextInitializer<?>> initializerClass : config
|
||||
.getContextInitializerClasses()) {
|
||||
initializers.add(BeanUtils.instantiateClass(initializerClass));
|
||||
}
|
||||
if (config.getParent() != null) {
|
||||
initializers.add(new ParentContextApplicationContextInitializer(
|
||||
config.getParentApplicationContext()));
|
||||
}
|
||||
return initializers;
|
||||
}
|
||||
|
||||
private boolean isEmbeddedWebEnvironment(MergedContextConfiguration config) {
|
||||
for (String annotation : INTEGRATION_TEST_ANNOTATIONS) {
|
||||
if (AnnotatedElementUtils.isAnnotated(config.getTestClass(), annotation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SpringBootTest annotation = AnnotatedElementUtils
|
||||
.findMergedAnnotation(config.getTestClass(), SpringBootTest.class);
|
||||
if (annotation != null && annotation.webEnvironment().isEmbedded()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processContextConfiguration(
|
||||
ContextConfigurationAttributes configAttributes) {
|
||||
super.processContextConfiguration(configAttributes);
|
||||
if (!configAttributes.hasResources()) {
|
||||
Class<?>[] defaultConfigClasses = detectDefaultConfigurationClasses(
|
||||
configAttributes.getDeclaringClass());
|
||||
configAttributes.setClasses(defaultConfigClasses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the default configuration classes for the supplied test class. By default
|
||||
* simply delegates to
|
||||
* {@link AnnotationConfigContextLoaderUtils#detectDefaultConfigurationClasses} .
|
||||
* @param declaringClass the test class that declared {@code @ContextConfiguration}
|
||||
* @return an array of default configuration classes, potentially empty but never
|
||||
* {@code null}
|
||||
* @see AnnotationConfigContextLoaderUtils
|
||||
*/
|
||||
protected Class<?>[] detectDefaultConfigurationClasses(Class<?> declaringClass) {
|
||||
return AnnotationConfigContextLoaderUtils
|
||||
.detectDefaultConfigurationClasses(declaringClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationContext loadContext(String... locations) throws Exception {
|
||||
throw new UnsupportedOperationException("SpringApplicationContextLoader "
|
||||
+ "does not support the loadContext(String...) method");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getResourceSuffixes() {
|
||||
return new String[] { "-context.xml", "Context.groovy" };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResourceSuffix() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to configure {@link WebMergedContextConfiguration}.
|
||||
*/
|
||||
private static class WebConfigurer {
|
||||
|
||||
private static final Class<GenericWebApplicationContext> WEB_CONTEXT_CLASS = GenericWebApplicationContext.class;
|
||||
|
||||
void configure(MergedContextConfiguration configuration,
|
||||
SpringApplication application,
|
||||
List<ApplicationContextInitializer<?>> initializers) {
|
||||
WebMergedContextConfiguration webConfiguration = (WebMergedContextConfiguration) configuration;
|
||||
addMockServletContext(initializers, webConfiguration);
|
||||
application.setApplicationContextClass(WEB_CONTEXT_CLASS);
|
||||
}
|
||||
|
||||
private void addMockServletContext(
|
||||
List<ApplicationContextInitializer<?>> initializers,
|
||||
WebMergedContextConfiguration webConfiguration) {
|
||||
SpringBootMockServletContext servletContext = new SpringBootMockServletContext(
|
||||
webConfiguration.getResourceBasePath());
|
||||
initializers.add(0, new ServletContextApplicationContextInitializer(
|
||||
servletContext, true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to configure {@link ReactiveWebMergedContextConfiguration}.
|
||||
*/
|
||||
private static class ReactiveWebConfigurer {
|
||||
|
||||
private static final Class<GenericReactiveWebApplicationContext> WEB_CONTEXT_CLASS = GenericReactiveWebApplicationContext.class;
|
||||
|
||||
void configure(SpringApplication application) {
|
||||
application.setApplicationContextClass(WEB_CONTEXT_CLASS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts a {@link ContextCustomizer} to a {@link ApplicationContextInitializer} so
|
||||
* that it can be triggered via {@link SpringApplication}.
|
||||
*/
|
||||
private static class ContextCustomizerAdapter
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
private final ContextCustomizer contextCustomizer;
|
||||
|
||||
private final MergedContextConfiguration config;
|
||||
|
||||
ContextCustomizerAdapter(ContextCustomizer contextCustomizer,
|
||||
MergedContextConfiguration config) {
|
||||
this.contextCustomizer = contextCustomizer;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
this.contextCustomizer.customizeContext(applicationContext, this.config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
private static class ParentContextApplicationContextInitializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
private final ApplicationContext parent;
|
||||
|
||||
ParentContextApplicationContextInitializer(ApplicationContext parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
applicationContext.setParent(this.parent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
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.SpringApplication;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.BootstrapWith;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Annotation that can be specified on a test class that runs Spring Boot based tests.
|
||||
* Provides the following features over and above the regular <em>Spring TestContext
|
||||
* Framework</em>:
|
||||
* <ul>
|
||||
* <li>Uses {@link SpringBootContextLoader} as the default {@link ContextLoader} when no
|
||||
* specific {@link ContextConfiguration#loader() @ContextConfiguration(loader=...)} is
|
||||
* defined.</li>
|
||||
* <li>Automatically searches for a
|
||||
* {@link SpringBootConfiguration @SpringBootConfiguration} when nested
|
||||
* {@code @Configuration} is not used, and no explicit {@link #classes() classes} are
|
||||
* specified.</li>
|
||||
* <li>Allows custom {@link Environment} properties to be defined using the
|
||||
* {@link #properties() properties attribute}.</li>
|
||||
* <li>Provides support for different {@link #webEnvironment() webEnvironment} modes,
|
||||
* including the ability to start a fully running web server listening on a
|
||||
* {@link WebEnvironment#DEFINED_PORT defined} or {@link WebEnvironment#RANDOM_PORT
|
||||
* random} port.</li>
|
||||
* <li>Registers a {@link org.springframework.boot.test.web.client.TestRestTemplate
|
||||
* TestRestTemplate} bean for use in web tests that are using a fully running web server.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.4.0
|
||||
* @see ContextConfiguration
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@BootstrapWith(SpringBootTestContextBootstrapper.class)
|
||||
public @interface SpringBootTest {
|
||||
|
||||
/**
|
||||
* Alias for {@link #properties()}.
|
||||
* @return the properties to apply
|
||||
*/
|
||||
@AliasFor("properties")
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* Properties in form {@literal key=value} that should be added to the Spring
|
||||
* {@link Environment} before the test runs.
|
||||
* @return the properties to add
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String[] properties() default {};
|
||||
|
||||
/**
|
||||
* The <em>annotated classes</em> to use for loading an
|
||||
* {@link org.springframework.context.ApplicationContext ApplicationContext}. Can also
|
||||
* be specified using
|
||||
* {@link ContextConfiguration#classes() @ContextConfiguration(classes=...)}. If no
|
||||
* explicit classes are defined the test will look for nested
|
||||
* {@link Configuration @Configuration} classes, before falling back to a
|
||||
* {@link SpringBootConfiguration} search.
|
||||
* @see ContextConfiguration#classes()
|
||||
* @return the annotated classes used to load the application context
|
||||
*/
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
/**
|
||||
* The type of web environment to create when applicable. Defaults to
|
||||
* {@link WebEnvironment#MOCK}.
|
||||
* @return the type of web environment
|
||||
*/
|
||||
WebEnvironment webEnvironment() default WebEnvironment.MOCK;
|
||||
|
||||
/**
|
||||
* An enumeration web environment modes.
|
||||
*/
|
||||
enum WebEnvironment {
|
||||
|
||||
/**
|
||||
* Creates a {@link WebApplicationContext} with a mock servlet environment if
|
||||
* servlet APIs are on the classpath, a {@link ReactiveWebApplicationContext} if
|
||||
* Spring WebFlux is on the classpath or a regular {@link ApplicationContext}
|
||||
* otherwise.
|
||||
*/
|
||||
MOCK(false),
|
||||
|
||||
/**
|
||||
* Creates a web application context (reactive or servlet based) and sets a
|
||||
* {@code server.port=0} {@link Environment} property (which usually triggers
|
||||
* listening on a random port). Often used in conjunction with a
|
||||
* {@link LocalServerPort} injected field on the test.
|
||||
*/
|
||||
RANDOM_PORT(true),
|
||||
|
||||
/**
|
||||
* Creates a (reactive) web application context without defining any
|
||||
* {@code server.port=0} {@link Environment} property.
|
||||
*/
|
||||
DEFINED_PORT(true),
|
||||
|
||||
/**
|
||||
* Creates an {@link ApplicationContext} and sets
|
||||
* {@link SpringApplication#setWebApplicationType(WebApplicationType)} to
|
||||
* {@link WebApplicationType#NONE}.
|
||||
*/
|
||||
NONE(false);
|
||||
|
||||
private final boolean embedded;
|
||||
|
||||
WebEnvironment(boolean embedded) {
|
||||
this.embedded = embedded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the environment uses an {@link ServletWebServerApplicationContext}.
|
||||
* @return if an {@link ServletWebServerApplicationContext} is used.
|
||||
*/
|
||||
public boolean isEmbedded() {
|
||||
return this.embedded;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestContextBootstrapper;
|
||||
import org.springframework.test.context.TestExecutionListener;
|
||||
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.context.web.WebMergedContextConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link TestContextBootstrapper} for Spring Boot. Provides support for
|
||||
* {@link SpringBootTest @SpringBootTest} and may also be used directly or subclassed.
|
||||
* Provides the following features over and above {@link DefaultTestContextBootstrapper}:
|
||||
* <ul>
|
||||
* <li>Uses {@link SpringBootContextLoader} as the
|
||||
* {@link #getDefaultContextLoaderClass(Class) default context loader}.</li>
|
||||
* <li>Automatically searches for a
|
||||
* {@link SpringBootConfiguration @SpringBootConfiguration} when required.</li>
|
||||
* <li>Allows custom {@link Environment} {@link #getProperties(Class)} to be defined.</li>
|
||||
* <li>Provides support for different {@link WebEnvironment webEnvironment} modes.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Brian Clozel
|
||||
* @author Madhura Bhave
|
||||
* @since 1.4.0
|
||||
* @see SpringBootTest
|
||||
* @see TestConfiguration
|
||||
*/
|
||||
public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstrapper {
|
||||
|
||||
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
|
||||
"org.springframework.web.context.ConfigurableWebApplicationContext" };
|
||||
|
||||
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
|
||||
+ "web.reactive.DispatcherHandler";
|
||||
|
||||
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
|
||||
+ "web.servlet.DispatcherServlet";
|
||||
|
||||
private static final String ACTIVATE_SERVLET_LISTENER = "org.springframework.test."
|
||||
+ "context.web.ServletTestExecutionListener.activateListener";
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(SpringBootTestContextBootstrapper.class);
|
||||
|
||||
@Override
|
||||
public TestContext buildTestContext() {
|
||||
TestContext context = super.buildTestContext();
|
||||
verifyConfiguration(context.getTestClass());
|
||||
WebEnvironment webEnvironment = getWebEnvironment(context.getTestClass());
|
||||
if (webEnvironment == WebEnvironment.MOCK
|
||||
&& deduceWebApplicationType() == WebApplicationType.SERVLET) {
|
||||
context.setAttribute(ACTIVATE_SERVLET_LISTENER, true);
|
||||
}
|
||||
else if (webEnvironment != null && webEnvironment.isEmbedded()) {
|
||||
context.setAttribute(ACTIVATE_SERVLET_LISTENER, false);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Class<? extends TestExecutionListener>> getDefaultTestExecutionListenerClasses() {
|
||||
Set<Class<? extends TestExecutionListener>> listeners = super.getDefaultTestExecutionListenerClasses();
|
||||
List<DefaultTestExecutionListenersPostProcessor> postProcessors = SpringFactoriesLoader
|
||||
.loadFactories(DefaultTestExecutionListenersPostProcessor.class,
|
||||
getClass().getClassLoader());
|
||||
for (DefaultTestExecutionListenersPostProcessor postProcessor : postProcessors) {
|
||||
listeners = postProcessor.postProcessDefaultTestExecutionListeners(listeners);
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContextLoader resolveContextLoader(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributesList) {
|
||||
Class<?>[] classes = getClasses(testClass);
|
||||
if (!ObjectUtils.isEmpty(classes)) {
|
||||
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
|
||||
addConfigAttributesClasses(configAttributes, classes);
|
||||
}
|
||||
}
|
||||
return super.resolveContextLoader(testClass, configAttributesList);
|
||||
}
|
||||
|
||||
private void addConfigAttributesClasses(
|
||||
ContextConfigurationAttributes configAttributes, Class<?>[] classes) {
|
||||
List<Class<?>> combined = new ArrayList<>();
|
||||
combined.addAll(Arrays.asList(classes));
|
||||
if (configAttributes.getClasses() != null) {
|
||||
combined.addAll(Arrays.asList(configAttributes.getClasses()));
|
||||
}
|
||||
configAttributes.setClasses(combined.toArray(new Class<?>[combined.size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends ContextLoader> getDefaultContextLoaderClass(
|
||||
Class<?> testClass) {
|
||||
return SpringBootContextLoader.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MergedContextConfiguration processMergedContextConfiguration(
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
Class<?>[] classes = getOrFindConfigurationClasses(mergedConfig);
|
||||
List<String> propertySourceProperties = getAndProcessPropertySourceProperties(
|
||||
mergedConfig);
|
||||
mergedConfig = createModifiedConfig(mergedConfig, classes,
|
||||
propertySourceProperties
|
||||
.toArray(new String[propertySourceProperties.size()]));
|
||||
WebEnvironment webEnvironment = getWebEnvironment(mergedConfig.getTestClass());
|
||||
if (webEnvironment != null && isWebEnvironmentSupported(mergedConfig)) {
|
||||
WebApplicationType webApplicationType = getWebApplicationType(mergedConfig);
|
||||
if (webApplicationType == WebApplicationType.SERVLET
|
||||
&& (webEnvironment.isEmbedded()
|
||||
|| webEnvironment == WebEnvironment.MOCK)) {
|
||||
WebAppConfiguration webAppConfiguration = AnnotatedElementUtils
|
||||
.findMergedAnnotation(mergedConfig.getTestClass(),
|
||||
WebAppConfiguration.class);
|
||||
String resourceBasePath = (webAppConfiguration == null ? "src/main/webapp"
|
||||
: webAppConfiguration.value());
|
||||
mergedConfig = new WebMergedContextConfiguration(mergedConfig,
|
||||
resourceBasePath);
|
||||
}
|
||||
else if (webApplicationType == WebApplicationType.REACTIVE
|
||||
&& (webEnvironment.isEmbedded()
|
||||
|| webEnvironment == WebEnvironment.MOCK)) {
|
||||
return new ReactiveWebMergedContextConfiguration(mergedConfig);
|
||||
}
|
||||
}
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
private WebApplicationType getWebApplicationType(
|
||||
MergedContextConfiguration configuration) {
|
||||
ConfigurationPropertySource source = new MapConfigurationPropertySource(
|
||||
TestPropertySourceUtils.convertInlinedPropertiesToMap(
|
||||
configuration.getPropertySourceProperties()));
|
||||
Binder binder = new Binder(source);
|
||||
return binder
|
||||
.bind("spring.main.web-application-type",
|
||||
Bindable.of(WebApplicationType.class))
|
||||
.orElseGet(this::deduceWebApplicationType);
|
||||
}
|
||||
|
||||
private WebApplicationType deduceWebApplicationType() {
|
||||
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
|
||||
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
|
||||
return WebApplicationType.REACTIVE;
|
||||
}
|
||||
for (String className : WEB_ENVIRONMENT_CLASSES) {
|
||||
if (!ClassUtils.isPresent(className, null)) {
|
||||
return WebApplicationType.NONE;
|
||||
}
|
||||
}
|
||||
return WebApplicationType.SERVLET;
|
||||
}
|
||||
|
||||
private boolean isWebEnvironmentSupported(MergedContextConfiguration mergedConfig) {
|
||||
Class<?> testClass = mergedConfig.getTestClass();
|
||||
ContextHierarchy hierarchy = AnnotationUtils.getAnnotation(testClass,
|
||||
ContextHierarchy.class);
|
||||
if (hierarchy == null || hierarchy.value().length == 0) {
|
||||
return true;
|
||||
}
|
||||
ContextConfiguration[] configurations = hierarchy.value();
|
||||
return isFromConfiguration(mergedConfig,
|
||||
configurations[configurations.length - 1]);
|
||||
}
|
||||
|
||||
private boolean isFromConfiguration(MergedContextConfiguration candidateConfig,
|
||||
ContextConfiguration configuration) {
|
||||
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(
|
||||
candidateConfig.getTestClass(), configuration);
|
||||
Set<Class<?>> configurationClasses = new HashSet<>(
|
||||
Arrays.asList(attributes.getClasses()));
|
||||
for (Class<?> candidate : candidateConfig.getClasses()) {
|
||||
if (configurationClasses.contains(candidate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected Class<?>[] getOrFindConfigurationClasses(
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
Class<?>[] classes = mergedConfig.getClasses();
|
||||
if (containsNonTestComponent(classes) || mergedConfig.hasLocations()) {
|
||||
return classes;
|
||||
}
|
||||
Class<?> found = new SpringBootConfigurationFinder()
|
||||
.findFromClass(mergedConfig.getTestClass());
|
||||
Assert.state(found != null,
|
||||
"Unable to find a @SpringBootConfiguration, you need to use "
|
||||
+ "@ContextConfiguration or @SpringBootTest(classes=...) "
|
||||
+ "with your test");
|
||||
logger.info("Found @SpringBootConfiguration " + found.getName() + " for test "
|
||||
+ mergedConfig.getTestClass());
|
||||
return merge(found, classes);
|
||||
}
|
||||
|
||||
private boolean containsNonTestComponent(Class<?>[] classes) {
|
||||
for (Class<?> candidate : classes) {
|
||||
if (!AnnotatedElementUtils.isAnnotated(candidate, TestConfiguration.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Class<?>[] merge(Class<?> head, Class<?>[] existing) {
|
||||
Class<?>[] result = new Class<?>[existing.length + 1];
|
||||
result[0] = head;
|
||||
System.arraycopy(existing, 0, result, 1, existing.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<String> getAndProcessPropertySourceProperties(
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
List<String> propertySourceProperties = new ArrayList<>(
|
||||
Arrays.asList(mergedConfig.getPropertySourceProperties()));
|
||||
String differentiator = getDifferentiatorPropertySourceProperty();
|
||||
if (differentiator != null) {
|
||||
propertySourceProperties.add(differentiator);
|
||||
}
|
||||
processPropertySourceProperties(mergedConfig, propertySourceProperties);
|
||||
return propertySourceProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a "differentiator" property to ensure that there is something to
|
||||
* differentiate regular tests and bootstrapped tests. Without this property a cached
|
||||
* context could be returned that wasn't created by this bootstrapper. By default uses
|
||||
* the bootstrapper class as a property.
|
||||
* @return the differentiator or {@code null}
|
||||
*/
|
||||
protected String getDifferentiatorPropertySourceProperty() {
|
||||
return getClass().getName() + "=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Post process the property source properties, adding or removing elements as
|
||||
* required.
|
||||
* @param mergedConfig the merged context configuration
|
||||
* @param propertySourceProperties the property source properties to process
|
||||
*/
|
||||
protected void processPropertySourceProperties(
|
||||
MergedContextConfiguration mergedConfig,
|
||||
List<String> propertySourceProperties) {
|
||||
Class<?> testClass = mergedConfig.getTestClass();
|
||||
String[] properties = getProperties(testClass);
|
||||
if (!ObjectUtils.isEmpty(properties)) {
|
||||
// Added first so that inlined properties from @TestPropertySource take
|
||||
// precedence
|
||||
propertySourceProperties.addAll(0, Arrays.asList(properties));
|
||||
}
|
||||
if (getWebEnvironment(testClass) == WebEnvironment.RANDOM_PORT) {
|
||||
propertySourceProperties.add("server.port=0");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link WebEnvironment} type for this test or null if undefined.
|
||||
* @param testClass the source test class
|
||||
* @return the {@link WebEnvironment} or {@code null}
|
||||
*/
|
||||
protected WebEnvironment getWebEnvironment(Class<?> testClass) {
|
||||
SpringBootTest annotation = getAnnotation(testClass);
|
||||
return (annotation == null ? null : annotation.webEnvironment());
|
||||
}
|
||||
|
||||
protected Class<?>[] getClasses(Class<?> testClass) {
|
||||
SpringBootTest annotation = getAnnotation(testClass);
|
||||
return (annotation == null ? null : annotation.classes());
|
||||
}
|
||||
|
||||
protected String[] getProperties(Class<?> testClass) {
|
||||
SpringBootTest annotation = getAnnotation(testClass);
|
||||
return (annotation == null ? null : annotation.properties());
|
||||
}
|
||||
|
||||
protected SpringBootTest getAnnotation(Class<?> testClass) {
|
||||
return AnnotatedElementUtils.getMergedAnnotation(testClass, SpringBootTest.class);
|
||||
}
|
||||
|
||||
protected void verifyConfiguration(Class<?> testClass) {
|
||||
SpringBootTest springBootTest = getAnnotation(testClass);
|
||||
if (springBootTest != null
|
||||
&& (springBootTest.webEnvironment() == WebEnvironment.DEFINED_PORT
|
||||
|| springBootTest.webEnvironment() == WebEnvironment.RANDOM_PORT)
|
||||
&& getAnnotation(WebAppConfiguration.class, testClass) != null) {
|
||||
throw new IllegalStateException("@WebAppConfiguration should only be used "
|
||||
+ "with @SpringBootTest when @SpringBootTest is configured with a "
|
||||
+ "mock web environment. Please remove @WebAppConfiguration or "
|
||||
+ "reconfigure @SpringBootTest.");
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Annotation> T getAnnotation(Class<T> annotationType,
|
||||
Class<?> testClass) {
|
||||
return AnnotatedElementUtils.getMergedAnnotation(testClass, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MergedContextConfiguration} with different classes.
|
||||
* @param mergedConfig the source config
|
||||
* @param classes the replacement classes
|
||||
* @return a new {@link MergedContextConfiguration}
|
||||
*/
|
||||
protected final MergedContextConfiguration createModifiedConfig(
|
||||
MergedContextConfiguration mergedConfig, Class<?>[] classes) {
|
||||
return createModifiedConfig(mergedConfig, classes,
|
||||
mergedConfig.getPropertySourceProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link MergedContextConfiguration} with different classes and
|
||||
* properties.
|
||||
* @param mergedConfig the source config
|
||||
* @param classes the replacement classes
|
||||
* @param propertySourceProperties the replacement properties
|
||||
* @return a new {@link MergedContextConfiguration}
|
||||
*/
|
||||
protected final MergedContextConfiguration createModifiedConfig(
|
||||
MergedContextConfiguration mergedConfig, Class<?>[] classes,
|
||||
String[] propertySourceProperties) {
|
||||
return new MergedContextConfiguration(mergedConfig.getTestClass(),
|
||||
mergedConfig.getLocations(), classes,
|
||||
mergedConfig.getContextInitializerClasses(),
|
||||
mergedConfig.getActiveProfiles(),
|
||||
mergedConfig.getPropertySourceLocations(), propertySourceProperties,
|
||||
mergedConfig.getContextCustomizers(), mergedConfig.getContextLoader(),
|
||||
getCacheAwareContextLoaderDelegate(), mergedConfig.getParent());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.test.web.client.LocalHostUriTemplateHandler;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate.HttpClientOption;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizer} for {@link SpringBootTest}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SpringBootTestContextCustomizer implements ContextCustomizer {
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedContextConfiguration) {
|
||||
SpringBootTest annotation = AnnotatedElementUtils.getMergedAnnotation(
|
||||
mergedContextConfiguration.getTestClass(), SpringBootTest.class);
|
||||
if (annotation.webEnvironment().isEmbedded()) {
|
||||
registerTestRestTemplate(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTestRestTemplate(ConfigurableApplicationContext context) {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
registerTestRestTemplate(context, (BeanDefinitionRegistry) context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void registerTestRestTemplate(ConfigurableApplicationContext context,
|
||||
BeanDefinitionRegistry registry) {
|
||||
registry.registerBeanDefinition(TestRestTemplate.class.getName(),
|
||||
new RootBeanDefinition(TestRestTemplateFactory.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} used to create and configure a {@link TestRestTemplate}.
|
||||
*/
|
||||
public static class TestRestTemplateFactory
|
||||
implements FactoryBean<TestRestTemplate>, ApplicationContextAware {
|
||||
|
||||
private static final HttpClientOption[] DEFAULT_OPTIONS = {};
|
||||
|
||||
private static final HttpClientOption[] SSL_OPTIONS = { HttpClientOption.SSL };
|
||||
|
||||
private TestRestTemplate object;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
RestTemplateBuilder builder = getRestTemplateBuilder(applicationContext);
|
||||
boolean sslEnabled = isSslEnabled(applicationContext);
|
||||
TestRestTemplate template = new TestRestTemplate(builder.build(), null, null,
|
||||
sslEnabled ? SSL_OPTIONS : DEFAULT_OPTIONS);
|
||||
LocalHostUriTemplateHandler handler = new LocalHostUriTemplateHandler(
|
||||
applicationContext.getEnvironment(), sslEnabled ? "https" : "http");
|
||||
template.setUriTemplateHandler(handler);
|
||||
this.object = template;
|
||||
}
|
||||
|
||||
private boolean isSslEnabled(ApplicationContext context) {
|
||||
try {
|
||||
AbstractServletWebServerFactory webServerFactory = context
|
||||
.getBean(AbstractServletWebServerFactory.class);
|
||||
return webServerFactory.getSsl() != null
|
||||
&& webServerFactory.getSsl().isEnabled();
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private RestTemplateBuilder getRestTemplateBuilder(
|
||||
ApplicationContext applicationContext) {
|
||||
try {
|
||||
return applicationContext.getBean(RestTemplateBuilder.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return new RestTemplateBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return TestRestTemplate.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestRestTemplate getObject() throws Exception {
|
||||
return this.object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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} for {@link SpringBootTest}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @see SpringBootTestContextCustomizer
|
||||
*/
|
||||
class SpringBootTestContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
if (AnnotatedElementUtils.findMergedAnnotation(testClass,
|
||||
SpringBootTest.class) != null) {
|
||||
return new SpringBootTestContextCustomizer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
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.context.TypeExcludeFilter;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link Component @Component} that can be used when a bean is intended only for tests,
|
||||
* and should be excluded from Spring Boot's component scanning.
|
||||
* <p>
|
||||
* Note that if you directly use {@link ComponentScan @ComponentScan} rather than relying
|
||||
* on {@code @SpringBootApplication} you should ensure that a {@link TypeExcludeFilter} is
|
||||
* declared as an {@link ComponentScan#excludeFilters() excludeFilter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see TypeExcludeFilter
|
||||
* @see TestConfiguration
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
public @interface TestComponent {
|
||||
|
||||
/**
|
||||
* The value may indicate a suggestion for a logical component name, to be turned into
|
||||
* a Spring bean in case of an auto-detected component.
|
||||
* @return the specified bean name, if any
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
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.SpringBootConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link Configuration @Configuration} that can be used to define additional beans or
|
||||
* customizations for a test. Unlike regular {@code @Configuration} classes the use of
|
||||
* {@code @TestConfiguration} does not prevent auto-detection of
|
||||
* {@link SpringBootConfiguration @SpringBootConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see SpringBootTestContextBootstrapper
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Configuration
|
||||
@TestComponent
|
||||
public @interface TestConfiguration {
|
||||
|
||||
/**
|
||||
* Explicitly specify the name of the Spring bean definition associated with this
|
||||
* Configuration class. See {@link Configuration#value()} for details.
|
||||
* @return the specified bean name, if any
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
/*
|
||||
* 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.context.assertj;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.assertj.core.api.AbstractObjectArrayAssert;
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
import org.assertj.core.api.AbstractThrowableAssert;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.assertj.core.api.MapAssert;
|
||||
import org.assertj.core.error.BasicErrorMessageFactory;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied to an
|
||||
* {@link ApplicationContext}.
|
||||
*
|
||||
* @param <C> The application context type
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
* @see ApplicationContextRunner
|
||||
* @see AssertableApplicationContext
|
||||
*/
|
||||
public class ApplicationContextAssert<C extends ApplicationContext>
|
||||
extends AbstractAssert<ApplicationContextAssert<C>, C> {
|
||||
|
||||
private final Throwable startupFailure;
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationContextAssert} instance.
|
||||
* @param applicationContext the source application context
|
||||
* @param startupFailure the startup failure or {@code null}
|
||||
*/
|
||||
ApplicationContextAssert(C applicationContext, Throwable startupFailure) {
|
||||
super(applicationContext, ApplicationContextAssert.class);
|
||||
Assert.notNull(applicationContext, "ApplicationContext must not be null");
|
||||
this.startupFailure = startupFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the application context contains a bean with the given name.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).hasBean("fooBean"); </pre>
|
||||
* @param name the name of the bean
|
||||
* @return {@code this} assertion object.
|
||||
* @throws AssertionError if the application context did not start
|
||||
* @throws AssertionError if the application context does not contain a bean with the
|
||||
* given name
|
||||
*/
|
||||
public ApplicationContextAssert<C> hasBean(String name) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"to have bean named:%n <%s>", name));
|
||||
}
|
||||
if (findBean(name) == null) {
|
||||
throwAssertionError(new BasicErrorMessageFactory(
|
||||
"%nExpecting:%n <%s>%nto have bean named:%n <%s>%nbut found no such bean",
|
||||
getApplicationContext(), name));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the application context contains a single bean with the given type.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).hasSingleBean(Foo.class); </pre>
|
||||
* @param type the bean type
|
||||
* @return {@code this} assertion object.
|
||||
* @throws AssertionError if the application context did not start
|
||||
* @throws AssertionError if the application context does no beans of the given type
|
||||
* @throws AssertionError if the application context contains multiple beans of the
|
||||
* given type
|
||||
*/
|
||||
public ApplicationContextAssert<C> hasSingleBean(Class<?> type) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"to have a single bean of type:%n <%s>", type));
|
||||
}
|
||||
String[] names = getApplicationContext().getBeanNamesForType(type);
|
||||
if (names.length == 0) {
|
||||
throwAssertionError(new BasicErrorMessageFactory(
|
||||
"%nExpecting:%n <%s>%nto have a single bean of type:%n <%s>%nbut found no beans of that type",
|
||||
getApplicationContext(), type));
|
||||
}
|
||||
if (names.length > 1) {
|
||||
throwAssertionError(new BasicErrorMessageFactory(
|
||||
"%nExpecting:%n <%s>%nto have a single bean of type:%n <%s>%nbut found:%n <%s>",
|
||||
getApplicationContext(), type, names));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the application context does not contain any beans of the given type.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).doesNotHaveBean(Foo.class); </pre>
|
||||
* @param type the bean type
|
||||
* @return {@code this} assertion object.
|
||||
* @throws AssertionError if the application context did not start
|
||||
* @throws AssertionError if the application context contains any beans of the given
|
||||
* type
|
||||
*/
|
||||
public ApplicationContextAssert<C> doesNotHaveBean(Class<?> type) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"not to have any beans of type:%n <%s>", type));
|
||||
}
|
||||
String[] names = getApplicationContext().getBeanNamesForType(type);
|
||||
if (names.length > 0) {
|
||||
throwAssertionError(new BasicErrorMessageFactory(
|
||||
"%nExpecting:%n <%s>%nnot to have a beans of type:%n <%s>%nbut found:%n <%s>",
|
||||
getApplicationContext(), type, names));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the application context does not contain a beans of the given name.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).doesNotHaveBean("fooBean"); </pre>
|
||||
* @param name the name of the bean
|
||||
* @return {@code this} assertion object.
|
||||
* @throws AssertionError if the application context did not start
|
||||
* @throws AssertionError if the application context contains a beans of the given
|
||||
* name
|
||||
*/
|
||||
public ApplicationContextAssert<C> doesNotHaveBean(String name) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"not to have any beans of name:%n <%s>", name));
|
||||
}
|
||||
try {
|
||||
Object bean = getApplicationContext().getBean(name);
|
||||
throwAssertionError(new BasicErrorMessageFactory(
|
||||
"%nExpecting:%n <%s>%nnot to have a bean of name:%n <%s>%nbut found:%n <%s>",
|
||||
getApplicationContext(), name, bean));
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the beans names of the given type from the application context, the names
|
||||
* becoming the object array under test.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).getBeanNames(Foo.class).containsOnly("fooBean"); </pre>
|
||||
* @param <T> the bean type
|
||||
* @param type the bean type
|
||||
* @return array assertions for the bean names
|
||||
* @throws AssertionError if the application context did not start
|
||||
*/
|
||||
public <T> AbstractObjectArrayAssert<?, String> getBeanNames(Class<T> type) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"to get beans names with type:%n <%s>", type));
|
||||
}
|
||||
return Assertions.assertThat(getApplicationContext().getBeanNamesForType(type))
|
||||
.as("Bean names of type <%s> from <%s>", type, getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a single bean of the given type from the application context, the bean
|
||||
* becoming the object under test. If no beans of the specified type can be found an
|
||||
* assert on {@code null} is returned.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).getBean(Foo.class).isInstanceOf(DefaultFoo.class);
|
||||
* assertThat(context).getBean(Bar.class).isNull();</pre>
|
||||
* @param <T> the bean type
|
||||
* @param type the bean type
|
||||
* @return bean assertions for the bean, or an assert on {@code null} if the no bean
|
||||
* is found
|
||||
* @throws AssertionError if the application context did not start
|
||||
* @throws AssertionError if the application context contains multiple beans of the
|
||||
* given type
|
||||
*/
|
||||
public <T> AbstractObjectAssert<?, T> getBean(Class<T> type) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"to contain bean of type:%n <%s>", type));
|
||||
}
|
||||
String[] names = getApplicationContext().getBeanNamesForType(type);
|
||||
if (names.length > 1) {
|
||||
throwAssertionError(new BasicErrorMessageFactory(
|
||||
"%nExpecting:%n <%s>%nsingle bean of type:%n <%s>%nbut found:%n <%s>",
|
||||
getApplicationContext(), type, names));
|
||||
}
|
||||
T bean = (names.length == 0 ? null
|
||||
: getApplicationContext().getBean(names[0], type));
|
||||
return Assertions.assertThat(bean).as("Bean of type <%s> from <%s>", type,
|
||||
getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a single bean of the given name from the application context, the bean
|
||||
* becoming the object under test. If no bean of the specified name can be found an
|
||||
* assert on {@code null} is returned.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).getBean("foo").isInstanceOf(Foo.class);
|
||||
* assertThat(context).getBean("foo").isNull();</pre>
|
||||
* @param name the name of the bean
|
||||
* @return bean assertions for the bean, or an assert on {@code null} if the no bean
|
||||
* is found
|
||||
* @throws AssertionError if the application context did not start
|
||||
*/
|
||||
public AbstractObjectAssert<?, Object> getBean(String name) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"to contain a bean of name:%n <%s>", name));
|
||||
}
|
||||
Object bean = findBean(name);
|
||||
return Assertions.assertThat(bean).as("Bean of name <%s> from <%s>", name,
|
||||
getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a single bean of the given name and type from the application context, the
|
||||
* bean becoming the object under test. If no bean of the specified name can be found
|
||||
* an assert on {@code null} is returned.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).getBean("foo", Foo.class).isInstanceOf(DefaultFoo.class);
|
||||
* assertThat(context).getBean("foo", Foo.class).isNull();</pre>
|
||||
* @param <T> the bean type
|
||||
* @param name the name of the bean
|
||||
* @param type the bean type
|
||||
* @return bean assertions for the bean, or an assert on {@code null} if the no bean
|
||||
* is found
|
||||
* @throws AssertionError if the application context did not start
|
||||
* @throws AssertionError if the application context contains a bean with the given
|
||||
* name but a different type
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> AbstractObjectAssert<?, T> getBean(String name, Class<T> type) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"to contain a bean of name:%n <%s> (%s)", name, type));
|
||||
}
|
||||
Object bean = findBean(name);
|
||||
if (bean != null && type != null && !type.isInstance(bean)) {
|
||||
throwAssertionError(new BasicErrorMessageFactory(
|
||||
"%nExpecting:%n <%s>%nto contain a bean of name:%n <%s> (%s)%nbut found:%n <%s> of type <%s>",
|
||||
getApplicationContext(), name, type, bean, bean.getClass()));
|
||||
}
|
||||
return Assertions.assertThat((T) bean).as(
|
||||
"Bean of name <%s> and type <%s> from <%s>", name, type,
|
||||
getApplicationContext());
|
||||
}
|
||||
|
||||
private Object findBean(String name) {
|
||||
try {
|
||||
return getApplicationContext().getBean(name);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a map bean names and instances of the given type from the application
|
||||
* context, the map becoming the object under test. If no bean of the specified type
|
||||
* can be found an assert on an empty {@code map} is returned.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).getBeans(Foo.class).containsKey("foo");
|
||||
* </pre>
|
||||
* @param <T> the bean type
|
||||
* @param type the bean type
|
||||
* @return bean assertions for the beans, or an assert on an empty {@code map} if the
|
||||
* no beans are found
|
||||
* @throws AssertionError if the application context did not start
|
||||
*/
|
||||
public <T> MapAssert<String, T> getBeans(Class<T> type) {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting(
|
||||
"to get beans of type:%n <%s>", type));
|
||||
}
|
||||
return Assertions.assertThat(getApplicationContext().getBeansOfType(type))
|
||||
.as("Beans of type <%s> from <%s>", type, getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the failure that stopped the application context from running, the failure
|
||||
* becoming the object under test.
|
||||
* <p>
|
||||
* Example: <pre class="code">
|
||||
* assertThat(context).getFailure().containsMessage("missing bean");
|
||||
* </pre>
|
||||
* @return assertions on the cause of the failure
|
||||
* @throws AssertionError if the application context started without a failure
|
||||
*/
|
||||
public AbstractThrowableAssert<?, ? extends Throwable> getFailure() {
|
||||
hasFailed();
|
||||
return assertThat(this.startupFailure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the application has failed to start.
|
||||
* <p>
|
||||
* Example: <pre class="code"> assertThat(context).hasFailed();
|
||||
* </pre>
|
||||
* @return {@code this} assertion object.
|
||||
* @throws AssertionError if the application context started without a failure
|
||||
*/
|
||||
public ApplicationContextAssert<C> hasFailed() {
|
||||
if (this.startupFailure == null) {
|
||||
throwAssertionError(new BasicErrorMessageFactory(
|
||||
"%nExpecting:%n <%s>%nto have failed%nbut context started successfully",
|
||||
getApplicationContext()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the application has not failed to start.
|
||||
* <p>
|
||||
* Example: <pre class="code"> assertThat(context).hasNotFailed();
|
||||
* </pre>
|
||||
* @return {@code this} assertion object.
|
||||
* @throws AssertionError if the application context failed to start
|
||||
*/
|
||||
public ApplicationContextAssert<C> hasNotFailed() {
|
||||
if (this.startupFailure != null) {
|
||||
throwAssertionError(contextFailedToStartWhenExpecting("to have not failed"));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected final C getApplicationContext() {
|
||||
return this.actual;
|
||||
}
|
||||
|
||||
protected final Throwable getStartupFailure() {
|
||||
return this.startupFailure;
|
||||
}
|
||||
|
||||
private ContextFailedToStart<C> contextFailedToStartWhenExpecting(
|
||||
String expectationFormat, Object... arguments) {
|
||||
return new ContextFailedToStart<>(getApplicationContext(), this.startupFailure,
|
||||
expectationFormat, arguments);
|
||||
}
|
||||
|
||||
private static final class ContextFailedToStart<C extends ApplicationContext>
|
||||
extends BasicErrorMessageFactory {
|
||||
|
||||
private ContextFailedToStart(C context, Throwable ex, String expectationFormat,
|
||||
Object... arguments) {
|
||||
super("%nExpecting:%n <%s>%n" + expectationFormat
|
||||
+ ":%nbut context failed to start:%n%s",
|
||||
combineArguments(context.toString(), ex, arguments));
|
||||
}
|
||||
|
||||
private static Object[] combineArguments(String context, Throwable ex,
|
||||
Object[] arguments) {
|
||||
Object[] combinedArguments = new Object[arguments.length + 2];
|
||||
combinedArguments[0] = unquotedString(context);
|
||||
System.arraycopy(arguments, 0, combinedArguments, 1, arguments.length);
|
||||
combinedArguments[combinedArguments.length - 1] = unquotedString(
|
||||
getIndentedStackTraceAsString(ex));
|
||||
return combinedArguments;
|
||||
}
|
||||
|
||||
private static String getIndentedStackTraceAsString(Throwable ex) {
|
||||
String stackTrace = getStackTraceAsString(ex);
|
||||
return indent(stackTrace);
|
||||
}
|
||||
|
||||
private static String getStackTraceAsString(Throwable ex) {
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintWriter printer = new PrintWriter(writer);
|
||||
ex.printStackTrace(printer);
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
private static String indent(String input) {
|
||||
BufferedReader reader = new BufferedReader(new StringReader(input));
|
||||
StringWriter writer = new StringWriter();
|
||||
PrintWriter printer = new PrintWriter(writer);
|
||||
try {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
printer.print(" ");
|
||||
printer.println(line);
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.context.assertj;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationContext} that additionally supports AssertJ style assertions. Can
|
||||
* be used to decorate and existing application context or an application context that
|
||||
* failed to start.
|
||||
* <p>
|
||||
* Assertions can be applied using the standard AssertJ {@code assertThat(...)} style (see
|
||||
* {@link ApplicationContextAssert} for a complete list). For example: <pre class="code">
|
||||
* assertThat(applicationContext).hasSingleBean(MyBean.class);
|
||||
* </pre>
|
||||
* <p>
|
||||
* If the original {@link ApplicationContext} is needed for any reason the
|
||||
* {@link #getSourceApplicationContext()} method can be used.
|
||||
* <p>
|
||||
* Any {@link ApplicationContext} method called on a context that has failed to start will
|
||||
* throw an {@link IllegalStateException}.
|
||||
*
|
||||
* @param <C> The application context type
|
||||
* @author Phillip Webb
|
||||
* @see AssertableApplicationContext
|
||||
* @see AssertableWebApplicationContext
|
||||
* @see AssertableReactiveWebApplicationContext
|
||||
* @see ApplicationContextAssert
|
||||
*/
|
||||
public interface ApplicationContextAssertProvider<C extends ApplicationContext> extends
|
||||
ApplicationContext, AssertProvider<ApplicationContextAssert<C>>, Closeable {
|
||||
|
||||
/**
|
||||
* Return an assert for AspectJ.
|
||||
* @return an AspectJ assert
|
||||
* @deprecated use standard AssertJ {@code assertThat(context)...} calls instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
ApplicationContextAssert<C> assertThat();
|
||||
|
||||
/**
|
||||
* Return the original source {@link ApplicationContext}.
|
||||
* @return the source application context
|
||||
* @throws IllegalStateException if the source context failed to start
|
||||
*/
|
||||
C getSourceApplicationContext();
|
||||
|
||||
/**
|
||||
* Return the original source {@link ApplicationContext}, casting it to the requested
|
||||
* type.
|
||||
* @param <T> the context type
|
||||
* @param requiredType the required context type
|
||||
* @return the source application context
|
||||
* @throws IllegalStateException if the source context failed to start
|
||||
*/
|
||||
<T extends C> T getSourceApplicationContext(Class<T> requiredType);
|
||||
|
||||
/**
|
||||
* Return the failure that caused application context to fail or {@code null} if the
|
||||
* context started without issue.
|
||||
* @return the startup failure or {@code null}
|
||||
*/
|
||||
Throwable getStartupFailure();
|
||||
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link ApplicationContextAssertProvider} instance.
|
||||
* @param <T> the assert provider type
|
||||
* @param <C> the context type
|
||||
* @param type the type of {@link ApplicationContextAssertProvider} required (must be
|
||||
* an interface)
|
||||
* @param contextType the type of {@link ApplicationContext} being managed (must be an
|
||||
* interface)
|
||||
* @param contextSupplier a supplier that will either return a fully configured
|
||||
* {@link ApplicationContext} or throw an exception if the context fails to start.
|
||||
* @return a {@link ApplicationContextAssertProvider} instance
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends ApplicationContextAssertProvider<C>, C extends ApplicationContext> T get(
|
||||
Class<T> type, Class<? extends C> contextType,
|
||||
Supplier<? extends C> contextSupplier) {
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
Assert.isTrue(type.isInterface(), "Type must be an interface");
|
||||
Assert.notNull(contextType, "ContextType must not be null");
|
||||
Assert.isTrue(contextType.isInterface(), "ContextType must be an interface");
|
||||
Class<?>[] interfaces = { type, contextType };
|
||||
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
|
||||
interfaces, new AssertProviderApplicationContextInvocationHandler(
|
||||
contextType, contextSupplier));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.context.assertj;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link InvocationHandler} used by {@link ApplicationContextAssertProvider} generated
|
||||
* proxies.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class AssertProviderApplicationContextInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Class<?> applicationContextType;
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final RuntimeException startupFailure;
|
||||
|
||||
AssertProviderApplicationContextInvocationHandler(Class<?> applicationContextType,
|
||||
Supplier<?> contextSupplier) {
|
||||
this.applicationContextType = applicationContextType;
|
||||
Object contextOrStartupFailure = getContextOrStartupFailure(contextSupplier);
|
||||
if (contextOrStartupFailure instanceof RuntimeException) {
|
||||
this.applicationContext = null;
|
||||
this.startupFailure = (RuntimeException) contextOrStartupFailure;
|
||||
}
|
||||
else {
|
||||
this.applicationContext = (ApplicationContext) contextOrStartupFailure;
|
||||
this.startupFailure = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Object getContextOrStartupFailure(Supplier<?> contextSupplier) {
|
||||
try {
|
||||
return contextSupplier.get();
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
return ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (isToString(method)) {
|
||||
return toString();
|
||||
}
|
||||
if (isGetSourceContext(method)) {
|
||||
return getSourceContext(args);
|
||||
}
|
||||
if (isGetStartupFailure(method)) {
|
||||
return getStartupFailure();
|
||||
}
|
||||
if (isAssertThat(method)) {
|
||||
return getAssertThat(proxy);
|
||||
}
|
||||
if (isCloseMethod(method)) {
|
||||
return invokeClose();
|
||||
}
|
||||
return invokeApplicationContextMethod(method, args);
|
||||
}
|
||||
|
||||
private boolean isToString(Method method) {
|
||||
return ("toString".equals(method.getName()) && method.getParameterCount() == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.startupFailure != null) {
|
||||
return "Unstarted application context "
|
||||
+ this.applicationContextType.getName() + "[startupFailure="
|
||||
+ this.startupFailure.getClass().getName() + "]";
|
||||
}
|
||||
ToStringBuilder builder = new ToStringBuilder(this.applicationContext)
|
||||
.append("id", this.applicationContext.getId())
|
||||
.append("applicationName", this.applicationContext.getApplicationName())
|
||||
.append("beanDefinitionCount",
|
||||
this.applicationContext.getBeanDefinitionCount());
|
||||
return "Started application " + builder;
|
||||
}
|
||||
|
||||
private boolean isGetSourceContext(Method method) {
|
||||
return "getSourceApplicationContext".equals(method.getName())
|
||||
&& ((method.getParameterCount() == 0) || Arrays.equals(
|
||||
new Class<?>[] { Class.class }, method.getParameterTypes()));
|
||||
}
|
||||
|
||||
private Object getSourceContext(Object[] args) {
|
||||
ApplicationContext context = getStartedApplicationContext();
|
||||
if (!ObjectUtils.isEmpty(args)) {
|
||||
Assert.isInstanceOf((Class<?>) args[0], context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
private boolean isGetStartupFailure(Method method) {
|
||||
return ("getStartupFailure".equals(method.getName())
|
||||
&& method.getParameterCount() == 0);
|
||||
}
|
||||
|
||||
private Object getStartupFailure() {
|
||||
return this.startupFailure;
|
||||
}
|
||||
|
||||
private boolean isAssertThat(Method method) {
|
||||
return ("assertThat".equals(method.getName()) && method.getParameterCount() == 0);
|
||||
}
|
||||
|
||||
private Object getAssertThat(Object proxy) {
|
||||
return new ApplicationContextAssert<>((ApplicationContext) proxy,
|
||||
this.startupFailure);
|
||||
}
|
||||
|
||||
private boolean isCloseMethod(Method method) {
|
||||
return ("close".equals(method.getName()) && method.getParameterCount() == 0);
|
||||
}
|
||||
|
||||
private Object invokeClose() throws IOException {
|
||||
if (this.applicationContext instanceof Closeable) {
|
||||
((Closeable) this.applicationContext).close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object invokeApplicationContextMethod(Method method, Object[] args)
|
||||
throws Throwable {
|
||||
try {
|
||||
return method.invoke(getStartedApplicationContext(), args);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
|
||||
private ApplicationContext getStartedApplicationContext() {
|
||||
if (this.startupFailure != null) {
|
||||
throw new IllegalStateException(toString() + " failed to start",
|
||||
this.startupFailure);
|
||||
}
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.context.assertj;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationContext} that additionally supports AssertJ style assertions. Can
|
||||
* be used to decorate and existing application context or an application context that
|
||||
* failed to start.
|
||||
* <p>
|
||||
* See {@link ApplicationContextAssertProvider} for more details.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see ApplicationContextRunner
|
||||
* @see ApplicationContext
|
||||
*/
|
||||
public interface AssertableApplicationContext
|
||||
extends ApplicationContextAssertProvider<ConfigurableApplicationContext> {
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link AssertableApplicationContext} instance.
|
||||
* @param contextSupplier a supplier that will either return a fully configured
|
||||
* {@link ConfigurableApplicationContext} or throw an exception if the context fails
|
||||
* to start.
|
||||
* @return an {@link AssertableApplicationContext} instance
|
||||
*/
|
||||
static AssertableApplicationContext get(
|
||||
Supplier<? extends ConfigurableApplicationContext> contextSupplier) {
|
||||
return ApplicationContextAssertProvider.get(AssertableApplicationContext.class,
|
||||
ConfigurableApplicationContext.class, contextSupplier);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context.assertj;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveWebApplicationContext} that additionally supports AssertJ style
|
||||
* assertions. Can be used to decorate and existing reactive web application context or an
|
||||
* application context that failed to start.
|
||||
* <p>
|
||||
* See {@link ApplicationContextAssertProvider} for more details.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see ReactiveWebApplicationContext
|
||||
* @see ReactiveWebApplicationContext
|
||||
*/
|
||||
public interface AssertableReactiveWebApplicationContext extends
|
||||
ApplicationContextAssertProvider<ConfigurableReactiveWebApplicationContext>,
|
||||
ReactiveWebApplicationContext {
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link AssertableReactiveWebApplicationContext}
|
||||
* instance.
|
||||
* @param contextSupplier a supplier that will either return a fully configured
|
||||
* {@link ConfigurableReactiveWebApplicationContext} or throw an exception if the
|
||||
* context fails to start.
|
||||
* @return a {@link AssertableReactiveWebApplicationContext} instance
|
||||
*/
|
||||
static AssertableReactiveWebApplicationContext get(
|
||||
Supplier<? extends ConfigurableReactiveWebApplicationContext> contextSupplier) {
|
||||
return ApplicationContextAssertProvider.get(
|
||||
AssertableReactiveWebApplicationContext.class,
|
||||
ConfigurableReactiveWebApplicationContext.class, contextSupplier);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.context.assertj;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link WebApplicationContext} that additionally supports AssertJ style assertions.
|
||||
* Can be used to decorate and existing servlet web application context or an application
|
||||
* context that failed to start.
|
||||
* <p>
|
||||
* See {@link ApplicationContextAssertProvider} for more details.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see WebApplicationContextRunner
|
||||
* @see WebApplicationContext
|
||||
*/
|
||||
public interface AssertableWebApplicationContext
|
||||
extends ApplicationContextAssertProvider<ConfigurableWebApplicationContext>,
|
||||
WebApplicationContext {
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link AssertableWebApplicationContext} instance.
|
||||
* @param contextSupplier a supplier that will either return a fully configured
|
||||
* {@link ConfigurableWebApplicationContext} or throw an exception if the context
|
||||
* fails to start.
|
||||
* @return a {@link AssertableWebApplicationContext} instance
|
||||
*/
|
||||
static AssertableWebApplicationContext get(
|
||||
Supplier<? extends ConfigurableWebApplicationContext> contextSupplier) {
|
||||
return ApplicationContextAssertProvider.get(AssertableWebApplicationContext.class,
|
||||
ConfigurableWebApplicationContext.class, contextSupplier);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* AssertJ support for ApplicationContexts.
|
||||
*/
|
||||
package org.springframework.boot.test.context.assertj;
|
||||
@@ -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.context.filter;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizer} to add the {@link TestTypeExcludeFilter} to the
|
||||
* {@link ApplicationContext}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ExcludeFilterContextCustomizer implements ContextCustomizer {
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedContextConfiguration) {
|
||||
context.getBeanFactory().registerSingleton(TestTypeExcludeFilter.class.getName(),
|
||||
new TestTypeExcludeFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.context.filter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} to add the {@link TestTypeExcludeFilter} to the
|
||||
* {@link ApplicationContext}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ExcludeFilterContextCustomizer
|
||||
*/
|
||||
class ExcludeFilterContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
return new ExcludeFilterContextCustomizer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.boot.context.TypeExcludeFilter;
|
||||
import org.springframework.boot.test.context.TestComponent;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
|
||||
/**
|
||||
* {@link TypeExcludeFilter} to exclude classes annotated with {@link TestComponent} as
|
||||
* well as inner-classes of tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class TestTypeExcludeFilter extends TypeExcludeFilter {
|
||||
|
||||
private static final String[] CLASS_ANNOTATIONS = { "org.junit.runner.RunWith",
|
||||
"org.junit.jupiter.api.extension.ExtendWith" };
|
||||
|
||||
private static final String[] METHOD_ANNOTATIONS = { "org.junit.Test",
|
||||
"org.junit.platform.commons.annotation.Testable", };
|
||||
|
||||
@Override
|
||||
public boolean match(MetadataReader metadataReader,
|
||||
MetadataReaderFactory metadataReaderFactory) throws IOException {
|
||||
if (isTestConfiguration(metadataReader)) {
|
||||
return true;
|
||||
}
|
||||
if (isTestClass(metadataReader)) {
|
||||
return true;
|
||||
}
|
||||
String enclosing = metadataReader.getClassMetadata().getEnclosingClassName();
|
||||
if (enclosing != null) {
|
||||
try {
|
||||
if (match(metadataReaderFactory.getMetadataReader(enclosing),
|
||||
metadataReaderFactory)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isTestConfiguration(MetadataReader metadataReader) {
|
||||
return (metadataReader.getAnnotationMetadata()
|
||||
.isAnnotated(TestComponent.class.getName()));
|
||||
}
|
||||
|
||||
private boolean isTestClass(MetadataReader metadataReader) {
|
||||
for (String annotation : CLASS_ANNOTATIONS) {
|
||||
if (metadataReader.getAnnotationMetadata().hasAnnotation(annotation)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
for (String annotation : METHOD_ANNOTATIONS) {
|
||||
if (metadataReader.getAnnotationMetadata().hasAnnotatedMethods(annotation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 support for {@link org.springframework.boot.context.TypeExcludeFilter}.
|
||||
*/
|
||||
package org.springframework.boot.test.context.filter;
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Classes and annotations related to configuring Spring's {@code ApplicationContext} for
|
||||
* tests.
|
||||
*/
|
||||
package org.springframework.boot.test.context;
|
||||
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* 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.context.runner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.context.annotation.Configurations;
|
||||
import org.springframework.boot.context.annotation.UserConfigurations;
|
||||
import org.springframework.boot.test.context.HidePackagesClassLoader;
|
||||
import org.springframework.boot.test.context.assertj.ApplicationContextAssert;
|
||||
import org.springframework.boot.test.context.assertj.ApplicationContextAssertProvider;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility design to run and an {@link ApplicationContext} and provide AssertJ style
|
||||
* assertions. The test is best used as a field of a test class, describing the shared
|
||||
* configuration required for the test:
|
||||
*
|
||||
* <pre class="code">
|
||||
* public class MyContextTests {
|
||||
* private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
* .withPropertyValues("spring.foo=bar")
|
||||
* .withUserConfiguration(MyConfiguration.class);
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* The initialization above makes sure to register {@code MyConfiguration} for all tests
|
||||
* and set the {@code spring.foo} property to {@code bar} unless specified otherwise.
|
||||
* <p>
|
||||
* Based on the configuration above, a specific test can simulate what will happen when
|
||||
* the context runs, perhaps with overridden property values:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Test
|
||||
* public someTest() {
|
||||
* this.contextRunner.withPropertyValues("spring.foo=biz").run((context) -> {
|
||||
* assertThat(context).containsSingleBean(MyBean.class);
|
||||
* // other assertions
|
||||
* });
|
||||
* }</pre>
|
||||
* <p>
|
||||
* The test above has changed the {@code spring.foo} property to {@code biz} and is
|
||||
* asserting that the context contains a single {@code MyBean} bean. The
|
||||
* {@link #run(ContextConsumer) run} method takes a {@link ContextConsumer} that can apply
|
||||
* assertions to the context. Upon completion, the context is automatically closed.
|
||||
* <p>
|
||||
* If the application context fails to start the {@code #run(ContextConsumer)} method is
|
||||
* called with a "failed" application context. Calls to the context will throw an
|
||||
* {@link IllegalStateException} and assertions that expect a running context will fail.
|
||||
* The {@link ApplicationContextAssert#getFailure() getFailure()} assertion can be used if
|
||||
* further checks are required on the cause of the failure: <pre class="code">
|
||||
* @Test
|
||||
* public someTest() {
|
||||
* this.context.withPropertyValues("spring.foo=fails").run((loaded) -> {
|
||||
* assertThat(loaded).getFailure().hasCauseInstanceOf(BadPropertyException.class);
|
||||
* // other assertions
|
||||
* });
|
||||
* }</pre>
|
||||
* <p>
|
||||
*
|
||||
* @param <SELF> The "self" type for this runner
|
||||
* @param <C> The context type
|
||||
* @param <A> The application context assertion provider
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see ApplicationContextRunner
|
||||
* @see WebApplicationContextRunner
|
||||
* @see ReactiveWebApplicationContextRunner
|
||||
* @see ApplicationContextAssert
|
||||
*/
|
||||
abstract class AbstractApplicationContextRunner<SELF extends AbstractApplicationContextRunner<SELF, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> {
|
||||
|
||||
private final Supplier<C> contextFactory;
|
||||
|
||||
private final TestPropertyValues environmentProperties;
|
||||
|
||||
private final TestPropertyValues systemProperties;
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final ApplicationContext parent;
|
||||
|
||||
private final List<Configurations> configurations;
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractApplicationContextRunner} instance.
|
||||
* @param contextFactory the factory used to create the actual context
|
||||
*/
|
||||
protected AbstractApplicationContextRunner(Supplier<C> contextFactory) {
|
||||
this(contextFactory, TestPropertyValues.empty(), TestPropertyValues.empty(), null,
|
||||
null, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractApplicationContextRunner} instance.
|
||||
* @param contextFactory the factory used to create the actual context
|
||||
* @param environmentProperties the environment properties
|
||||
* @param systemProperties the system properties
|
||||
* @param classLoader the class loader
|
||||
* @param parent the parent
|
||||
* @param configurations the configuration
|
||||
*/
|
||||
protected AbstractApplicationContextRunner(Supplier<C> contextFactory,
|
||||
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
|
||||
ClassLoader classLoader, ApplicationContext parent,
|
||||
List<Configurations> configurations) {
|
||||
Assert.notNull(contextFactory, "ContextFactory must not be null");
|
||||
Assert.notNull(environmentProperties, "EnvironmentProperties must not be null");
|
||||
Assert.notNull(systemProperties, "SystemProperties must not be null");
|
||||
Assert.notNull(configurations, "Configurations must not be null");
|
||||
this.contextFactory = contextFactory;
|
||||
this.environmentProperties = environmentProperties;
|
||||
this.systemProperties = systemProperties;
|
||||
this.classLoader = classLoader;
|
||||
this.parent = parent;
|
||||
this.configurations = Collections.unmodifiableList(configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified {@link Environment} property pairs. Key-value pairs can be
|
||||
* specified with colon (":") or equals ("=") separators. Override matching keys that
|
||||
* might have been specified previously.
|
||||
* @param pairs the key-value pairs for properties that need to be added to the
|
||||
* environment
|
||||
* @return a new instance with the updated property values
|
||||
* @see TestPropertyValues
|
||||
* @see #withSystemProperties(String...)
|
||||
*/
|
||||
public SELF withPropertyValues(String... pairs) {
|
||||
return newInstance(this.contextFactory, this.environmentProperties.and(pairs),
|
||||
this.systemProperties, this.classLoader, this.parent,
|
||||
this.configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified {@link System} property pairs. Key-value pairs can be specified
|
||||
* with colon (":") or equals ("=") separators. System properties are added before the
|
||||
* context is {@link #run(ContextConsumer) run} and restored when the context is
|
||||
* closed.
|
||||
* @param pairs the key-value pairs for properties that need to be added to the system
|
||||
* @return a new instance with the updated system properties
|
||||
* @see TestPropertyValues
|
||||
* @see #withSystemProperties(String...)
|
||||
*/
|
||||
public SELF withSystemProperties(String... pairs) {
|
||||
return newInstance(this.contextFactory, this.environmentProperties,
|
||||
this.systemProperties.and(pairs), this.classLoader, this.parent,
|
||||
this.configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the {@link ClassLoader} that the {@link ApplicationContext} should use.
|
||||
* Customizing the {@link ClassLoader} is an effective manner to hide resources from
|
||||
* the classpath.
|
||||
* @param classLoader the classloader to use (can be null to use the default)
|
||||
* @return a new instance with the updated class loader
|
||||
* @see HidePackagesClassLoader
|
||||
*/
|
||||
public SELF withClassLoader(ClassLoader classLoader) {
|
||||
return newInstance(this.contextFactory, this.environmentProperties,
|
||||
this.systemProperties, classLoader, this.parent, this.configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the {@link ConfigurableApplicationContext#setParent(ApplicationContext)
|
||||
* parent} of the {@link ApplicationContext}.
|
||||
* @param parent the parent
|
||||
* @return a new instance with the updated parent
|
||||
*/
|
||||
public SELF withParent(ApplicationContext parent) {
|
||||
return newInstance(this.contextFactory, this.environmentProperties,
|
||||
this.systemProperties, this.classLoader, parent, this.configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified user configuration classes with the
|
||||
* {@link ApplicationContext}.
|
||||
* @param configurationClasses the user configuration classes to add
|
||||
* @return a new instance with the updated configuration
|
||||
*/
|
||||
public SELF withUserConfiguration(Class<?>... configurationClasses) {
|
||||
return withConfiguration(UserConfigurations.of(configurationClasses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified configuration classes with the {@link ApplicationContext}.
|
||||
* @param configurations the configurations to add
|
||||
* @return a new instance with the updated configuration
|
||||
*/
|
||||
public SELF withConfiguration(Configurations configurations) {
|
||||
Assert.notNull(configurations, "Configurations must not be null");
|
||||
return newInstance(this.contextFactory, this.environmentProperties,
|
||||
this.systemProperties, this.classLoader, this.parent,
|
||||
add(this.configurations, configurations));
|
||||
}
|
||||
|
||||
private <T> List<T> add(List<T> list, T element) {
|
||||
List<T> result = new ArrayList<>(list);
|
||||
result.add(element);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract SELF newInstance(Supplier<C> contextFactory,
|
||||
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
|
||||
ClassLoader classLoader, ApplicationContext parent,
|
||||
List<Configurations> configurations);
|
||||
|
||||
/**
|
||||
* Create and refresh a new {@link ApplicationContext} based on the current state of
|
||||
* this loader. The context is consumed by the specified {@code consumer} and closed
|
||||
* upon completion.
|
||||
* @param consumer the consumer of the created {@link ApplicationContext}
|
||||
* @return this instance
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public SELF run(ContextConsumer<? super A> consumer) {
|
||||
this.systemProperties.applyToSystemProperties(() -> {
|
||||
try (A context = createAssertableContext()) {
|
||||
accept(consumer, context);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return (SELF) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private A createAssertableContext() {
|
||||
ResolvableType resolvableType = ResolvableType
|
||||
.forClass(AbstractApplicationContextRunner.class, getClass());
|
||||
Class<A> assertType = (Class<A>) resolvableType.resolveGeneric(1);
|
||||
Class<C> contextType = (Class<C>) resolvableType.resolveGeneric(2);
|
||||
return ApplicationContextAssertProvider.get(assertType, contextType,
|
||||
this::createAndLoadContext);
|
||||
}
|
||||
|
||||
private C createAndLoadContext() {
|
||||
C context = this.contextFactory.get();
|
||||
try {
|
||||
configureContext(context);
|
||||
return context;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
context.close();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private void configureContext(C context) {
|
||||
if (this.parent != null) {
|
||||
context.setParent(this.parent);
|
||||
}
|
||||
if (this.classLoader != null) {
|
||||
Assert.isInstanceOf(DefaultResourceLoader.class, context);
|
||||
((DefaultResourceLoader) context).setClassLoader(this.classLoader);
|
||||
}
|
||||
this.environmentProperties.applyTo(context);
|
||||
Class<?>[] classes = Configurations.getClasses(this.configurations);
|
||||
if (classes.length > 0) {
|
||||
((AnnotationConfigRegistry) context).register(classes);
|
||||
}
|
||||
context.refresh();
|
||||
}
|
||||
|
||||
private void accept(ContextConsumer<? super A> consumer, A context) {
|
||||
try {
|
||||
consumer.accept(context);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
rethrow(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <E extends Throwable> void rethrow(Throwable e) throws E {
|
||||
throw (E) e;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.context.runner;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.context.annotation.Configurations;
|
||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link AbstractApplicationContextRunner ApplicationContext runner} for a standard,
|
||||
* non-web environment {@link ConfigurableApplicationContext}.
|
||||
* <p>
|
||||
* See {@link AbstractApplicationContextRunner} for details.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class ApplicationContextRunner extends
|
||||
AbstractApplicationContextRunner<ApplicationContextRunner, ConfigurableApplicationContext, AssertableApplicationContext> {
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationContextRunner} instance using an
|
||||
* {@link AnnotationConfigApplicationContext} as the underlying source.
|
||||
*/
|
||||
public ApplicationContextRunner() {
|
||||
this(AnnotationConfigApplicationContext::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationContextRunner} instance using the specified
|
||||
* {@code contextFactory} as the underlying source.
|
||||
* @param contextFactory a supplier that returns a new instance on each call
|
||||
*/
|
||||
public ApplicationContextRunner(
|
||||
Supplier<ConfigurableApplicationContext> contextFactory) {
|
||||
super(contextFactory);
|
||||
}
|
||||
|
||||
private ApplicationContextRunner(
|
||||
Supplier<ConfigurableApplicationContext> contextFactory,
|
||||
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
|
||||
ClassLoader classLoader, ApplicationContext parent,
|
||||
List<Configurations> configurations) {
|
||||
super(contextFactory, environmentProperties, systemProperties, classLoader,
|
||||
parent, configurations);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ApplicationContextRunner newInstance(
|
||||
Supplier<ConfigurableApplicationContext> contextFactory,
|
||||
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
|
||||
ClassLoader classLoader, ApplicationContext parent,
|
||||
List<Configurations> configurations) {
|
||||
return new ApplicationContextRunner(contextFactory, environmentProperties,
|
||||
systemProperties, classLoader, parent, configurations);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context.runner;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Callback interface used to process an {@link ApplicationContext} with the ability to
|
||||
* throw a (checked) exception.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @param <C> The application context type
|
||||
* @since 2.0.0
|
||||
* @see AbstractApplicationContextRunner
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ContextConsumer<C extends ApplicationContext> {
|
||||
|
||||
/**
|
||||
* Performs this operation on the supplied {@code context}.
|
||||
* @param context the application context to consume
|
||||
* @throws Throwable any exception that might occur in assertions
|
||||
*/
|
||||
void accept(C context) throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.context.runner;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.context.annotation.Configurations;
|
||||
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link AbstractApplicationContextRunner ApplicationContext runner} for a
|
||||
* {@link ConfigurableReactiveWebApplicationContext}.
|
||||
* <p>
|
||||
* See {@link AbstractApplicationContextRunner} for details.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class ReactiveWebApplicationContextRunner extends
|
||||
AbstractApplicationContextRunner<ReactiveWebApplicationContextRunner, ConfigurableReactiveWebApplicationContext, AssertableReactiveWebApplicationContext> {
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveWebApplicationContextRunner} instance using a
|
||||
* {@link GenericReactiveWebApplicationContext} as the underlying source.
|
||||
*/
|
||||
public ReactiveWebApplicationContextRunner() {
|
||||
this(GenericReactiveWebApplicationContext::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationContextRunner} instance using the specified
|
||||
* {@code contextFactory} as the underlying source.
|
||||
* @param contextFactory a supplier that returns a new instance on each call
|
||||
*/
|
||||
public ReactiveWebApplicationContextRunner(
|
||||
Supplier<ConfigurableReactiveWebApplicationContext> contextFactory) {
|
||||
super(contextFactory);
|
||||
}
|
||||
|
||||
private ReactiveWebApplicationContextRunner(
|
||||
Supplier<ConfigurableReactiveWebApplicationContext> contextFactory,
|
||||
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
|
||||
ClassLoader classLoader, ApplicationContext parent,
|
||||
List<Configurations> configurations) {
|
||||
super(contextFactory, environmentProperties, systemProperties, classLoader,
|
||||
parent, configurations);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactiveWebApplicationContextRunner newInstance(
|
||||
Supplier<ConfigurableReactiveWebApplicationContext> contextFactory,
|
||||
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
|
||||
ClassLoader classLoader, ApplicationContext parent,
|
||||
List<Configurations> configurations) {
|
||||
return new ReactiveWebApplicationContextRunner(contextFactory,
|
||||
environmentProperties, systemProperties, classLoader, parent,
|
||||
configurations);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.context.runner;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.context.annotation.Configurations;
|
||||
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link AbstractApplicationContextRunner ApplicationContext runner} for a Servlet
|
||||
* based {@link ConfigurableWebApplicationContext}.
|
||||
* <p>
|
||||
* See {@link AbstractApplicationContextRunner} for details.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class WebApplicationContextRunner extends
|
||||
AbstractApplicationContextRunner<WebApplicationContextRunner, ConfigurableWebApplicationContext, AssertableWebApplicationContext> {
|
||||
|
||||
/**
|
||||
* Create a new {@link WebApplicationContextRunner} instance using an
|
||||
* {@link AnnotationConfigWebApplicationContext} with a {@link MockServletContext} as
|
||||
* the underlying source.
|
||||
* @see #withMockServletContext(Supplier)
|
||||
*/
|
||||
public WebApplicationContextRunner() {
|
||||
this(withMockServletContext(AnnotationConfigWebApplicationContext::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link WebApplicationContextRunner} instance using the specified
|
||||
* {@code contextFactory} as the underlying source.
|
||||
* @param contextFactory a supplier that returns a new instance on each call
|
||||
*/
|
||||
public WebApplicationContextRunner(
|
||||
Supplier<ConfigurableWebApplicationContext> contextFactory) {
|
||||
super(contextFactory);
|
||||
}
|
||||
|
||||
private WebApplicationContextRunner(
|
||||
Supplier<ConfigurableWebApplicationContext> contextFactory,
|
||||
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
|
||||
ClassLoader classLoader, ApplicationContext parent,
|
||||
List<Configurations> configurations) {
|
||||
super(contextFactory, environmentProperties, systemProperties, classLoader,
|
||||
parent, configurations);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebApplicationContextRunner newInstance(
|
||||
Supplier<ConfigurableWebApplicationContext> contextFactory,
|
||||
TestPropertyValues environmentProperties, TestPropertyValues systemProperties,
|
||||
ClassLoader classLoader, ApplicationContext parent,
|
||||
List<Configurations> configurations) {
|
||||
return new WebApplicationContextRunner(contextFactory, environmentProperties,
|
||||
systemProperties, classLoader, parent, configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate the specified {@code contextFactory} to set a {@link MockServletContext}
|
||||
* on each newly created {@link WebApplicationContext}.
|
||||
* @param contextFactory the context factory to decorate
|
||||
* @return an updated supplier that will set the {@link MockServletContext}
|
||||
*/
|
||||
public static Supplier<ConfigurableWebApplicationContext> withMockServletContext(
|
||||
Supplier<ConfigurableWebApplicationContext> contextFactory) {
|
||||
return (contextFactory == null ? null : () -> {
|
||||
ConfigurableWebApplicationContext context = contextFactory.get();
|
||||
context.setServletContext(new MockServletContext());
|
||||
return context;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test utilities to run application contexts for testing.
|
||||
*/
|
||||
package org.springframework.boot.test.context.runner;
|
||||
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Base class for AssertJ based JSON marshal testers. Exposes specific Asserts following a
|
||||
* {@code read}, {@code write} or {@code parse} of JSON content. Typically used in
|
||||
* combination with an AssertJ {@link Assertions#assertThat(Object) assertThat} call. For
|
||||
* example: <pre class="code">
|
||||
* public class ExampleObjectJsonTests {
|
||||
*
|
||||
* private AbstractJsonTester<ExampleObject> json = //...
|
||||
*
|
||||
* @Test
|
||||
* public void testWriteJson() {
|
||||
* ExampleObject object = //...
|
||||
* assertThat(json.write(object)).isEqualToJson("expected.json");
|
||||
* assertThat(json.read("expected.json")).isEqualTo(object);
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre> For a complete list of supported assertions see {@link JsonContentAssert} and
|
||||
* {@link ObjectContentAssert}.
|
||||
* <p>
|
||||
* To use this library JSONAssert must be on the test classpath.
|
||||
*
|
||||
* @param <T> the type under test
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see JsonContentAssert
|
||||
* @see ObjectContentAssert
|
||||
*/
|
||||
public abstract class AbstractJsonMarshalTester<T> {
|
||||
|
||||
private Class<?> resourceLoadClass;
|
||||
|
||||
private ResolvableType type;
|
||||
|
||||
/**
|
||||
* Create a new uninitialized {@link AbstractJsonMarshalTester} instance.
|
||||
*/
|
||||
protected AbstractJsonMarshalTester() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractJsonMarshalTester} instance.
|
||||
* @param resourceLoadClass the source class used when loading relative classpath
|
||||
* resources
|
||||
* @param type the type under test
|
||||
*/
|
||||
public AbstractJsonMarshalTester(Class<?> resourceLoadClass, ResolvableType type) {
|
||||
Assert.notNull(resourceLoadClass, "ResourceLoadClass must not be null");
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
initialize(resourceLoadClass, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the marshal tester for use.
|
||||
* @param resourceLoadClass the source class used when loading relative classpath
|
||||
* resources
|
||||
* @param type the type under test
|
||||
*/
|
||||
protected final void initialize(Class<?> resourceLoadClass, ResolvableType type) {
|
||||
if (this.resourceLoadClass == null && this.type == null) {
|
||||
this.resourceLoadClass = resourceLoadClass;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type under test.
|
||||
* @return the type under test
|
||||
*/
|
||||
protected final ResolvableType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return class used to load relative resources.
|
||||
* @return the resource load class
|
||||
*/
|
||||
protected final Class<?> getResourceLoadClass() {
|
||||
return this.resourceLoadClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link JsonContent} from writing the specific value.
|
||||
* @param value the value to write
|
||||
* @return the {@link JsonContent}
|
||||
* @throws IOException on write error
|
||||
*/
|
||||
public JsonContent<T> write(T value) throws IOException {
|
||||
verify();
|
||||
Assert.notNull(value, "Value must not be null");
|
||||
String json = writeObject(value, this.type);
|
||||
return new JsonContent<>(this.resourceLoadClass, this.type, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object created from parsing the specific JSON bytes.
|
||||
* @param jsonBytes the source JSON bytes
|
||||
* @return the resulting object
|
||||
* @throws IOException on parse error
|
||||
*/
|
||||
public T parseObject(byte[] jsonBytes) throws IOException {
|
||||
verify();
|
||||
return parse(jsonBytes).getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ObjectContent} from parsing the specific JSON bytes.
|
||||
* @param jsonBytes the source JSON bytes
|
||||
* @return the {@link ObjectContent}
|
||||
* @throws IOException on parse error
|
||||
*/
|
||||
public ObjectContent<T> parse(byte[] jsonBytes) throws IOException {
|
||||
verify();
|
||||
Assert.notNull(jsonBytes, "JsonBytes must not be null");
|
||||
return read(new ByteArrayResource(jsonBytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object created from parsing the specific JSON String.
|
||||
* @param jsonString the source JSON string
|
||||
* @return the resulting object
|
||||
* @throws IOException on parse error
|
||||
*/
|
||||
public T parseObject(String jsonString) throws IOException {
|
||||
verify();
|
||||
return parse(jsonString).getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ObjectContent} from parsing the specific JSON String.
|
||||
* @param jsonString the source JSON string
|
||||
* @return the {@link ObjectContent}
|
||||
* @throws IOException on parse error
|
||||
*/
|
||||
public ObjectContent<T> parse(String jsonString) throws IOException {
|
||||
verify();
|
||||
Assert.notNull(jsonString, "JsonString must not be null");
|
||||
return read(new StringReader(jsonString));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object created from reading from the specified classpath resource.
|
||||
* @param resourcePath the source resource path. May be a full path or a path relative
|
||||
* to the {@code resourceLoadClass} passed to the constructor
|
||||
* @return the resulting object
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public T readObject(String resourcePath) throws IOException {
|
||||
verify();
|
||||
return read(resourcePath).getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ObjectContent} from reading from the specified classpath resource.
|
||||
* @param resourcePath the source resource path. May be a full path or a path relative
|
||||
* to the {@code resourceLoadClass} passed to the constructor
|
||||
* @return the {@link ObjectContent}
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public ObjectContent<T> read(String resourcePath) throws IOException {
|
||||
verify();
|
||||
Assert.notNull(resourcePath, "ResourcePath must not be null");
|
||||
return read(new ClassPathResource(resourcePath, this.resourceLoadClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object created from reading from the specified file.
|
||||
* @param file the source file
|
||||
* @return the resulting object
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public T readObject(File file) throws IOException {
|
||||
verify();
|
||||
return read(file).getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ObjectContent} from reading from the specified file.
|
||||
* @param file the source file
|
||||
* @return the {@link ObjectContent}
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public ObjectContent<T> read(File file) throws IOException {
|
||||
verify();
|
||||
Assert.notNull(file, "File must not be null");
|
||||
return read(new FileSystemResource(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object created from reading from the specified input stream.
|
||||
* @param inputStream the source input stream
|
||||
* @return the resulting object
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public T readObject(InputStream inputStream) throws IOException {
|
||||
verify();
|
||||
return read(inputStream).getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ObjectContent} from reading from the specified input stream.
|
||||
* @param inputStream the source input stream
|
||||
* @return the {@link ObjectContent}
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public ObjectContent<T> read(InputStream inputStream) throws IOException {
|
||||
verify();
|
||||
Assert.notNull(inputStream, "InputStream must not be null");
|
||||
return read(new InputStreamResource(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object created from reading from the specified resource.
|
||||
* @param resource the source resource
|
||||
* @return the resulting object
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public T readObject(Resource resource) throws IOException {
|
||||
verify();
|
||||
return read(resource).getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ObjectContent} from reading from the specified resource.
|
||||
* @param resource the source resource
|
||||
* @return the {@link ObjectContent}
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public ObjectContent<T> read(Resource resource) throws IOException {
|
||||
verify();
|
||||
Assert.notNull(resource, "Resource must not be null");
|
||||
InputStream inputStream = resource.getInputStream();
|
||||
T object = readObject(inputStream, this.type);
|
||||
closeQuietly(inputStream);
|
||||
return new ObjectContent<>(this.type, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object created from reading from the specified reader.
|
||||
* @param reader the source reader
|
||||
* @return the resulting object
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public T readObject(Reader reader) throws IOException {
|
||||
verify();
|
||||
return read(reader).getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ObjectContent} from reading from the specified reader.
|
||||
* @param reader the source reader
|
||||
* @return the {@link ObjectContent}
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
public ObjectContent<T> read(Reader reader) throws IOException {
|
||||
verify();
|
||||
Assert.notNull(reader, "Reader must not be null");
|
||||
T object = readObject(reader, this.type);
|
||||
closeQuietly(reader);
|
||||
return new ObjectContent<>(this.type, object);
|
||||
}
|
||||
|
||||
private void closeQuietly(Closeable closeable) {
|
||||
try {
|
||||
closeable.close();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
private void verify() {
|
||||
Assert.state(this.resourceLoadClass != null,
|
||||
"Uninitialized JsonMarshalTester (ResourceLoadClass is null)");
|
||||
Assert.state(this.type != null, "Uninitialized JsonMarshalTester (Type is null)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the specified object to a JSON string.
|
||||
* @param value the source value (never {@code null})
|
||||
* @param type the resulting type (never {@code null})
|
||||
* @return the JSON string
|
||||
* @throws IOException on write error
|
||||
*/
|
||||
protected abstract String writeObject(T value, ResolvableType type)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Read from the specified input stream to create an object of the specified type. The
|
||||
* default implementation delegates to {@link #readObject(Reader, ResolvableType)}.
|
||||
* @param inputStream the source input stream (never {@code null})
|
||||
* @param type the resulting type (never {@code null})
|
||||
* @return the resulting object
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
protected T readObject(InputStream inputStream, ResolvableType type)
|
||||
throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
return readObject(reader, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read from the specified reader to create an object of the specified type.
|
||||
* @param reader the source reader (never {@code null})
|
||||
* @param type the resulting type (never {@code null})
|
||||
* @return the resulting object
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
protected abstract T readObject(Reader reader, ResolvableType type)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Utility class used to support field initialization. Used by subclasses to support
|
||||
* {@code initFields}.
|
||||
*
|
||||
* @param <M> The marshaller type
|
||||
*/
|
||||
protected static abstract class FieldInitializer<M> {
|
||||
|
||||
private final Class<?> testerClass;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected FieldInitializer(
|
||||
Class<? extends AbstractJsonMarshalTester> testerClass) {
|
||||
Assert.notNull(testerClass, "TesterClass must not be null");
|
||||
this.testerClass = testerClass;
|
||||
}
|
||||
|
||||
public void initFields(final Object testInstance, final M marshaller) {
|
||||
Assert.notNull(testInstance, "TestInstance must not be null");
|
||||
Assert.notNull(marshaller, "Marshaller must not be null");
|
||||
initFields(testInstance, () -> marshaller);
|
||||
}
|
||||
|
||||
public void initFields(final Object testInstance,
|
||||
final ObjectFactory<M> marshaller) {
|
||||
Assert.notNull(testInstance, "TestInstance must not be null");
|
||||
Assert.notNull(marshaller, "Marshaller must not be null");
|
||||
ReflectionUtils.doWithFields(testInstance.getClass(),
|
||||
(field) -> doWithField(field, testInstance, marshaller));
|
||||
}
|
||||
|
||||
protected void doWithField(Field field, Object test,
|
||||
ObjectFactory<M> marshaller) {
|
||||
if (this.testerClass.isAssignableFrom(field.getType())) {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
Object existingValue = ReflectionUtils.getField(field, test);
|
||||
if (existingValue == null) {
|
||||
setupField(field, test, marshaller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupField(Field field, Object test, ObjectFactory<M> marshaller) {
|
||||
ResolvableType type = ResolvableType.forField(field).getGeneric();
|
||||
ReflectionUtils.setField(field, test,
|
||||
createTester(test.getClass(), type, marshaller.getObject()));
|
||||
}
|
||||
|
||||
protected abstract AbstractJsonMarshalTester<Object> createTester(
|
||||
Class<?> resourceLoadClass, ResolvableType type, M marshaller);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AssertJ based JSON tester that works with basic JSON strings. Allows testing of JSON
|
||||
* payloads created from any source, for example:<pre class="code">
|
||||
* public class ExampleObjectJsonTests {
|
||||
*
|
||||
* private BasicJsonTester json = new BasicJsonTester(getClass());
|
||||
*
|
||||
* @Test
|
||||
* public void testWriteJson() throws IOException {
|
||||
* assertThat(json.from("example.json")).extractingJsonPathStringValue("@.name")
|
||||
.isEqualTo("Spring");
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* See {@link AbstractJsonMarshalTester} for more details.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class BasicJsonTester {
|
||||
|
||||
private JsonLoader loader;
|
||||
|
||||
/**
|
||||
* Create a new uninitialized {@link BasicJsonTester} instance.
|
||||
*/
|
||||
protected BasicJsonTester() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicJsonTester} instance that will load resources as UTF-8.
|
||||
* @param resourceLoadClass the source class used to load resources
|
||||
*/
|
||||
public BasicJsonTester(Class<?> resourceLoadClass) {
|
||||
this(resourceLoadClass, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicJsonTester} instance.
|
||||
* @param resourceLoadClass the source class used to load resources
|
||||
* @param charset the charset used to load resources
|
||||
* @since 1.4.1
|
||||
*/
|
||||
public BasicJsonTester(Class<?> resourceLoadClass, Charset charset) {
|
||||
Assert.notNull(resourceLoadClass, "ResourceLoadClass must not be null");
|
||||
this.loader = new JsonLoader(resourceLoadClass, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the marshal tester for use, configuring it to load JSON resources as
|
||||
* UTF-8.
|
||||
* @param resourceLoadClass the source class used when loading relative classpath
|
||||
* resources
|
||||
* @param type the type under test
|
||||
*/
|
||||
protected final void initialize(Class<?> resourceLoadClass, ResolvableType type) {
|
||||
this.initialize(resourceLoadClass, null, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the marshal tester for use.
|
||||
* @param resourceLoadClass the source class used when loading relative classpath
|
||||
* resources
|
||||
* @param charset the charset used when loading relative classpath resources
|
||||
* @param type the type under test
|
||||
* @since 1.4.1
|
||||
*/
|
||||
protected final void initialize(Class<?> resourceLoadClass, Charset charset,
|
||||
ResolvableType type) {
|
||||
if (this.loader == null) {
|
||||
this.loader = new JsonLoader(resourceLoadClass, charset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JSON content from the specified String source. The source can contain the
|
||||
* JSON itself or, if it ends with {@code .json}, the name of a resource to be loaded
|
||||
* using {@code resourceLoadClass}.
|
||||
* @param source JSON content or a {@code .json} resource name
|
||||
* @return the JSON content
|
||||
*/
|
||||
public JsonContent<Object> from(CharSequence source) {
|
||||
verify();
|
||||
return getJsonContent(this.loader.getJson(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JSON content from the specified resource path.
|
||||
* @param path the path of the resource to load
|
||||
* @param resourceLoadClass the source class used to load the resource
|
||||
* @return the JSON content
|
||||
*/
|
||||
public JsonContent<Object> from(String path, Class<?> resourceLoadClass) {
|
||||
verify();
|
||||
return getJsonContent(this.loader.getJson(path, resourceLoadClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JSON content from the specified JSON bytes.
|
||||
* @param source the bytes of JSON
|
||||
* @return the JSON content
|
||||
*/
|
||||
public JsonContent<Object> from(byte[] source) {
|
||||
verify();
|
||||
return getJsonContent(this.loader.getJson(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JSON content from the specified JSON file.
|
||||
* @param source the file containing JSON
|
||||
* @return the JSON content
|
||||
*/
|
||||
public JsonContent<Object> from(File source) {
|
||||
verify();
|
||||
return getJsonContent(this.loader.getJson(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JSON content from the specified JSON input stream.
|
||||
* @param source the input stream containing JSON
|
||||
* @return the JSON content
|
||||
*/
|
||||
public JsonContent<Object> from(InputStream source) {
|
||||
verify();
|
||||
return getJsonContent(this.loader.getJson(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create JSON content from the specified JSON resource.
|
||||
* @param source the resource containing JSON
|
||||
* @return the JSON content
|
||||
*/
|
||||
public JsonContent<Object> from(Resource source) {
|
||||
verify();
|
||||
return getJsonContent(this.loader.getJson(source));
|
||||
}
|
||||
|
||||
private void verify() {
|
||||
Assert.state(this.loader != null, "Uninitialized BasicJsonTester");
|
||||
}
|
||||
|
||||
private JsonContent<Object> getJsonContent(String json) {
|
||||
return new JsonContent<>(this.loader.getResourceLoadClass(), null, json);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.json;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* A {@link ContextCustomizerFactory} that produces a {@link ContextCustomizer} that warns
|
||||
* the user when multiple occurrences of {@code JSONObject} are found on the class path.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class DuplicateJsonObjectContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
return new DuplicateJsonObjectContextCustomizer();
|
||||
}
|
||||
|
||||
private static class DuplicateJsonObjectContextCustomizer
|
||||
implements ContextCustomizer {
|
||||
|
||||
private final Log logger = LogFactory
|
||||
.getLog(DuplicateJsonObjectContextCustomizer.class);
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
List<URL> jsonObjects = findJsonObjects();
|
||||
if (jsonObjects.size() > 1) {
|
||||
logDuplicateJsonObjectsWarning(jsonObjects);
|
||||
}
|
||||
}
|
||||
|
||||
private List<URL> findJsonObjects() {
|
||||
List<URL> jsonObjects = new ArrayList<>();
|
||||
try {
|
||||
Enumeration<URL> resources = getClass().getClassLoader()
|
||||
.getResources("org/json/JSONObject.class");
|
||||
while (resources.hasMoreElements()) {
|
||||
jsonObjects.add(resources.nextElement());
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Continue
|
||||
}
|
||||
return jsonObjects;
|
||||
}
|
||||
|
||||
private void logDuplicateJsonObjectsWarning(List<URL> jsonObjects) {
|
||||
StringBuilder message = new StringBuilder("\n\nFound multiple occurrences of"
|
||||
+ " org.json.JSONObject on the class path:\n\n");
|
||||
for (URL jsonObject : jsonObjects) {
|
||||
message.append("\t" + jsonObject + "\n");
|
||||
}
|
||||
message.append("\nYou may wish to exclude one of them to ensure"
|
||||
+ " predictable runtime behaviour\n");
|
||||
this.logger.warn(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AssertJ based JSON tester backed by Gson. Usually instantiated via
|
||||
* {@link #initFields(Object, Gson)}, for example: <pre class="code">
|
||||
* public class ExampleObjectJsonTests {
|
||||
*
|
||||
* private GsonTester<ExampleObject> json;
|
||||
*
|
||||
* @Before
|
||||
* public void setup() {
|
||||
* Gson gson = new GsonBuilder().create();
|
||||
* GsonTester.initFields(this, gson);
|
||||
* }
|
||||
*
|
||||
* @Test
|
||||
* public void testWriteJson() throws IOException {
|
||||
* ExampleObject object = //...
|
||||
* assertThat(json.write(object)).isEqualToJson("expected.json");
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* See {@link AbstractJsonMarshalTester} for more details.
|
||||
*
|
||||
* @param <T> the type under test
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class GsonTester<T> extends AbstractJsonMarshalTester<T> {
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
/**
|
||||
* Create a new uninitialized {@link GsonTester} instance.
|
||||
* @param gson the Gson instance
|
||||
*/
|
||||
protected GsonTester(Gson gson) {
|
||||
Assert.notNull(gson, "Gson must not be null");
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link GsonTester} instance.
|
||||
* @param resourceLoadClass the source class used to load resources
|
||||
* @param type the type under test
|
||||
* @param gson the Gson instance
|
||||
* @see #initFields(Object, Gson)
|
||||
*/
|
||||
public GsonTester(Class<?> resourceLoadClass, ResolvableType type, Gson gson) {
|
||||
super(resourceLoadClass, type);
|
||||
Assert.notNull(gson, "Gson must not be null");
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String writeObject(T value, ResolvableType type) throws IOException {
|
||||
return this.gson.toJson(value, type.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T readObject(Reader reader, ResolvableType type) throws IOException {
|
||||
return this.gson.fromJson(reader, type.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to initialize {@link GsonTester} fields. See {@link GsonTester
|
||||
* class-level documentation} for example usage.
|
||||
* @param testInstance the test instance
|
||||
* @param gson the Gson instance
|
||||
*/
|
||||
public static void initFields(Object testInstance, Gson gson) {
|
||||
new GsonFieldInitializer().initFields(testInstance, gson);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to initialize {@link GsonTester} fields. See {@link GsonTester
|
||||
* class-level documentation} for example usage.
|
||||
* @param testInstance the test instance
|
||||
* @param gson an object factory to create the Gson instance
|
||||
*/
|
||||
public static void initFields(Object testInstance, ObjectFactory<Gson> gson) {
|
||||
new GsonFieldInitializer().initFields(testInstance, gson);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FieldInitializer} for Gson.
|
||||
*/
|
||||
private static class GsonFieldInitializer extends FieldInitializer<Gson> {
|
||||
|
||||
protected GsonFieldInitializer() {
|
||||
super(GsonTester.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJsonMarshalTester<Object> createTester(
|
||||
Class<?> resourceLoadClass, ResolvableType type, Gson marshaller) {
|
||||
return new GsonTester<>(resourceLoadClass, type, marshaller);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AssertJ based JSON tester backed by Jackson. Usually instantiated via
|
||||
* {@link #initFields(Object, ObjectMapper)}, for example: <pre class="code">
|
||||
* public class ExampleObjectJsonTests {
|
||||
*
|
||||
* private JacksonTester<ExampleObject> json;
|
||||
*
|
||||
* @Before
|
||||
* public void setup() {
|
||||
* ObjectMapper objectMapper = new ObjectMapper();
|
||||
* JacksonTester.initFields(this, objectMapper);
|
||||
* }
|
||||
*
|
||||
* @Test
|
||||
* public void testWriteJson() throws IOException {
|
||||
* ExampleObject object = //...
|
||||
* assertThat(json.write(object)).isEqualToJson("expected.json");
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* See {@link AbstractJsonMarshalTester} for more details.
|
||||
*
|
||||
* @param <T> the type under test
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class JacksonTester<T> extends AbstractJsonMarshalTester<T> {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private Class<?> view;
|
||||
|
||||
/**
|
||||
* Create a new {@link JacksonTester} instance.
|
||||
* @param objectMapper the Jackson object mapper
|
||||
*/
|
||||
protected JacksonTester(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "ObjectMapper must not be null");
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JacksonTester} instance.
|
||||
* @param resourceLoadClass the source class used to load resources
|
||||
* @param type the type under test
|
||||
* @param objectMapper the Jackson object mapper
|
||||
*/
|
||||
public JacksonTester(Class<?> resourceLoadClass, ResolvableType type,
|
||||
ObjectMapper objectMapper) {
|
||||
this(resourceLoadClass, type, objectMapper, null);
|
||||
}
|
||||
|
||||
public JacksonTester(Class<?> resourceLoadClass, ResolvableType type,
|
||||
ObjectMapper objectMapper, Class<?> view) {
|
||||
super(resourceLoadClass, type);
|
||||
Assert.notNull(objectMapper, "ObjectMapper must not be null");
|
||||
this.objectMapper = objectMapper;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T readObject(InputStream inputStream, ResolvableType type)
|
||||
throws IOException {
|
||||
return getObjectReader(type).readValue(inputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T readObject(Reader reader, ResolvableType type) throws IOException {
|
||||
return getObjectReader(type).readValue(reader);
|
||||
}
|
||||
|
||||
private ObjectReader getObjectReader(ResolvableType type) {
|
||||
ObjectReader objectReader = this.objectMapper.readerFor(getType(type));
|
||||
if (this.view != null) {
|
||||
return objectReader.withView(this.view);
|
||||
}
|
||||
return objectReader;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String writeObject(T value, ResolvableType type) throws IOException {
|
||||
return getObjectWriter(type).writeValueAsString(value);
|
||||
}
|
||||
|
||||
private ObjectWriter getObjectWriter(ResolvableType type) {
|
||||
ObjectWriter objectWriter = this.objectMapper.writerFor(getType(type));
|
||||
if (this.view != null) {
|
||||
return objectWriter.withView(this.view);
|
||||
}
|
||||
return objectWriter;
|
||||
}
|
||||
|
||||
private JavaType getType(ResolvableType type) {
|
||||
return this.objectMapper.constructType(type.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to initialize {@link JacksonTester} fields. See {@link JacksonTester
|
||||
* class-level documentation} for example usage.
|
||||
* @param testInstance the test instance
|
||||
* @param objectMapper the object mapper
|
||||
* @see #initFields(Object, ObjectMapper)
|
||||
*/
|
||||
public static void initFields(Object testInstance, ObjectMapper objectMapper) {
|
||||
new JacksonFieldInitializer().initFields(testInstance, objectMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to initialize {@link JacksonTester} fields. See {@link JacksonTester
|
||||
* class-level documentation} for example usage.
|
||||
* @param testInstance the test instance
|
||||
* @param objectMapperFactory a factory to create the object mapper
|
||||
* @see #initFields(Object, ObjectMapper)
|
||||
*/
|
||||
public static void initFields(Object testInstance,
|
||||
ObjectFactory<ObjectMapper> objectMapperFactory) {
|
||||
new JacksonFieldInitializer().initFields(testInstance, objectMapperFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance of {@link JacksonTester} with the view that should be used
|
||||
* for json serialization/deserialization.
|
||||
* @param view the view class
|
||||
* @return the new instance
|
||||
*/
|
||||
public JacksonTester<T> forView(Class<?> view) {
|
||||
return new JacksonTester<>(this.getResourceLoadClass(), this.getType(),
|
||||
this.objectMapper, view);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FieldInitializer} for Jackson.
|
||||
*/
|
||||
private static class JacksonFieldInitializer extends FieldInitializer<ObjectMapper> {
|
||||
|
||||
protected JacksonFieldInitializer() {
|
||||
super(JacksonTester.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJsonMarshalTester<Object> createTester(
|
||||
Class<?> resourceLoadClass, ResolvableType type,
|
||||
ObjectMapper marshaller) {
|
||||
return new JacksonTester<>(resourceLoadClass, type, marshaller);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* JSON content created usually from a JSON tester. Generally used only to
|
||||
* {@link AssertProvider provide} {@link JsonContentAssert} to AssertJ {@code assertThat}
|
||||
* calls.
|
||||
*
|
||||
* @param <T> the source type that created the content
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public final class JsonContent<T> implements AssertProvider<JsonContentAssert> {
|
||||
|
||||
private final Class<?> resourceLoadClass;
|
||||
|
||||
private final ResolvableType type;
|
||||
|
||||
private final String json;
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonContent} instance.
|
||||
* @param resourceLoadClass the source class used to load resources
|
||||
* @param type the type under test (or {@code null} if not known)
|
||||
* @param json the actual JSON content
|
||||
*/
|
||||
public JsonContent(Class<?> resourceLoadClass, ResolvableType type, String json) {
|
||||
Assert.notNull(resourceLoadClass, "ResourceLoadClass must not be null");
|
||||
Assert.notNull(json, "JSON must not be null");
|
||||
this.resourceLoadClass = resourceLoadClass;
|
||||
this.type = type;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use AssertJ's {@link org.assertj.core.api.Assertions#assertThat assertThat}
|
||||
* instead.
|
||||
*
|
||||
* @deprecated in favor of AssertJ's {@link org.assertj.core.api.Assertions#assertThat
|
||||
* assertThat}
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public JsonContentAssert assertThat() {
|
||||
return new JsonContentAssert(this.resourceLoadClass, this.json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the actual JSON content string.
|
||||
* @return the JSON content
|
||||
*/
|
||||
public String getJson() {
|
||||
return this.json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JsonContent " + this.json
|
||||
+ (this.type == null ? "" : " created from " + this.type);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
/**
|
||||
* Internal helper used to load JSON from various sources.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class JsonLoader {
|
||||
|
||||
private final Class<?> resourceLoadClass;
|
||||
|
||||
private final Charset charset;
|
||||
|
||||
JsonLoader(Class<?> resourceLoadClass, Charset charset) {
|
||||
this.resourceLoadClass = resourceLoadClass;
|
||||
this.charset = charset == null ? Charset.forName("UTF-8") : charset;
|
||||
}
|
||||
|
||||
Class<?> getResourceLoadClass() {
|
||||
return this.resourceLoadClass;
|
||||
}
|
||||
|
||||
String getJson(CharSequence source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (source.toString().endsWith(".json")) {
|
||||
return getJson(
|
||||
new ClassPathResource(source.toString(), this.resourceLoadClass));
|
||||
}
|
||||
return source.toString();
|
||||
}
|
||||
|
||||
String getJson(String path, Class<?> resourceLoadClass) {
|
||||
return getJson(new ClassPathResource(path, resourceLoadClass));
|
||||
}
|
||||
|
||||
String getJson(byte[] source) {
|
||||
return getJson(new ByteArrayInputStream(source));
|
||||
}
|
||||
|
||||
String getJson(File source) {
|
||||
try {
|
||||
return getJson(new FileInputStream(source));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unable to load JSON from " + source, ex);
|
||||
}
|
||||
}
|
||||
|
||||
String getJson(Resource source) {
|
||||
try {
|
||||
return getJson(source.getInputStream());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unable to load JSON from " + source, ex);
|
||||
}
|
||||
}
|
||||
|
||||
String getJson(InputStream source) {
|
||||
try {
|
||||
return FileCopyUtils
|
||||
.copyToString(new InputStreamReader(source, this.charset));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unable to load JSON from InputStream", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import javax.json.bind.Jsonb;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AssertJ based JSON tester backed by Jsonb. Usually instantiated via
|
||||
* {@link #initFields(Object, Jsonb)}, for example: <pre class="code">
|
||||
* public class ExampleObjectJsonTests {
|
||||
*
|
||||
* private JsonbTester<ExampleObject> json;
|
||||
*
|
||||
* @Before
|
||||
* public void setup() {
|
||||
* Jsonb jsonb = JsonbBuilder.create();
|
||||
* JsonbTester.initFields(this, jsonb);
|
||||
* }
|
||||
*
|
||||
* @Test
|
||||
* public void testWriteJson() throws IOException {
|
||||
* ExampleObject object = // ...
|
||||
* assertThat(json.write(object)).isEqualToJson("expected.json");
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* See {@link AbstractJsonMarshalTester} for more details.
|
||||
*
|
||||
* @param <T> the type under test
|
||||
* @author Eddú Meléndez
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class JsonbTester<T> extends AbstractJsonMarshalTester<T> {
|
||||
|
||||
private final Jsonb jsonb;
|
||||
|
||||
/**
|
||||
* Create a new uninitialized {@link JsonbTester} instance.
|
||||
* @param jsonb the Jsonb instance
|
||||
*/
|
||||
protected JsonbTester(Jsonb jsonb) {
|
||||
Assert.notNull(jsonb, "Jsonb must not be null");
|
||||
this.jsonb = jsonb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JsonbTester} instance.
|
||||
* @param resourceLoadClass the source class used to load resources
|
||||
* @param type the type under test
|
||||
* @param jsonb the Jsonb instance
|
||||
* @see #initFields(Object, Jsonb)
|
||||
*/
|
||||
public JsonbTester(Class<?> resourceLoadClass, ResolvableType type, Jsonb jsonb) {
|
||||
super(resourceLoadClass, type);
|
||||
Assert.notNull(jsonb, "Jsonb must not be null");
|
||||
this.jsonb = jsonb;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String writeObject(T value, ResolvableType type) throws IOException {
|
||||
return this.jsonb.toJson(value, type.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T readObject(Reader reader, ResolvableType type) throws IOException {
|
||||
return this.jsonb.fromJson(reader, type.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to initialize {@link JsonbTester} fields. See {@link JsonbTester
|
||||
* class-level documentation} for example usage.
|
||||
* @param testInstance the test instance
|
||||
* @param jsonb the Jsonb instance
|
||||
*/
|
||||
public static void initFields(Object testInstance, Jsonb jsonb) {
|
||||
new JsonbFieldInitializer().initFields(testInstance, jsonb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to initialize {@link JsonbTester} fields. See {@link JsonbTester
|
||||
* class-level documentation} for example usage.
|
||||
* @param testInstance the test instance
|
||||
* @param jsonb an object factory to create the Jsonb instance
|
||||
*/
|
||||
public static void initFields(Object testInstance, ObjectFactory<Jsonb> jsonb) {
|
||||
new JsonbTester.JsonbFieldInitializer().initFields(testInstance, jsonb);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FieldInitializer} for Jsonb.
|
||||
*/
|
||||
private static class JsonbFieldInitializer extends FieldInitializer<Jsonb> {
|
||||
|
||||
protected JsonbFieldInitializer() {
|
||||
super(JsonbTester.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractJsonMarshalTester<Object> createTester(
|
||||
Class<?> resourceLoadClass, ResolvableType type, Jsonb marshaller) {
|
||||
return new JsonbTester<>(resourceLoadClass, type, marshaller);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import org.assertj.core.api.AssertProvider;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Object content usually created from {@link AbstractJsonMarshalTester}. Generally used
|
||||
* only to {@link AssertProvider provide} {@link ObjectContentAssert} to AssertJ
|
||||
* {@code assertThat} calls.
|
||||
*
|
||||
* @param <T> the content type
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public final class ObjectContent<T> implements AssertProvider<ObjectContentAssert<T>> {
|
||||
|
||||
private final ResolvableType type;
|
||||
|
||||
private final T object;
|
||||
|
||||
/**
|
||||
* Create a new {@link ObjectContent} instance.
|
||||
* @param type the type under test (or {@code null} if not known)
|
||||
* @param object the actual object content
|
||||
*/
|
||||
public ObjectContent(ResolvableType type, T object) {
|
||||
Assert.notNull(object, "Object must not be null");
|
||||
this.type = type;
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectContentAssert<T> assertThat() {
|
||||
return new ObjectContentAssert<>(this.object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the actual object content.
|
||||
* @return the object content
|
||||
*/
|
||||
public T getObject() {
|
||||
return this.object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ObjectContent " + this.object
|
||||
+ (this.type == null ? "" : " created from " + this.type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.api.AbstractMapAssert;
|
||||
import org.assertj.core.api.AbstractObjectArrayAssert;
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
import org.assertj.core.api.Assert;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.assertj.core.internal.Objects;
|
||||
|
||||
/**
|
||||
* AssertJ {@link Assert} for {@link ObjectContent}.
|
||||
*
|
||||
* @param <A> The actual type
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class ObjectContentAssert<A>
|
||||
extends AbstractObjectAssert<ObjectContentAssert<A>, A> {
|
||||
|
||||
protected ObjectContentAssert(A actual) {
|
||||
super(actual, ObjectContentAssert.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the actual value is an array, and returns an array assertion, to
|
||||
* allow chaining of array-specific assertions from this call.
|
||||
* @return an array assertion object
|
||||
*/
|
||||
public AbstractObjectArrayAssert<?, Object> asArray() {
|
||||
Objects.instance().assertIsInstanceOf(this.info, this.actual, Object[].class);
|
||||
return Assertions.assertThat((Object[]) this.actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the actual value is a map, and returns a map assertion, to allow
|
||||
* chaining of map-specific assertions from this call.
|
||||
* @return a map assertion object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public AbstractMapAssert<?, ?, Object, Object> asMap() {
|
||||
Objects.instance().assertIsInstanceOf(this.info, this.actual, Map.class);
|
||||
return Assertions.assertThat((Map<Object, Object>) this.actual);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 testing JSON.
|
||||
*/
|
||||
package org.springframework.boot.test.json;
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Base class for {@link MockDefinition} and {@link SpyDefinition}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see DefinitionsParser
|
||||
*/
|
||||
abstract class Definition {
|
||||
|
||||
private static final int MULTIPLIER = 31;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final MockReset reset;
|
||||
|
||||
private final boolean proxyTargetAware;
|
||||
|
||||
private final QualifierDefinition qualifier;
|
||||
|
||||
Definition(String name, MockReset reset, boolean proxyTargetAware,
|
||||
QualifierDefinition qualifier) {
|
||||
this.name = name;
|
||||
this.reset = (reset != null ? reset : MockReset.AFTER);
|
||||
this.proxyTargetAware = proxyTargetAware;
|
||||
this.qualifier = qualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name for bean.
|
||||
* @return the name or {@code null}
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mock reset mode.
|
||||
* @return the reset mode
|
||||
*/
|
||||
public MockReset getReset() {
|
||||
return this.reset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if AOP advised beans should be proxy target aware.
|
||||
* @return if proxy target aware
|
||||
*/
|
||||
public boolean isProxyTargetAware() {
|
||||
return this.proxyTargetAware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the qualifier or {@code null}.
|
||||
* @return the qualifier
|
||||
*/
|
||||
public QualifierDefinition getQualifier() {
|
||||
return this.qualifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.name);
|
||||
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.reset);
|
||||
result = MULTIPLIER * result
|
||||
+ ObjectUtils.nullSafeHashCode(this.proxyTargetAware);
|
||||
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.qualifier);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || !getClass().isAssignableFrom(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
Definition other = (Definition) obj;
|
||||
boolean result = true;
|
||||
result = result && ObjectUtils.nullSafeEquals(this.name, other.name);
|
||||
result = result && ObjectUtils.nullSafeEquals(this.reset, other.reset);
|
||||
result = result && ObjectUtils.nullSafeEquals(this.proxyTargetAware,
|
||||
other.proxyTargetAware);
|
||||
result = result && ObjectUtils.nullSafeEquals(this.qualifier, other.qualifier);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser to create {@link MockDefinition} and {@link SpyDefinition} instances from
|
||||
* {@link MockBean @MockBean} and {@link SpyBean @SpyBean} annotations declared on or in a
|
||||
* class.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class DefinitionsParser {
|
||||
|
||||
private final Set<Definition> definitions;
|
||||
|
||||
private final Map<Definition, Field> definitionFields;
|
||||
|
||||
DefinitionsParser() {
|
||||
this(Collections.<Definition>emptySet());
|
||||
}
|
||||
|
||||
DefinitionsParser(Collection<? extends Definition> existing) {
|
||||
this.definitions = new LinkedHashSet<>();
|
||||
this.definitionFields = new LinkedHashMap<>();
|
||||
if (existing != null) {
|
||||
this.definitions.addAll(existing);
|
||||
}
|
||||
}
|
||||
|
||||
public void parse(Class<?> source) {
|
||||
parseElement(source);
|
||||
ReflectionUtils.doWithFields(source, this::parseElement);
|
||||
}
|
||||
|
||||
private void parseElement(AnnotatedElement element) {
|
||||
for (MockBean annotation : AnnotationUtils.getRepeatableAnnotations(element,
|
||||
MockBean.class, MockBeans.class)) {
|
||||
parseMockBeanAnnotation(annotation, element);
|
||||
}
|
||||
for (SpyBean annotation : AnnotationUtils.getRepeatableAnnotations(element,
|
||||
SpyBean.class, SpyBeans.class)) {
|
||||
parseSpyBeanAnnotation(annotation, element);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseMockBeanAnnotation(MockBean annotation, AnnotatedElement element) {
|
||||
Set<ResolvableType> typesToMock = getOrDeduceTypes(element, annotation.value());
|
||||
Assert.state(!typesToMock.isEmpty(),
|
||||
"Unable to deduce type to mock from " + element);
|
||||
if (StringUtils.hasLength(annotation.name())) {
|
||||
Assert.state(typesToMock.size() == 1,
|
||||
"The name attribute can only be used when mocking a single class");
|
||||
}
|
||||
for (ResolvableType typeToMock : typesToMock) {
|
||||
MockDefinition definition = new MockDefinition(annotation.name(), typeToMock,
|
||||
annotation.extraInterfaces(), annotation.answer(),
|
||||
annotation.serializable(), annotation.reset(),
|
||||
QualifierDefinition.forElement(element));
|
||||
addDefinition(element, definition, "mock");
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSpyBeanAnnotation(SpyBean annotation, AnnotatedElement element) {
|
||||
Set<ResolvableType> typesToSpy = getOrDeduceTypes(element, annotation.value());
|
||||
Assert.state(!typesToSpy.isEmpty(),
|
||||
"Unable to deduce type to spy from " + element);
|
||||
if (StringUtils.hasLength(annotation.name())) {
|
||||
Assert.state(typesToSpy.size() == 1,
|
||||
"The name attribute can only be used when spying a single class");
|
||||
}
|
||||
for (ResolvableType typeToSpy : typesToSpy) {
|
||||
SpyDefinition definition = new SpyDefinition(annotation.name(), typeToSpy,
|
||||
annotation.reset(), annotation.proxyTargetAware(),
|
||||
QualifierDefinition.forElement(element));
|
||||
addDefinition(element, definition, "spy");
|
||||
}
|
||||
}
|
||||
|
||||
private void addDefinition(AnnotatedElement element, Definition definition,
|
||||
String type) {
|
||||
boolean isNewDefinition = this.definitions.add(definition);
|
||||
Assert.state(isNewDefinition, "Duplicate " + type + " definition " + definition);
|
||||
if (element instanceof Field) {
|
||||
Field field = (Field) element;
|
||||
this.definitionFields.put(definition, field);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<ResolvableType> getOrDeduceTypes(AnnotatedElement element,
|
||||
Class<?>[] value) {
|
||||
Set<ResolvableType> types = new LinkedHashSet<>();
|
||||
for (Class<?> clazz : value) {
|
||||
types.add(ResolvableType.forClass(clazz));
|
||||
}
|
||||
if (types.isEmpty() && element instanceof Field) {
|
||||
types.add(ResolvableType.forField((Field) element));
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
public Set<Definition> getDefinitions() {
|
||||
return Collections.unmodifiableSet(this.definitions);
|
||||
}
|
||||
|
||||
public Field getField(Definition definition) {
|
||||
return this.definitionFields.get(definition);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.MockSettings;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to add mocks to a Spring {@link ApplicationContext}. Can be
|
||||
* used as a class level annotation or on fields in either {@code @Configuration} classes,
|
||||
* or test classes that are {@link RunWith @RunWith} the {@link SpringRunner}.
|
||||
* <p>
|
||||
* Mocks can be registered by type or by {@link #name() bean name}. Any existing single
|
||||
* bean of the same type defined in the context will be replaced by the mock. If no
|
||||
* existing bean is defined a new one will be added. Dependencies that are known to the
|
||||
* application context but are not beans (such as those
|
||||
* {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object)
|
||||
* registered directly} ) will not be found and a mocked bean will be added to the context
|
||||
* alongside the existing dependency.
|
||||
* <p>
|
||||
* When {@code @MockBean} is used on a field, as well as being registered in the
|
||||
* application context, the mock will also be injected into the field. Typical usage might
|
||||
* be: <pre class="code">
|
||||
* @RunWith(SpringRunner.class)
|
||||
* public class ExampleTests {
|
||||
*
|
||||
* @MockBean
|
||||
* private ExampleService service;
|
||||
*
|
||||
* @Autowired
|
||||
* private UserOfService userOfService;
|
||||
*
|
||||
* @Test
|
||||
* public void testUserOfService() {
|
||||
* given(this.service.greet()).willReturn("Hello");
|
||||
* String actual = this.userOfService.makeUse();
|
||||
* assertEquals("Was: Hello", actual);
|
||||
* }
|
||||
*
|
||||
* @Configuration
|
||||
* @Import(UserOfService.class) // A @Component injected with ExampleService
|
||||
* static class Config {
|
||||
* }
|
||||
*
|
||||
*
|
||||
* }
|
||||
* </pre> If there is more than one bean of the requested type, qualifier metadata must be
|
||||
* specified at field level: <pre class="code">
|
||||
* @RunWith(SpringRunner.class)
|
||||
* public class ExampleTests {
|
||||
*
|
||||
* @MockBean
|
||||
* @Qualifier("example")
|
||||
* private ExampleService service;
|
||||
*
|
||||
* ...
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* This annotation is {@code @Repeatable} and may be specified multiple times when working
|
||||
* with Java 8 or contained within an {@link MockBeans @MockBeans} annotation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see MockitoPostProcessor
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Repeatable(MockBeans.class)
|
||||
public @interface MockBean {
|
||||
|
||||
/**
|
||||
* The name of the bean to register or replace. If not specified the name will either
|
||||
* be generated or, if the mock replaces an existing bean, the existing name will be
|
||||
* used.
|
||||
* @return the name of the bean
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* The classes to mock. This is an alias of {@link #classes()} which can be used for
|
||||
* brevity if no other attributes are defined. See {@link #classes()} for details.
|
||||
* @return the classes to mock
|
||||
*/
|
||||
@AliasFor("classes")
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The classes to mock. Each class specified here will result in a mock being created
|
||||
* and registered with the application context. Classes can be omitted when the
|
||||
* annotation is used on a field.
|
||||
* <p>
|
||||
* When {@code @MockBean} also defines a {@code name} this attribute can only contain
|
||||
* a single value.
|
||||
* <p>
|
||||
* If this is the only specified attribute consider using the {@code value} alias
|
||||
* instead.
|
||||
* @return the classes to mock
|
||||
*/
|
||||
@AliasFor("value")
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
/**
|
||||
* Any extra interfaces that should also be declared on the mock. See
|
||||
* {@link MockSettings#extraInterfaces(Class...)} for details.
|
||||
* @return any extra interfaces
|
||||
*/
|
||||
Class<?>[] extraInterfaces() default {};
|
||||
|
||||
/**
|
||||
* The {@link Answers} type to use on the mock.
|
||||
* @return the answer type
|
||||
*/
|
||||
Answers answer() default Answers.RETURNS_DEFAULTS;
|
||||
|
||||
/**
|
||||
* If the generated mock is serializable. See {@link MockSettings#serializable()} for
|
||||
* details.
|
||||
* @return if the mock is serializable
|
||||
*/
|
||||
boolean serializable() default false;
|
||||
|
||||
/**
|
||||
* The reset mode to apply to the mock bean. The default is {@link MockReset#AFTER}
|
||||
* meaning that mocks are automatically reset after each test method is invoked.
|
||||
* @return the reset mode
|
||||
*/
|
||||
MockReset reset() default MockReset.AFTER;
|
||||
|
||||
}
|
||||
@@ -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.mock.mockito;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Container annotation that aggregates several {@link MockBean} annotations.
|
||||
* <p>
|
||||
* Can be used natively, declaring several nested {@link MockBean} annotations. Can also
|
||||
* be used in conjunction with Java 8's support for <em>repeatable annotations</em>, where
|
||||
* {@link MockBean} can simply be declared several times on the same
|
||||
* {@linkplain ElementType#TYPE type}, implicitly generating this container annotation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
public @interface MockBeans {
|
||||
|
||||
/**
|
||||
* Return the contained {@link MockBean} annotations.
|
||||
* @return the mock beans
|
||||
*/
|
||||
MockBean[] value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.MockSettings;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A complete definition that can be used to create a Mockito mock.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MockDefinition extends Definition {
|
||||
|
||||
private static final int MULTIPLIER = 31;
|
||||
|
||||
private final ResolvableType typeToMock;
|
||||
|
||||
private final Set<Class<?>> extraInterfaces;
|
||||
|
||||
private final Answers answer;
|
||||
|
||||
private final boolean serializable;
|
||||
|
||||
MockDefinition(String name, ResolvableType typeToMock, Class<?>[] extraInterfaces,
|
||||
Answers answer, boolean serializable, MockReset reset,
|
||||
QualifierDefinition qualifier) {
|
||||
super(name, reset, false, qualifier);
|
||||
Assert.notNull(typeToMock, "TypeToMock must not be null");
|
||||
this.typeToMock = typeToMock;
|
||||
this.extraInterfaces = asClassSet(extraInterfaces);
|
||||
this.answer = (answer != null ? answer : Answers.RETURNS_DEFAULTS);
|
||||
this.serializable = serializable;
|
||||
}
|
||||
|
||||
private Set<Class<?>> asClassSet(Class<?>[] classes) {
|
||||
Set<Class<?>> classSet = new LinkedHashSet<>();
|
||||
if (classes != null) {
|
||||
classSet.addAll(Arrays.asList(classes));
|
||||
}
|
||||
return Collections.unmodifiableSet(classSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type that should be mocked.
|
||||
* @return the type to mock; never {@code null}
|
||||
*/
|
||||
public ResolvableType getTypeToMock() {
|
||||
return this.typeToMock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the extra interfaces.
|
||||
* @return the extra interfaces or an empty set
|
||||
*/
|
||||
public Set<Class<?>> getExtraInterfaces() {
|
||||
return this.extraInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the answers mode.
|
||||
* @return the answers mode; never {@code null}
|
||||
*/
|
||||
public Answers getAnswer() {
|
||||
return this.answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the mock is serializable.
|
||||
* @return if the mock is serializable
|
||||
*/
|
||||
public boolean isSerializable() {
|
||||
return this.serializable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.typeToMock);
|
||||
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.extraInterfaces);
|
||||
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.answer);
|
||||
result = MULTIPLIER * result + Boolean.hashCode(this.serializable);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
MockDefinition other = (MockDefinition) obj;
|
||||
boolean result = super.equals(obj);
|
||||
result = result && ObjectUtils.nullSafeEquals(this.typeToMock, other.typeToMock);
|
||||
result = result && ObjectUtils.nullSafeEquals(this.extraInterfaces,
|
||||
other.extraInterfaces);
|
||||
result = result && ObjectUtils.nullSafeEquals(this.answer, other.answer);
|
||||
result = result && this.serializable == other.serializable;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("name", getName())
|
||||
.append("typeToMock", this.typeToMock)
|
||||
.append("extraInterfaces", this.extraInterfaces)
|
||||
.append("answer", this.answer).append("serializable", this.serializable)
|
||||
.append("reset", getReset()).toString();
|
||||
}
|
||||
|
||||
public <T> T createMock() {
|
||||
return createMock(getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T createMock(String name) {
|
||||
MockSettings settings = MockReset.withSettings(getReset());
|
||||
if (StringUtils.hasLength(name)) {
|
||||
settings.name(name);
|
||||
}
|
||||
if (!this.extraInterfaces.isEmpty()) {
|
||||
settings.extraInterfaces(this.extraInterfaces.toArray(new Class<?>[] {}));
|
||||
}
|
||||
settings.defaultAnswer(this.answer);
|
||||
if (this.serializable) {
|
||||
settings.serializable();
|
||||
}
|
||||
return (T) Mockito.mock(this.typeToMock.resolve(), settings);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mockito.MockSettings;
|
||||
import org.mockito.MockingDetails;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.listeners.InvocationListener;
|
||||
import org.mockito.listeners.MethodInvocationReport;
|
||||
import org.mockito.mock.MockCreationSettings;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Reset strategy used on a mock bean. Usually applied to a mock via the
|
||||
* {@link MockBean @MockBean} annotation but can also be directly applied to any mock in
|
||||
* the {@code ApplicationContext} using the static methods.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see ResetMocksTestExecutionListener
|
||||
*/
|
||||
public enum MockReset {
|
||||
|
||||
/**
|
||||
* Reset the mock before the test method runs.
|
||||
*/
|
||||
BEFORE,
|
||||
|
||||
/**
|
||||
* Reset the mock after the test method runs.
|
||||
*/
|
||||
AFTER,
|
||||
|
||||
/**
|
||||
* Don't reset the mock.
|
||||
*/
|
||||
NONE;
|
||||
|
||||
/**
|
||||
* Create {@link MockSettings settings} to be used with mocks where reset should occur
|
||||
* before each test method runs.
|
||||
* @return mock settings
|
||||
*/
|
||||
public static MockSettings before() {
|
||||
return withSettings(BEFORE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link MockSettings settings} to be used with mocks where reset should occur
|
||||
* after each test method runs.
|
||||
* @return mock settings
|
||||
*/
|
||||
public static MockSettings after() {
|
||||
return withSettings(AFTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link MockSettings settings} to be used with mocks where a specific reset
|
||||
* should occur.
|
||||
* @param reset the reset type
|
||||
* @return mock settings
|
||||
*/
|
||||
public static MockSettings withSettings(MockReset reset) {
|
||||
return apply(reset, Mockito.withSettings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply {@link MockReset} to existing {@link MockSettings settings}.
|
||||
* @param reset the reset type
|
||||
* @param settings the settings
|
||||
* @return the configured settings
|
||||
*/
|
||||
public static MockSettings apply(MockReset reset, MockSettings settings) {
|
||||
Assert.notNull(settings, "Settings must not be null");
|
||||
if (reset != null && reset != NONE) {
|
||||
settings.invocationListeners(new ResetInvocationListener(reset));
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MockReset} associated with the given mock.
|
||||
* @param mock the source mock
|
||||
* @return the reset type (never {@code null})
|
||||
*/
|
||||
static MockReset get(Object mock) {
|
||||
MockReset reset = MockReset.NONE;
|
||||
MockingDetails mockingDetails = Mockito.mockingDetails(mock);
|
||||
if (mockingDetails.isMock()) {
|
||||
MockCreationSettings<?> settings = mockingDetails.getMockCreationSettings();
|
||||
List<InvocationListener> listeners = settings.getInvocationListeners();
|
||||
for (Object listener : listeners) {
|
||||
if (listener instanceof ResetInvocationListener) {
|
||||
reset = ((ResetInvocationListener) listener).getReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return reset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy {@link InvocationListener} used to hold the {@link MockReset} value.
|
||||
*/
|
||||
private static class ResetInvocationListener implements InvocationListener {
|
||||
|
||||
private final MockReset reset;
|
||||
|
||||
ResetInvocationListener(MockReset reset) {
|
||||
this.reset = reset;
|
||||
}
|
||||
|
||||
public MockReset getReset() {
|
||||
return this.reset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportInvocation(MethodInvocationReport methodInvocationReport) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.mock.mockito;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.Interceptor;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.mockito.internal.matchers.LocalizedMatcher;
|
||||
import org.mockito.internal.progress.ArgumentMatcherStorage;
|
||||
import org.mockito.internal.progress.MockingProgress;
|
||||
import org.mockito.internal.progress.ThreadSafeMockingProgress;
|
||||
import org.mockito.internal.verification.MockAwareVerificationMode;
|
||||
import org.mockito.verification.VerificationMode;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AOP {@link Interceptor} that attempts to make AOP proxy beans work with Mockito. Works
|
||||
* by bypassing AOP advice when a method is invoked via
|
||||
* {@code Mockito#verify(Object) verify(mock)}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
|
||||
|
||||
private final Object source;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Verification verification = new Verification();
|
||||
|
||||
MockitoAopProxyTargetInterceptor(Object source, Object target) throws Exception {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
if (this.verification.isVerifying()) {
|
||||
this.verification.replaceVerifyMock(this.source, this.target);
|
||||
return AopUtils.invokeJoinpointUsingReflection(this.target,
|
||||
invocation.getMethod(), invocation.getArguments());
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public static void applyTo(Object source) {
|
||||
Assert.state(AopUtils.isAopProxy(source), "Source must be an AOP proxy");
|
||||
try {
|
||||
Advised advised = (Advised) source;
|
||||
for (Advisor advisor : advised.getAdvisors()) {
|
||||
if (advisor instanceof MockitoAopProxyTargetInterceptor) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Object target = AopTestUtils.getUltimateTargetObject(source);
|
||||
Advice advice = new MockitoAopProxyTargetInterceptor(source, target);
|
||||
advised.addAdvice(0, advice);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to apply Mockito AOP support", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Verification {
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final MockingProgress progress = ThreadSafeMockingProgress
|
||||
.mockingProgress();
|
||||
|
||||
public boolean isVerifying() {
|
||||
synchronized (this.monitor) {
|
||||
VerificationMode mode = this.progress.pullVerificationMode();
|
||||
if (mode != null) {
|
||||
resetVerificationStarted(mode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void replaceVerifyMock(Object source, Object target) {
|
||||
synchronized (this.monitor) {
|
||||
VerificationMode mode = this.progress.pullVerificationMode();
|
||||
if (mode != null) {
|
||||
if (mode instanceof MockAwareVerificationMode) {
|
||||
MockAwareVerificationMode mockAwareMode = (MockAwareVerificationMode) mode;
|
||||
if (mockAwareMode.getMock() == source) {
|
||||
mode = new MockAwareVerificationMode(target, mode,
|
||||
Collections.emptySet());
|
||||
}
|
||||
}
|
||||
resetVerificationStarted(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetVerificationStarted(VerificationMode mode) {
|
||||
ArgumentMatcherStorage storage = this.progress.getArgumentMatcherStorage();
|
||||
List<LocalizedMatcher> matchers = storage.pullLocalizedMatchers();
|
||||
this.progress.verificationStarted(mode);
|
||||
matchers.stream().map(LocalizedMatcher::getMatcher)
|
||||
.forEach(storage::reportMatcher);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.mock.mockito;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Beans created using Mockito.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class MockitoBeans implements Iterable<Object> {
|
||||
|
||||
private final List<Object> beans = new ArrayList<>();
|
||||
|
||||
void add(Object bean) {
|
||||
this.beans.add(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return this.beans.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
/**
|
||||
* A {@link ContextCustomizer} to add Mockito support.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MockitoContextCustomizer implements ContextCustomizer {
|
||||
|
||||
private final Set<Definition> definitions;
|
||||
|
||||
MockitoContextCustomizer(Set<? extends Definition> definitions) {
|
||||
this.definitions = new LinkedHashSet<>(definitions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedContextConfiguration) {
|
||||
if (context instanceof BeanDefinitionRegistry) {
|
||||
MockitoPostProcessor.register((BeanDefinitionRegistry) context,
|
||||
this.definitions);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.definitions.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
MockitoContextCustomizer other = (MockitoContextCustomizer) obj;
|
||||
return this.definitions.equals(other.definitions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.mock.mockito;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
|
||||
/**
|
||||
* A {@link ContextCustomizerFactory} to add Mockito support.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class MockitoContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
// We gather the explicit mock definitions here since they form part of the
|
||||
// MergedContextConfiguration key. Different mocks need to have a different key.
|
||||
DefinitionsParser parser = new DefinitionsParser();
|
||||
parser.parse(testClass);
|
||||
return new MockitoContextCustomizer(parser.getDefinitions());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link BeanFactoryPostProcessor} used to register and inject
|
||||
* {@link MockBean @MockBeans} with the {@link ApplicationContext}. An initial set of
|
||||
* definitions can be passed to the processor with additional definitions being
|
||||
* automatically created from {@code @Configuration} classes that use
|
||||
* {@link MockBean @MockBean}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
|
||||
implements BeanClassLoaderAware, BeanFactoryAware, BeanFactoryPostProcessor,
|
||||
Ordered {
|
||||
|
||||
private static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
|
||||
|
||||
private static final String BEAN_NAME = MockitoPostProcessor.class.getName();
|
||||
|
||||
private static final String CONFIGURATION_CLASS_ATTRIBUTE = Conventions
|
||||
.getQualifiedAttributeName(ConfigurationClassPostProcessor.class,
|
||||
"configurationClass");
|
||||
|
||||
private final Set<Definition> definitions;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
|
||||
|
||||
private final MockitoBeans mockitoBeans = new MockitoBeans();
|
||||
|
||||
private Map<Definition, String> beanNameRegistry = new HashMap<>();
|
||||
|
||||
private Map<Field, RegisteredField> fieldRegistry = new HashMap<>();
|
||||
|
||||
private Map<String, SpyDefinition> spies = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Create a new {@link MockitoPostProcessor} instance with the given initial
|
||||
* definitions.
|
||||
* @param definitions the initial definitions
|
||||
*/
|
||||
public MockitoPostProcessor(Set<Definition> definitions) {
|
||||
this.definitions = definitions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
|
||||
"Mock beans can only be used with a ConfigurableListableBeanFactory");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
Assert.isInstanceOf(BeanDefinitionRegistry.class, beanFactory,
|
||||
"@MockBean can only be used on bean factories that "
|
||||
+ "implement BeanDefinitionRegistry");
|
||||
postProcessBeanFactory(beanFactory, (BeanDefinitionRegistry) beanFactory);
|
||||
}
|
||||
|
||||
private void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinitionRegistry registry) {
|
||||
beanFactory.registerSingleton(MockitoBeans.class.getName(), this.mockitoBeans);
|
||||
DefinitionsParser parser = new DefinitionsParser(this.definitions);
|
||||
for (Class<?> configurationClass : getConfigurationClasses(beanFactory)) {
|
||||
parser.parse(configurationClass);
|
||||
}
|
||||
Set<Definition> definitions = parser.getDefinitions();
|
||||
for (Definition definition : definitions) {
|
||||
Field field = parser.getField(definition);
|
||||
register(beanFactory, registry, definition, field);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Class<?>> getConfigurationClasses(
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
Set<Class<?>> configurationClasses = new LinkedHashSet<>();
|
||||
for (BeanDefinition beanDefinition : getConfigurationBeanDefinitions(beanFactory)
|
||||
.values()) {
|
||||
configurationClasses.add(ClassUtils.resolveClassName(
|
||||
beanDefinition.getBeanClassName(), this.classLoader));
|
||||
}
|
||||
return configurationClasses;
|
||||
}
|
||||
|
||||
private Map<String, BeanDefinition> getConfigurationBeanDefinitions(
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
Map<String, BeanDefinition> definitions = new LinkedHashMap<>();
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
|
||||
if (definition.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE) != null) {
|
||||
definitions.put(beanName, definition);
|
||||
}
|
||||
}
|
||||
return definitions;
|
||||
}
|
||||
|
||||
private void register(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinitionRegistry registry, Definition definition, Field field) {
|
||||
if (definition instanceof MockDefinition) {
|
||||
registerMock(beanFactory, registry, (MockDefinition) definition, field);
|
||||
}
|
||||
else if (definition instanceof SpyDefinition) {
|
||||
registerSpy(beanFactory, registry, (SpyDefinition) definition, field);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerMock(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinitionRegistry registry, MockDefinition definition, Field field) {
|
||||
RootBeanDefinition beanDefinition = createBeanDefinition(definition);
|
||||
String beanName = getBeanName(beanFactory, registry, definition, beanDefinition);
|
||||
String transformedBeanName = BeanFactoryUtils.transformedBeanName(beanName);
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1,
|
||||
beanName);
|
||||
if (registry.containsBeanDefinition(transformedBeanName)) {
|
||||
registry.removeBeanDefinition(transformedBeanName);
|
||||
}
|
||||
registry.registerBeanDefinition(transformedBeanName, beanDefinition);
|
||||
Object mock = createMock(definition, beanName);
|
||||
beanFactory.registerSingleton(transformedBeanName, mock);
|
||||
this.mockitoBeans.add(mock);
|
||||
this.beanNameRegistry.put(definition, beanName);
|
||||
if (field != null) {
|
||||
this.fieldRegistry.put(field, new RegisteredField(definition, beanName));
|
||||
}
|
||||
}
|
||||
|
||||
private RootBeanDefinition createBeanDefinition(MockDefinition mockDefinition) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(
|
||||
mockDefinition.getTypeToMock().resolve());
|
||||
definition.setTargetType(mockDefinition.getTypeToMock());
|
||||
definition.setFactoryBeanName(BEAN_NAME);
|
||||
definition.setFactoryMethodName("createMock");
|
||||
definition.getConstructorArgumentValues().addIndexedArgumentValue(0,
|
||||
mockDefinition);
|
||||
if (mockDefinition.getQualifier() != null) {
|
||||
mockDefinition.getQualifier().applyTo(definition);
|
||||
}
|
||||
return definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used by defined beans to actually create the mock.
|
||||
* @param mockDefinition the mock definition
|
||||
* @param name the bean name
|
||||
* @return the mock instance
|
||||
*/
|
||||
protected final Object createMock(MockDefinition mockDefinition, String name) {
|
||||
return mockDefinition.createMock(name + " bean");
|
||||
}
|
||||
|
||||
private String getBeanName(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinitionRegistry registry, MockDefinition mockDefinition,
|
||||
RootBeanDefinition beanDefinition) {
|
||||
if (StringUtils.hasLength(mockDefinition.getName())) {
|
||||
return mockDefinition.getName();
|
||||
}
|
||||
Set<String> existingBeans = findCandidateBeans(beanFactory, mockDefinition);
|
||||
if (existingBeans.isEmpty()) {
|
||||
return this.beanNameGenerator.generateBeanName(beanDefinition, registry);
|
||||
}
|
||||
if (existingBeans.size() == 1) {
|
||||
return existingBeans.iterator().next();
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Unable to register mock bean " + mockDefinition.getTypeToMock()
|
||||
+ " expected a single matching bean to replace but found "
|
||||
+ existingBeans);
|
||||
}
|
||||
|
||||
private void registerSpy(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinitionRegistry registry, SpyDefinition definition, Field field) {
|
||||
String[] existingBeans = getExistingBeans(beanFactory, definition.getTypeToSpy());
|
||||
if (ObjectUtils.isEmpty(existingBeans)) {
|
||||
createSpy(registry, definition, field);
|
||||
}
|
||||
else {
|
||||
registerSpies(registry, definition, field, existingBeans);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> findCandidateBeans(ConfigurableListableBeanFactory beanFactory,
|
||||
MockDefinition mockDefinition) {
|
||||
QualifierDefinition qualifier = mockDefinition.getQualifier();
|
||||
Set<String> candidates = new TreeSet<>();
|
||||
for (String candidate : getExistingBeans(beanFactory,
|
||||
mockDefinition.getTypeToMock())) {
|
||||
if (qualifier == null || qualifier.matches(beanFactory, candidate)) {
|
||||
candidates.add(candidate);
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private String[] getExistingBeans(ConfigurableListableBeanFactory beanFactory,
|
||||
ResolvableType type) {
|
||||
Set<String> beans = new LinkedHashSet<>(
|
||||
Arrays.asList(beanFactory.getBeanNamesForType(type)));
|
||||
String resolvedTypeName = type.resolve(Object.class).getName();
|
||||
for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class)) {
|
||||
beanName = BeanFactoryUtils.transformedBeanName(beanName);
|
||||
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
|
||||
if (resolvedTypeName
|
||||
.equals(beanDefinition.getAttribute(FACTORY_BEAN_OBJECT_TYPE))) {
|
||||
beans.add(beanName);
|
||||
}
|
||||
}
|
||||
beans.removeIf(this::isScopedTarget);
|
||||
return beans.toArray(new String[beans.size()]);
|
||||
}
|
||||
|
||||
private boolean isScopedTarget(String beanName) {
|
||||
try {
|
||||
return ScopedProxyUtils.isScopedTarget(beanName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void createSpy(BeanDefinitionRegistry registry, SpyDefinition definition,
|
||||
Field field) {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
definition.getTypeToSpy().resolve());
|
||||
String beanName = this.beanNameGenerator.generateBeanName(beanDefinition,
|
||||
registry);
|
||||
registry.registerBeanDefinition(beanName, beanDefinition);
|
||||
registerSpy(definition, field, beanName);
|
||||
}
|
||||
|
||||
private void registerSpies(BeanDefinitionRegistry registry, SpyDefinition definition,
|
||||
Field field, String[] existingBeans) {
|
||||
try {
|
||||
registerSpy(definition, field,
|
||||
determineBeanName(existingBeans, definition, registry));
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to register spy bean " + definition.getTypeToSpy(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String determineBeanName(String[] existingBeans, SpyDefinition definition,
|
||||
BeanDefinitionRegistry registry) {
|
||||
if (StringUtils.hasText(definition.getName())) {
|
||||
return definition.getName();
|
||||
}
|
||||
if (existingBeans.length == 1) {
|
||||
return existingBeans[0];
|
||||
}
|
||||
return determinePrimaryCandidate(registry, existingBeans,
|
||||
definition.getTypeToSpy());
|
||||
}
|
||||
|
||||
private String determinePrimaryCandidate(BeanDefinitionRegistry registry,
|
||||
String[] candidateBeanNames, ResolvableType type) {
|
||||
String primaryBeanName = null;
|
||||
for (String candidateBeanName : candidateBeanNames) {
|
||||
BeanDefinition beanDefinition = registry.getBeanDefinition(candidateBeanName);
|
||||
if (beanDefinition.isPrimary()) {
|
||||
if (primaryBeanName != null) {
|
||||
throw new NoUniqueBeanDefinitionException(type.resolve(),
|
||||
candidateBeanNames.length,
|
||||
"more than one 'primary' bean found among candidates: "
|
||||
+ Arrays.asList(candidateBeanNames));
|
||||
}
|
||||
primaryBeanName = candidateBeanName;
|
||||
}
|
||||
}
|
||||
return primaryBeanName;
|
||||
}
|
||||
|
||||
private void registerSpy(SpyDefinition definition, Field field, String beanName) {
|
||||
this.spies.put(beanName, definition);
|
||||
this.beanNameRegistry.put(definition, beanName);
|
||||
if (field != null) {
|
||||
this.fieldRegistry.put(field, new RegisteredField(definition, beanName));
|
||||
}
|
||||
}
|
||||
|
||||
protected Object createSpyIfNecessary(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
SpyDefinition definition = this.spies.get(beanName);
|
||||
if (definition != null) {
|
||||
bean = definition.createSpy(beanName, bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
|
||||
PropertyDescriptor[] pds, final Object bean, String beanName)
|
||||
throws BeansException {
|
||||
ReflectionUtils.doWithFields(bean.getClass(),
|
||||
(field) -> postProcessField(bean, field));
|
||||
return pvs;
|
||||
}
|
||||
|
||||
private void postProcessField(Object bean, Field field) {
|
||||
RegisteredField registered = this.fieldRegistry.get(field);
|
||||
if (registered != null && StringUtils.hasLength(registered.getBeanName())) {
|
||||
inject(field, bean, registered.getBeanName(), registered.getDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
void inject(Field field, Object target, Definition definition) {
|
||||
String beanName = this.beanNameRegistry.get(definition);
|
||||
Assert.state(StringUtils.hasLength(beanName),
|
||||
"No bean found for definition " + definition);
|
||||
inject(field, target, beanName, definition);
|
||||
}
|
||||
|
||||
private void inject(Field field, Object target, String beanName,
|
||||
Definition definition) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
Assert.state(ReflectionUtils.getField(field, target) == null,
|
||||
"The field " + field + " cannot have an existing value");
|
||||
Object bean = this.beanFactory.getBean(beanName, field.getType());
|
||||
if (definition.isProxyTargetAware() && isAopProxy(bean)) {
|
||||
MockitoAopProxyTargetInterceptor.applyTo(bean);
|
||||
}
|
||||
ReflectionUtils.setField(field, target, bean);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException("Could not inject field: " + field, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAopProxy(Object object) {
|
||||
try {
|
||||
return AopUtils.isAopProxy(object);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE - 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the processor with a {@link BeanDefinitionRegistry}. Not required when
|
||||
* using the {@link SpringRunner} as registration is automatic.
|
||||
* @param registry the bean definition registry
|
||||
*/
|
||||
public static void register(BeanDefinitionRegistry registry) {
|
||||
register(registry, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the processor with a {@link BeanDefinitionRegistry}. Not required when
|
||||
* using the {@link SpringRunner} as registration is automatic.
|
||||
* @param registry the bean definition registry
|
||||
* @param definitions the initial mock/spy definitions
|
||||
*/
|
||||
public static void register(BeanDefinitionRegistry registry,
|
||||
Set<Definition> definitions) {
|
||||
register(registry, MockitoPostProcessor.class, definitions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the processor with a {@link BeanDefinitionRegistry}. Not required when
|
||||
* using the {@link SpringRunner} as registration is automatic.
|
||||
* @param registry the bean definition registry
|
||||
* @param postProcessor the post processor class to register
|
||||
* @param definitions the initial mock/spy definitions
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void register(BeanDefinitionRegistry registry,
|
||||
Class<? extends MockitoPostProcessor> postProcessor,
|
||||
Set<Definition> definitions) {
|
||||
SpyPostProcessor.register(registry);
|
||||
BeanDefinition definition = getOrAddBeanDefinition(registry, postProcessor);
|
||||
ValueHolder constructorArg = definition.getConstructorArgumentValues()
|
||||
.getIndexedArgumentValue(0, Set.class);
|
||||
Set<Definition> existing = (Set<Definition>) constructorArg.getValue();
|
||||
if (definitions != null) {
|
||||
existing.addAll(definitions);
|
||||
}
|
||||
}
|
||||
|
||||
private static BeanDefinition getOrAddBeanDefinition(BeanDefinitionRegistry registry,
|
||||
Class<? extends MockitoPostProcessor> postProcessor) {
|
||||
if (!registry.containsBeanDefinition(BEAN_NAME)) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(postProcessor);
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
ConstructorArgumentValues constructorArguments = definition
|
||||
.getConstructorArgumentValues();
|
||||
constructorArguments.addIndexedArgumentValue(0,
|
||||
new LinkedHashSet<MockDefinition>());
|
||||
registry.registerBeanDefinition(BEAN_NAME, definition);
|
||||
return definition;
|
||||
}
|
||||
return registry.getBeanDefinition(BEAN_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to handle {@link SpyBean} definitions. Registered as a
|
||||
* separate processor so that it can be ordered above AOP post processors.
|
||||
*/
|
||||
static class SpyPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
|
||||
implements PriorityOrdered {
|
||||
|
||||
private static final String BEAN_NAME = SpyPostProcessor.class.getName();
|
||||
|
||||
private final MockitoPostProcessor mockitoPostProcessor;
|
||||
|
||||
SpyPostProcessor(MockitoPostProcessor mockitoPostProcessor) {
|
||||
this.mockitoPostProcessor = mockitoPostProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEarlyBeanReference(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return createSpyIfNecessary(bean, beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof FactoryBean) {
|
||||
return bean;
|
||||
}
|
||||
return createSpyIfNecessary(bean, beanName);
|
||||
}
|
||||
|
||||
private Object createSpyIfNecessary(Object bean, String beanName) {
|
||||
return this.mockitoPostProcessor.createSpyIfNecessary(bean, beanName);
|
||||
}
|
||||
|
||||
public static void register(BeanDefinitionRegistry registry) {
|
||||
if (!registry.containsBeanDefinition(BEAN_NAME)) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(
|
||||
SpyPostProcessor.class);
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
ConstructorArgumentValues constructorArguments = definition
|
||||
.getConstructorArgumentValues();
|
||||
constructorArguments.addIndexedArgumentValue(0,
|
||||
new RuntimeBeanReference(MockitoPostProcessor.BEAN_NAME));
|
||||
registry.registerBeanDefinition(BEAN_NAME, definition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A registered field item.
|
||||
*/
|
||||
private static class RegisteredField {
|
||||
|
||||
private final Definition definition;
|
||||
|
||||
private final String beanName;
|
||||
|
||||
RegisteredField(Definition definition, String beanName) {
|
||||
this.definition = definition;
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public Definition getDefinition() {
|
||||
return this.definition;
|
||||
}
|
||||
|
||||
public String getBeanName() {
|
||||
return this.beanName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
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.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.FieldCallback;
|
||||
|
||||
/**
|
||||
* {@link TestExecutionListener} to trigger {@link MockitoAnnotations#initMocks(Object)}
|
||||
* when {@link MockBean @MockBean} annotations are used. Primarily to allow {@link Captor}
|
||||
* annotations.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.2
|
||||
*/
|
||||
public class MockitoTestExecutionListener extends AbstractTestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void prepareTestInstance(TestContext testContext) throws Exception {
|
||||
if (hasMockitoAnnotations(testContext)) {
|
||||
MockitoAnnotations.initMocks(testContext.getTestInstance());
|
||||
}
|
||||
injectFields(testContext);
|
||||
}
|
||||
|
||||
private boolean hasMockitoAnnotations(TestContext testContext) {
|
||||
MockitoAnnotationCollection collector = new MockitoAnnotationCollection();
|
||||
ReflectionUtils.doWithFields(testContext.getTestClass(), collector);
|
||||
return collector.hasAnnotations();
|
||||
}
|
||||
|
||||
private void injectFields(TestContext testContext) {
|
||||
DefinitionsParser parser = new DefinitionsParser();
|
||||
parser.parse(testContext.getTestClass());
|
||||
if (!parser.getDefinitions().isEmpty()) {
|
||||
injectFields(testContext, parser);
|
||||
}
|
||||
}
|
||||
|
||||
private void injectFields(TestContext testContext, DefinitionsParser parser) {
|
||||
ApplicationContext applicationContext = testContext.getApplicationContext();
|
||||
MockitoPostProcessor postProcessor = applicationContext
|
||||
.getBean(MockitoPostProcessor.class);
|
||||
for (Definition definition : parser.getDefinitions()) {
|
||||
Field field = parser.getField(definition);
|
||||
if (field != null) {
|
||||
postProcessor.inject(field, testContext.getTestInstance(), definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FieldCallback} to collect Mockito annotations.
|
||||
*/
|
||||
private static class MockitoAnnotationCollection implements FieldCallback {
|
||||
|
||||
private final Set<Annotation> annotations = new LinkedHashSet<>();
|
||||
|
||||
@Override
|
||||
public void doWith(Field field)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
for (Annotation annotation : field.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().getName().startsWith("org.mockito")) {
|
||||
this.annotations.add(annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasAnnotations() {
|
||||
return !this.annotations.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.mock.mockito;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
/**
|
||||
* Definition of a Spring {@link Qualifier @Qualifier}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @see Definition
|
||||
*/
|
||||
class QualifierDefinition {
|
||||
|
||||
private final Field field;
|
||||
|
||||
private final DependencyDescriptor descriptor;
|
||||
|
||||
private final Set<Annotation> annotations;
|
||||
|
||||
QualifierDefinition(Field field, Set<Annotation> annotations) {
|
||||
// We can't use the field or descriptor as part of the context key
|
||||
// but we can assume that if two fields have the same qualifiers then
|
||||
// it's safe for Spring to use either for qualifier logic
|
||||
this.field = field;
|
||||
this.descriptor = new DependencyDescriptor(field, true);
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
public boolean matches(ConfigurableListableBeanFactory beanFactory, String beanName) {
|
||||
return beanFactory.isAutowireCandidate(beanName, this.descriptor);
|
||||
}
|
||||
|
||||
public void applyTo(RootBeanDefinition definition) {
|
||||
definition.setQualifiedElement(this.field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.annotations.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || !getClass().isAssignableFrom(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
QualifierDefinition other = (QualifierDefinition) obj;
|
||||
return this.annotations.equals(other.annotations);
|
||||
}
|
||||
|
||||
public static QualifierDefinition forElement(AnnotatedElement element) {
|
||||
if (element != null && element instanceof Field) {
|
||||
Field field = (Field) element;
|
||||
Set<Annotation> annotations = getQualifierAnnotations(field);
|
||||
if (!annotations.isEmpty()) {
|
||||
return new QualifierDefinition(field, annotations);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Set<Annotation> getQualifierAnnotations(Field field) {
|
||||
// Assume that any annotations other than @MockBean/@SpyBean are qualifiers
|
||||
Annotation[] candidates = field.getDeclaredAnnotations();
|
||||
Set<Annotation> annotations = new HashSet<>(candidates.length);
|
||||
for (Annotation candidate : candidates) {
|
||||
if (!isMockOrSpyAnnotation(candidate)) {
|
||||
annotations.add(candidate);
|
||||
}
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
private static boolean isMockOrSpyAnnotation(Annotation candidate) {
|
||||
Class<? extends Annotation> type = candidate.annotationType();
|
||||
return (type.equals(MockBean.class) || type.equals(SpyBean.class)
|
||||
|| AnnotationUtils.isAnnotationMetaPresent(type, MockBean.class)
|
||||
|| AnnotationUtils.isAnnotationMetaPresent(type, SpyBean.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.mock.mockito;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
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.AbstractTestExecutionListener;
|
||||
|
||||
/**
|
||||
* {@link TestExecutionListener} to reset any mock beans that have been marked with a
|
||||
* {@link MockReset}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class ResetMocksTestExecutionListener extends AbstractTestExecutionListener {
|
||||
|
||||
@Override
|
||||
public void beforeTestMethod(TestContext testContext) throws Exception {
|
||||
resetMocks(testContext.getApplicationContext(), MockReset.BEFORE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
resetMocks(testContext.getApplicationContext(), MockReset.AFTER);
|
||||
}
|
||||
|
||||
private void resetMocks(ApplicationContext applicationContext, MockReset reset) {
|
||||
if (applicationContext instanceof ConfigurableApplicationContext) {
|
||||
resetMocks((ConfigurableApplicationContext) applicationContext, reset);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetMocks(ConfigurableApplicationContext applicationContext,
|
||||
MockReset reset) {
|
||||
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
|
||||
String[] names = beanFactory.getBeanDefinitionNames();
|
||||
Set<String> instantiatedSingletons = new HashSet<>(
|
||||
Arrays.asList(beanFactory.getSingletonNames()));
|
||||
for (String name : names) {
|
||||
BeanDefinition definition = beanFactory.getBeanDefinition(name);
|
||||
if (definition.isSingleton() && instantiatedSingletons.contains(name)) {
|
||||
Object bean = beanFactory.getSingleton(name);
|
||||
if (reset.equals(MockReset.get(bean))) {
|
||||
Mockito.reset(bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
MockitoBeans mockedBeans = beanFactory.getBean(MockitoBeans.class);
|
||||
for (Object mockedBean : mockedBeans) {
|
||||
if (reset.equals(MockReset.get(mockedBean))) {
|
||||
Mockito.reset(mockedBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Continue
|
||||
}
|
||||
if (applicationContext.getParent() != null) {
|
||||
resetMocks(applicationContext.getParent(), reset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to apply Mockito spies to a Spring
|
||||
* {@link ApplicationContext}. Can be used as a class level annotation or on fields in
|
||||
* either {@code @Configuration} classes, or test classes that are
|
||||
* {@link RunWith @RunWith} the {@link SpringRunner}.
|
||||
* <p>
|
||||
* Spies can be applied by type or by {@link #name() bean name}. All beans in the context
|
||||
* of the same type will be wrapped with the spy. If no existing bean is defined a new one
|
||||
* will be added. Dependencies that are known to the application context but are not beans
|
||||
* (such as those
|
||||
* {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object)
|
||||
* registered directly} ) will not be found and a spied bean will be added to the context
|
||||
* alongside the existing dependency.
|
||||
* <p>
|
||||
* When {@code @SpyBean} is used on a field, as well as being registered in the
|
||||
* application context, the spy will also be injected into the field. Typical usage might
|
||||
* be: <pre class="code">
|
||||
* @RunWith(SpringRunner.class)
|
||||
* public class ExampleTests {
|
||||
*
|
||||
* @SpyBean
|
||||
* private ExampleService service;
|
||||
*
|
||||
* @Autowired
|
||||
* private UserOfService userOfService;
|
||||
*
|
||||
* @Test
|
||||
* public void testUserOfService() {
|
||||
* String actual = this.userOfService.makeUse();
|
||||
* assertEquals("Was: Hello", actual);
|
||||
* verify(this.service).greet();
|
||||
* }
|
||||
*
|
||||
* @Configuration
|
||||
* @Import(UserOfService.class) // A @Component injected with ExampleService
|
||||
* static class Config {
|
||||
* }
|
||||
*
|
||||
*
|
||||
* }
|
||||
* </pre> If there is more than one bean of the requested type, qualifier metadata must be
|
||||
* specified at field level: <pre class="code">
|
||||
* @RunWith(SpringRunner.class)
|
||||
* public class ExampleTests {
|
||||
*
|
||||
* @SpyBean
|
||||
* @Qualifier("example")
|
||||
* private ExampleService service;
|
||||
*
|
||||
* ...
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* This annotation is {@code @Repeatable} and may be specified multiple times when working
|
||||
* with Java 8 or contained within a {@link SpyBeans @SpyBeans} annotation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see MockitoPostProcessor
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Repeatable(SpyBeans.class)
|
||||
public @interface SpyBean {
|
||||
|
||||
/**
|
||||
* The name of the bean to spy. If not specified the name will either be generated or,
|
||||
* if the spy is for an existing bean, the existing name will be used.
|
||||
* @return the name of the bean
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* The classes to spy. This is an alias of {@link #classes()} which can be used for
|
||||
* brevity if no other attributes are defined. See {@link #classes()} for details.
|
||||
* @return the classes to spy
|
||||
*/
|
||||
@AliasFor("classes")
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The classes to spy. Each class specified here will result in a spy being applied.
|
||||
* Classes can be omitted when the annotation is used on a field.
|
||||
* <p>
|
||||
* When {@code @SpyBean} also defines a {@code name} this attribute can only contain a
|
||||
* single value.
|
||||
* <p>
|
||||
* If this is the only specified attribute consider using the {@code value} alias
|
||||
* instead.
|
||||
* @return the classes to spy
|
||||
*/
|
||||
@AliasFor("value")
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
/**
|
||||
* The reset mode to apply to the spied bean. The default is {@link MockReset#AFTER}
|
||||
* meaning that spies are automatically reset after each test method is invoked.
|
||||
* @return the reset mode
|
||||
*/
|
||||
MockReset reset() default MockReset.AFTER;
|
||||
|
||||
/**
|
||||
* Indicates that Mockito methods such as {@link Mockito#verify(Object) verify(mock)}
|
||||
* should use the {@code target} of AOP advised beans, rather than the proxy itself.
|
||||
* If set to {@code false} you may need to use the result of
|
||||
* {@link org.springframework.test.util.AopTestUtils#getUltimateTargetObject(Object)
|
||||
* AopTestUtils.getUltimateTargetObject(...)} when calling Mockito methods.
|
||||
* @return {@code true} if the target of AOP advised beans is used or {@code false} if
|
||||
* the proxy is used directly
|
||||
*/
|
||||
boolean proxyTargetAware() default true;
|
||||
|
||||
}
|
||||
@@ -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.mock.mockito;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Container annotation that aggregates several {@link SpyBean} annotations.
|
||||
* <p>
|
||||
* Can be used natively, declaring several nested {@link SpyBean} annotations. Can also be
|
||||
* used in conjunction with Java 8's support for <em>repeatable annotations</em>, where
|
||||
* {@link SpyBean} can simply be declared several times on the same
|
||||
* {@linkplain ElementType#TYPE type}, implicitly generating this container annotation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
public @interface SpyBeans {
|
||||
|
||||
/**
|
||||
* Return the contained {@link SpyBean} annotations.
|
||||
* @return the spy beans
|
||||
*/
|
||||
SpyBean[] value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.mock.mockito;
|
||||
|
||||
import org.mockito.MockSettings;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A complete definition that can be used to create a Mockito spy.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SpyDefinition extends Definition {
|
||||
|
||||
private static final int MULTIPLIER = 31;
|
||||
|
||||
private final ResolvableType typeToSpy;
|
||||
|
||||
SpyDefinition(String name, ResolvableType typeToSpy, MockReset reset,
|
||||
boolean proxyTargetAware, QualifierDefinition qualifier) {
|
||||
super(name, reset, proxyTargetAware, qualifier);
|
||||
Assert.notNull(typeToSpy, "TypeToSpy must not be null");
|
||||
this.typeToSpy = typeToSpy;
|
||||
|
||||
}
|
||||
|
||||
public ResolvableType getTypeToSpy() {
|
||||
return this.typeToSpy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.typeToSpy);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
SpyDefinition other = (SpyDefinition) obj;
|
||||
boolean result = super.equals(obj);
|
||||
result = result && ObjectUtils.nullSafeEquals(this.typeToSpy, other.typeToSpy);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("name", getName())
|
||||
.append("typeToSpy", this.typeToSpy).append("reset", getReset())
|
||||
.toString();
|
||||
}
|
||||
|
||||
public <T> T createSpy(Object instance) {
|
||||
return createSpy(getName(), instance);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T createSpy(String name, Object instance) {
|
||||
Assert.notNull(instance, "Instance must not be null");
|
||||
Assert.isInstanceOf(this.typeToSpy.resolve(), instance);
|
||||
if (Mockito.mockingDetails(instance).isSpy()) {
|
||||
return (T) instance;
|
||||
}
|
||||
MockSettings settings = MockReset.withSettings(getReset());
|
||||
if (StringUtils.hasLength(name)) {
|
||||
settings.name(name);
|
||||
}
|
||||
settings.spiedInstance(instance);
|
||||
settings.defaultAnswer(Mockito.CALLS_REAL_METHODS);
|
||||
return (T) Mockito.mock(instance.getClass(), settings);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mockito integration for Spring Boot tests.
|
||||
*/
|
||||
package org.springframework.boot.test.mock.mockito;
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.mock.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.springframework.core.io.FileSystemResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
||||
/**
|
||||
* {@link MockServletContext} implementation for Spring Boot. Respects well-known Spring
|
||||
* Boot resource locations and uses an empty directory for "/" if no locations can be
|
||||
* found.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class SpringBootMockServletContext extends MockServletContext {
|
||||
|
||||
private static final String[] SPRING_BOOT_RESOURCE_LOCATIONS = new String[] {
|
||||
"classpath:META-INF/resources", "classpath:resources", "classpath:static",
|
||||
"classpath:public" };
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private File emptyRootFolder;
|
||||
|
||||
public SpringBootMockServletContext(String resourceBasePath) {
|
||||
this(resourceBasePath, new FileSystemResourceLoader());
|
||||
}
|
||||
|
||||
public SpringBootMockServletContext(String resourceBasePath,
|
||||
ResourceLoader resourceLoader) {
|
||||
super(resourceBasePath, resourceLoader);
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResourceLocation(String path) {
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
String resourceLocation = getResourceBasePathLocation(path);
|
||||
if (exists(resourceLocation)) {
|
||||
return resourceLocation;
|
||||
}
|
||||
for (String prefix : SPRING_BOOT_RESOURCE_LOCATIONS) {
|
||||
resourceLocation = prefix + path;
|
||||
if (exists(resourceLocation)) {
|
||||
return resourceLocation;
|
||||
}
|
||||
}
|
||||
return super.getResourceLocation(path);
|
||||
}
|
||||
|
||||
protected final String getResourceBasePathLocation(String path) {
|
||||
return super.getResourceLocation(path);
|
||||
}
|
||||
|
||||
private boolean exists(String resourceLocation) {
|
||||
try {
|
||||
Resource resource = this.resourceLoader.getResource(resourceLocation);
|
||||
return resource.exists();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(String path) throws MalformedURLException {
|
||||
URL resource = super.getResource(path);
|
||||
if (resource == null && "/".equals(path)) {
|
||||
// Liquibase assumes that "/" always exists, if we don't have a directory
|
||||
// use a temporary location.
|
||||
try {
|
||||
if (this.emptyRootFolder == null) {
|
||||
synchronized (this) {
|
||||
File tempFolder = File.createTempFile("spr", "servlet");
|
||||
tempFolder.delete();
|
||||
tempFolder.mkdirs();
|
||||
tempFolder.deleteOnExit();
|
||||
this.emptyRootFolder = tempFolder;
|
||||
}
|
||||
}
|
||||
return this.emptyRootFolder.toURI().toURL();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mock web classes specific to Spring Boot.
|
||||
*/
|
||||
package org.springframework.boot.test.mock.web;
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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.rule;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Assert;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import org.springframework.boot.ansi.AnsiOutput;
|
||||
import org.springframework.boot.ansi.AnsiOutput.Enabled;
|
||||
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
|
||||
/**
|
||||
* JUnit {@code @Rule} to capture output from System.out and System.err.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class OutputCapture implements TestRule {
|
||||
|
||||
private CaptureOutputStream captureOut;
|
||||
|
||||
private CaptureOutputStream captureErr;
|
||||
|
||||
private ByteArrayOutputStream copy;
|
||||
|
||||
private List<Matcher<? super String>> matchers = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public Statement apply(final Statement base, Description description) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
captureOutput();
|
||||
try {
|
||||
base.evaluate();
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (!OutputCapture.this.matchers.isEmpty()) {
|
||||
String output = OutputCapture.this.toString();
|
||||
Assert.assertThat(output, allOf(OutputCapture.this.matchers));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
releaseOutput();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected void captureOutput() {
|
||||
AnsiOutputControl.get().disableAnsiOutput();
|
||||
this.copy = new ByteArrayOutputStream();
|
||||
this.captureOut = new CaptureOutputStream(System.out, this.copy);
|
||||
this.captureErr = new CaptureOutputStream(System.err, this.copy);
|
||||
System.setOut(new PrintStream(this.captureOut));
|
||||
System.setErr(new PrintStream(this.captureErr));
|
||||
}
|
||||
|
||||
protected void releaseOutput() {
|
||||
AnsiOutputControl.get().enabledAnsiOutput();
|
||||
System.setOut(this.captureOut.getOriginal());
|
||||
System.setErr(this.captureErr.getOriginal());
|
||||
this.copy = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard all currently accumulated output.
|
||||
*/
|
||||
public void reset() {
|
||||
this.copy.reset();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
try {
|
||||
this.captureOut.flush();
|
||||
this.captureErr.flush();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
flush();
|
||||
return this.copy.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the output is matched by the supplied {@code matcher}. Verification is
|
||||
* performed after the test method has executed.
|
||||
* @param matcher the matcher
|
||||
*/
|
||||
public void expect(Matcher<? super String> matcher) {
|
||||
this.matchers.add(matcher);
|
||||
}
|
||||
|
||||
private static class CaptureOutputStream extends OutputStream {
|
||||
|
||||
private final PrintStream original;
|
||||
|
||||
private final OutputStream copy;
|
||||
|
||||
CaptureOutputStream(PrintStream original, OutputStream copy) {
|
||||
this.original = original;
|
||||
this.copy = copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
this.copy.write(b);
|
||||
this.original.write(b);
|
||||
this.original.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
this.copy.write(b, off, len);
|
||||
this.original.write(b, off, len);
|
||||
}
|
||||
|
||||
public PrintStream getOriginal() {
|
||||
return this.original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
this.copy.flush();
|
||||
this.original.flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow AnsiOutput to not be on the test classpath.
|
||||
*/
|
||||
private static class AnsiOutputControl {
|
||||
|
||||
public void disableAnsiOutput() {
|
||||
}
|
||||
|
||||
public void enabledAnsiOutput() {
|
||||
}
|
||||
|
||||
public static AnsiOutputControl get() {
|
||||
try {
|
||||
Class.forName("org.springframework.boot.ansi.AnsiOutput");
|
||||
return new AnsiPresentOutputControl();
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
return new AnsiOutputControl();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class AnsiPresentOutputControl extends AnsiOutputControl {
|
||||
|
||||
@Override
|
||||
public void disableAnsiOutput() {
|
||||
AnsiOutput.setEnabled(Enabled.NEVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enabledAnsiOutput() {
|
||||
AnsiOutput.setEnabled(Enabled.DETECT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Useful JUnit {@code @Rule} classes.
|
||||
*/
|
||||
package org.springframework.boot.test.rule;
|
||||
@@ -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.util;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* Application context related test utilities.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public abstract class ApplicationContextTestUtils {
|
||||
|
||||
/**
|
||||
* Closes this {@link ApplicationContext} and its parent hierarchy if any.
|
||||
* @param context the context to close (can be {@code null})
|
||||
*/
|
||||
public static void closeAll(ApplicationContext context) {
|
||||
if (context != null) {
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) context).close();
|
||||
}
|
||||
closeAll(context.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
|
||||
/**
|
||||
* Test utilities for setting environment values.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.4.0
|
||||
* @deprecated since 2.0.0 in favor of {@link TestPropertyValues}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class EnvironmentTestUtils {
|
||||
|
||||
/**
|
||||
* Add additional (high priority) values to an {@link Environment} owned by an
|
||||
* {@link ApplicationContext}. Name-value pairs can be specified with colon (":") or
|
||||
* equals ("=") separators.
|
||||
* @param context the context with an environment to modify
|
||||
* @param pairs the name:value pairs
|
||||
*/
|
||||
public static void addEnvironment(ConfigurableApplicationContext context,
|
||||
String... pairs) {
|
||||
addEnvironment(context.getEnvironment(), pairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional (high priority) values to an {@link Environment}. Name-value pairs
|
||||
* can be specified with colon (":") or equals ("=") separators.
|
||||
* @param environment the environment to modify
|
||||
* @param pairs the name:value pairs
|
||||
*/
|
||||
public static void addEnvironment(ConfigurableEnvironment environment,
|
||||
String... pairs) {
|
||||
addEnvironment("test", environment, pairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional (high priority) values to an {@link Environment}. Name-value pairs
|
||||
* can be specified with colon (":") or equals ("=") separators.
|
||||
* @param environment the environment to modify
|
||||
* @param name the property source name
|
||||
* @param pairs the name:value pairs
|
||||
*/
|
||||
public static void addEnvironment(String name, ConfigurableEnvironment environment,
|
||||
String... pairs) {
|
||||
MutablePropertySources sources = environment.getPropertySources();
|
||||
Map<String, Object> map = getOrAdd(sources, name);
|
||||
for (String pair : pairs) {
|
||||
int index = getSeparatorIndex(pair);
|
||||
String key = (index > 0 ? pair.substring(0, index) : pair);
|
||||
String value = (index > 0 ? pair.substring(index + 1) : "");
|
||||
map.put(key.trim(), value.trim());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> getOrAdd(MutablePropertySources sources,
|
||||
String name) {
|
||||
if (sources.contains(name)) {
|
||||
return (Map<String, Object>) sources.get(name).getSource();
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
sources.addFirst(new MapPropertySource(name, map));
|
||||
return map;
|
||||
}
|
||||
|
||||
private static int getSeparatorIndex(String pair) {
|
||||
int colonIndex = pair.indexOf(":");
|
||||
int equalIndex = pair.indexOf("=");
|
||||
if (colonIndex == -1) {
|
||||
return equalIndex;
|
||||
}
|
||||
if (equalIndex == -1) {
|
||||
return colonIndex;
|
||||
}
|
||||
return Math.min(colonIndex, equalIndex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.SystemEnvironmentPropertySource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Test utilities for adding properties. Properties can be applied to a Spring
|
||||
* {@link Environment} or to the {@link System#getProperties() system environment}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class TestPropertyValues {
|
||||
|
||||
private static final TestPropertyValues EMPTY = new TestPropertyValues(
|
||||
Collections.emptyMap());
|
||||
|
||||
private final Map<String, Object> properties;
|
||||
|
||||
private TestPropertyValues(Map<String, Object> properties) {
|
||||
this.properties = Collections.unmodifiableMap(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder method to add more properties.
|
||||
* @param pairs the property pairs to add
|
||||
* @return a new {@link TestPropertyValues} instance
|
||||
*/
|
||||
public TestPropertyValues and(String... pairs) {
|
||||
return and(Arrays.stream(pairs).map(Pair::parse));
|
||||
}
|
||||
|
||||
private TestPropertyValues and(Stream<Pair> pairs) {
|
||||
Map<String, Object> properties = new LinkedHashMap<>(this.properties);
|
||||
pairs.filter(Objects::nonNull).forEach((pair) -> pair.addTo(properties));
|
||||
return new TestPropertyValues(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the properties from the underlying map to the environment owned by an
|
||||
* {@link ApplicationContext}.
|
||||
* @param context the context with an environment to modify
|
||||
*/
|
||||
public void applyTo(ConfigurableApplicationContext context) {
|
||||
applyTo(context.getEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the properties from the underlying map to the environment. The default property
|
||||
* source used is {@link MapPropertySource}.
|
||||
* @param environment the environment that needs to be modified
|
||||
*/
|
||||
public void applyTo(ConfigurableEnvironment environment) {
|
||||
applyTo(environment, Type.MAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the properties from the underlying map to the environment using the specified
|
||||
* property source type.
|
||||
* @param environment the environment that needs to be modified
|
||||
* @param type the type of {@link PropertySource} to be added. See {@link Type}
|
||||
*/
|
||||
public void applyTo(ConfigurableEnvironment environment, Type type) {
|
||||
applyTo(environment, type, "test");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the properties from the underlying map to the environment using the specified
|
||||
* property source type and name.
|
||||
* @param environment the environment that needs to be modified
|
||||
* @param type the type of {@link PropertySource} to be added. See {@link Type}
|
||||
* @param name the name for the property source
|
||||
*/
|
||||
public void applyTo(ConfigurableEnvironment environment, Type type, String name) {
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
Assert.notNull(type, "Property source type must not be null");
|
||||
Assert.notNull(name, "Property source name must not be null");
|
||||
MutablePropertySources sources = environment.getPropertySources();
|
||||
addToSources(sources, type, name);
|
||||
ConfigurationPropertySources.attach(environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the properties to the {@link System#getProperties() system properties} for the
|
||||
* duration of the {@code call}, restoring previous values when the call completes.
|
||||
* @param <T> the result type
|
||||
* @param call the call to make
|
||||
* @return the result of the call
|
||||
*/
|
||||
public <T> T applyToSystemProperties(Callable<T> call) {
|
||||
try (SystemPropertiesHandler handler = new SystemPropertiesHandler()) {
|
||||
return call.call();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
rethrow(ex);
|
||||
throw new IllegalStateException("Original cause not rethrown", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <E extends Throwable> void rethrow(Throwable e) throws E {
|
||||
throw (E) e;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addToSources(MutablePropertySources sources, Type type, String name) {
|
||||
if (sources.contains(name)) {
|
||||
PropertySource<?> propertySource = sources.get(name);
|
||||
if (propertySource.getClass().equals(type.getSourceClass())) {
|
||||
((Map<String, Object>) propertySource.getSource())
|
||||
.putAll(this.properties);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Map<String, Object> source = new LinkedHashMap<>(this.properties);
|
||||
sources.addFirst((type.equals(Type.MAP) ? new MapPropertySource(name, source)
|
||||
: new SystemEnvironmentPropertySource(name, source)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TestPropertyValues} with the underlying map populated with the
|
||||
* given property pairs. Name-value pairs can be specified with colon (":") or equals
|
||||
* ("=") separators.
|
||||
* @param pairs the name-value pairs for properties that need to be added to the
|
||||
* environment
|
||||
* @return the new instance
|
||||
*/
|
||||
public static TestPropertyValues of(String... pairs) {
|
||||
return of(Stream.of(pairs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TestPropertyValues} with the underlying map populated with the
|
||||
* given property pairs. Name-value pairs can be specified with colon (":") or equals
|
||||
* ("=") separators.
|
||||
* @param pairs the name-value pairs for properties that need to be added to the
|
||||
* environment
|
||||
* @return the new instance
|
||||
*/
|
||||
public static TestPropertyValues of(Iterable<String> pairs) {
|
||||
if (pairs == null) {
|
||||
return empty();
|
||||
}
|
||||
return of(StreamSupport.stream(pairs.spliterator(), false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link TestPropertyValues} with the underlying map populated with the
|
||||
* given property pairs. Name-value pairs can be specified with colon (":") or equals
|
||||
* ("=") separators.
|
||||
* @param pairs the name-value pairs for properties that need to be added to the
|
||||
* environment
|
||||
* @return the new instance
|
||||
*/
|
||||
public static TestPropertyValues of(Stream<String> pairs) {
|
||||
if (pairs == null) {
|
||||
return empty();
|
||||
}
|
||||
return empty().and(pairs.map(Pair::parse));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an empty {@link TestPropertyValues} instance.
|
||||
* @return an empty instance
|
||||
*/
|
||||
public static TestPropertyValues empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of property source.
|
||||
*/
|
||||
public enum Type {
|
||||
|
||||
/**
|
||||
* Used for {@link SystemEnvironmentPropertySource}.
|
||||
*/
|
||||
SYSTEM(SystemEnvironmentPropertySource.class),
|
||||
|
||||
/**
|
||||
* Used for {@link MapPropertySource}.
|
||||
*/
|
||||
MAP(MapPropertySource.class);
|
||||
|
||||
private Class<? extends MapPropertySource> sourceClass;
|
||||
|
||||
Type(Class<? extends MapPropertySource> sourceClass) {
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
public Class<? extends MapPropertySource> getSourceClass() {
|
||||
return this.sourceClass;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A single name value pair.
|
||||
*/
|
||||
public static class Pair {
|
||||
|
||||
private String name;
|
||||
|
||||
private String value;
|
||||
|
||||
public Pair(String name, String value) {
|
||||
Assert.hasLength(name, "Name must not be empty");
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void addTo(Map<String, Object> properties) {
|
||||
properties.put(this.name, this.value);
|
||||
}
|
||||
|
||||
public static Pair parse(String pair) {
|
||||
int index = getSeparatorIndex(pair);
|
||||
String name = (index > 0 ? pair.substring(0, index) : pair);
|
||||
String value = (index > 0 ? pair.substring(index + 1) : "");
|
||||
return of(name.trim(), value.trim());
|
||||
}
|
||||
|
||||
private static int getSeparatorIndex(String pair) {
|
||||
int colonIndex = pair.indexOf(":");
|
||||
int equalIndex = pair.indexOf("=");
|
||||
if (colonIndex == -1) {
|
||||
return equalIndex;
|
||||
}
|
||||
if (equalIndex == -1) {
|
||||
return colonIndex;
|
||||
}
|
||||
return Math.min(colonIndex, equalIndex);
|
||||
}
|
||||
|
||||
private static Pair of(String name, String value) {
|
||||
if (StringUtils.isEmpty(name) && StringUtils.isEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
return new Pair(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler to apply and restore system properties.
|
||||
*/
|
||||
private class SystemPropertiesHandler implements Closeable {
|
||||
|
||||
private final Map<String, String> previous;
|
||||
|
||||
SystemPropertiesHandler() {
|
||||
this.previous = apply(TestPropertyValues.this.properties);
|
||||
}
|
||||
|
||||
private Map<String, String> apply(Map<String, ?> properties) {
|
||||
Map<String, String> previous = new LinkedHashMap<>();
|
||||
properties.forEach((name, value) -> previous.put(name,
|
||||
setOrClear(name, (String) value)));
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.previous.forEach(this::setOrClear);
|
||||
}
|
||||
|
||||
private String setOrClear(String name, String value) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return (String) System.getProperties().remove(name);
|
||||
}
|
||||
return (String) System.getProperties().setProperty(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* General purpose test utilities.
|
||||
*/
|
||||
package org.springframework.boot.test.util;
|
||||
@@ -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.web.client;
|
||||
|
||||
import org.springframework.boot.web.client.RootUriTemplateHandler;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
import org.springframework.web.util.UriTemplateHandler;
|
||||
|
||||
/**
|
||||
* {@link UriTemplateHandler} will automatically prefix relative URIs with
|
||||
* <code>localhost:${local.server.port}</code>.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
* @author Madhura Bhave
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class LocalHostUriTemplateHandler extends RootUriTemplateHandler {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final String scheme;
|
||||
|
||||
private final String prefix = "server.servlet.";
|
||||
|
||||
/**
|
||||
* Create a new {@code LocalHostUriTemplateHandler} that will generate {@code http}
|
||||
* URIs using the given {@code environment} to determine the context path and port.
|
||||
* @param environment the environment used to determine the port
|
||||
*/
|
||||
public LocalHostUriTemplateHandler(Environment environment) {
|
||||
this(environment, "http");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code LocalHostUriTemplateHandler} that will generate URIs with the
|
||||
* given {@code scheme} and use the given {@code environment} to determine the
|
||||
* context-path and port.
|
||||
* @param environment the environment used to determine the port
|
||||
* @param scheme the scheme of the root uri
|
||||
* @since 1.4.1
|
||||
*/
|
||||
public LocalHostUriTemplateHandler(Environment environment, String scheme) {
|
||||
super(new DefaultUriBuilderFactory());
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
Assert.notNull(scheme, "Scheme must not be null");
|
||||
this.environment = environment;
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootUri() {
|
||||
String port = this.environment.getProperty("local.server.port", "8080");
|
||||
String contextPath = this.environment.getProperty(this.prefix + "context-path",
|
||||
"");
|
||||
return this.scheme + "://localhost:" + port + contextPath;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.web.client;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.boot.web.client.RestTemplateCustomizer;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.test.web.client.RequestExpectationManager;
|
||||
import org.springframework.test.web.client.SimpleRequestExpectationManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* {@link RestTemplateCustomizer} that can be applied to a {@link RestTemplateBuilder}
|
||||
* instances to add {@link MockRestServiceServer} support.
|
||||
* <p>
|
||||
* Typically applied to an existing builder before it is used, for example:
|
||||
* <pre class="code">
|
||||
* MockServerRestTemplateCustomizer customizer = new MockServerRestTemplateCustomizer();
|
||||
* MyBean bean = new MyBean(new RestTemplateBuilder(customizer));
|
||||
* customizer.getServer().expect(requestTo("/hello")).andRespond(withSuccess());
|
||||
* bean.makeRestCall();
|
||||
* </pre>
|
||||
* <p>
|
||||
* If the customizer is only used once, the {@link #getServer()} method can be used to
|
||||
* obtain the mock server. If the customizer has been used more than once the
|
||||
* {@link #getServer(RestTemplate)} or {@link #getServers()} method must be used to access
|
||||
* the related server.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see #getServer()
|
||||
* @see #getServer(RestTemplate)
|
||||
*/
|
||||
public class MockServerRestTemplateCustomizer implements RestTemplateCustomizer {
|
||||
|
||||
private Map<RestTemplate, RequestExpectationManager> expectationManagers = new ConcurrentHashMap<>();
|
||||
|
||||
private Map<RestTemplate, MockRestServiceServer> servers = new ConcurrentHashMap<>();
|
||||
|
||||
private final Class<? extends RequestExpectationManager> expectationManager;
|
||||
|
||||
private boolean detectRootUri = true;
|
||||
|
||||
public MockServerRestTemplateCustomizer() {
|
||||
this.expectationManager = SimpleRequestExpectationManager.class;
|
||||
}
|
||||
|
||||
public MockServerRestTemplateCustomizer(
|
||||
Class<? extends RequestExpectationManager> expectationManager) {
|
||||
Assert.notNull(expectationManager, "ExpectationManager must not be null");
|
||||
this.expectationManager = expectationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if root URIs from {@link RootUriRequestExpectationManager} should be detected
|
||||
* and applied to the {@link MockRestServiceServer}.
|
||||
* @param detectRootUri if root URIs should be detected
|
||||
*/
|
||||
public void setDetectRootUri(boolean detectRootUri) {
|
||||
this.detectRootUri = detectRootUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(RestTemplate restTemplate) {
|
||||
RequestExpectationManager expectationManager = createExpectationManager();
|
||||
if (this.detectRootUri) {
|
||||
expectationManager = RootUriRequestExpectationManager
|
||||
.forRestTemplate(restTemplate, expectationManager);
|
||||
}
|
||||
MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate)
|
||||
.build(expectationManager);
|
||||
this.expectationManagers.put(restTemplate, expectationManager);
|
||||
this.servers.put(restTemplate, server);
|
||||
}
|
||||
|
||||
protected RequestExpectationManager createExpectationManager() {
|
||||
return BeanUtils.instantiateClass(this.expectationManager);
|
||||
}
|
||||
|
||||
public MockRestServiceServer getServer() {
|
||||
Assert.state(!this.servers.isEmpty(),
|
||||
"Unable to return a single MockRestServiceServer since "
|
||||
+ "MockServerRestTemplateCustomizer has not been bound to "
|
||||
+ "a RestTemplate");
|
||||
Assert.state(this.servers.size() == 1,
|
||||
"Unable to return a single MockRestServiceServer since "
|
||||
+ "MockServerRestTemplateCustomizer has been bound to "
|
||||
+ "more than one RestTemplate");
|
||||
return this.servers.values().iterator().next();
|
||||
}
|
||||
|
||||
public Map<RestTemplate, RequestExpectationManager> getExpectationManagers() {
|
||||
return this.expectationManagers;
|
||||
}
|
||||
|
||||
public MockRestServiceServer getServer(RestTemplate restTemplate) {
|
||||
return this.servers.get(restTemplate);
|
||||
}
|
||||
|
||||
public Map<RestTemplate, MockRestServiceServer> getServers() {
|
||||
return Collections.unmodifiableMap(this.servers);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.springframework.boot.web.client.RootUriTemplateHandler;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.support.HttpRequestWrapper;
|
||||
import org.springframework.mock.http.client.MockClientHttpRequest;
|
||||
import org.springframework.test.web.client.ExpectedCount;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.test.web.client.MockRestServiceServer.MockRestServiceServerBuilder;
|
||||
import org.springframework.test.web.client.RequestExpectationManager;
|
||||
import org.springframework.test.web.client.RequestMatcher;
|
||||
import org.springframework.test.web.client.ResponseActions;
|
||||
import org.springframework.test.web.client.SimpleRequestExpectationManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriTemplateHandler;
|
||||
|
||||
/**
|
||||
* {@link RequestExpectationManager} that strips the specified root URI from the request
|
||||
* before verification. Can be used to simply test declarations when all REST calls start
|
||||
* the same way. For example: <pre class="code">
|
||||
* RestTemplate restTemplate = new RestTemplateBuilder().rootUri("http://example.com").build();
|
||||
* MockRestServiceServer server = RootUriRequestExpectationManager.bindTo(restTemplate);
|
||||
* server.expect(requestTo("/hello")).andRespond(withSuccess());
|
||||
* restTemplate.getForEntity("/hello", String.class);
|
||||
* </pre>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see RootUriTemplateHandler
|
||||
* @see #bindTo(RestTemplate)
|
||||
* @see #forRestTemplate(RestTemplate, RequestExpectationManager)
|
||||
*/
|
||||
public class RootUriRequestExpectationManager implements RequestExpectationManager {
|
||||
|
||||
private final String rootUri;
|
||||
|
||||
private final RequestExpectationManager expectationManager;
|
||||
|
||||
public RootUriRequestExpectationManager(String rootUri,
|
||||
RequestExpectationManager expectationManager) {
|
||||
Assert.notNull(rootUri, "RootUri must not be null");
|
||||
Assert.notNull(expectationManager, "ExpectationManager must not be null");
|
||||
this.rootUri = rootUri;
|
||||
this.expectationManager = expectationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseActions expectRequest(ExpectedCount count,
|
||||
RequestMatcher requestMatcher) {
|
||||
return this.expectationManager.expectRequest(count, requestMatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse validateRequest(ClientHttpRequest request)
|
||||
throws IOException {
|
||||
String uri = request.getURI().toString();
|
||||
if (uri.startsWith(this.rootUri)) {
|
||||
request = replaceURI(request, uri.substring(this.rootUri.length()));
|
||||
}
|
||||
try {
|
||||
return this.expectationManager.validateRequest(request);
|
||||
}
|
||||
catch (AssertionError ex) {
|
||||
String message = ex.getMessage();
|
||||
String prefix = "Request URI expected:</";
|
||||
if (message != null && message.startsWith(prefix)) {
|
||||
throw new AssertionError("Request URI expected:<" + this.rootUri
|
||||
+ message.substring(prefix.length() - 1));
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private ClientHttpRequest replaceURI(ClientHttpRequest request,
|
||||
String replacementUri) {
|
||||
URI uri;
|
||||
try {
|
||||
uri = new URI(replacementUri);
|
||||
if (request instanceof MockClientHttpRequest) {
|
||||
((MockClientHttpRequest) request).setURI(uri);
|
||||
return request;
|
||||
}
|
||||
return new ReplaceUriClientHttpRequest(uri, request);
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify() {
|
||||
this.expectationManager.verify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.expectationManager.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bound {@link MockRestServiceServer} for the given {@link RestTemplate},
|
||||
* configured with {@link RootUriRequestExpectationManager} when possible.
|
||||
* @param restTemplate the source REST template
|
||||
* @return a configured {@link MockRestServiceServer}
|
||||
*/
|
||||
public static MockRestServiceServer bindTo(RestTemplate restTemplate) {
|
||||
return bindTo(restTemplate, new SimpleRequestExpectationManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bound {@link MockRestServiceServer} for the given {@link RestTemplate},
|
||||
* configured with {@link RootUriRequestExpectationManager} when possible.
|
||||
* @param restTemplate the source REST template
|
||||
* @param expectationManager the source {@link RequestExpectationManager}
|
||||
* @return a configured {@link MockRestServiceServer}
|
||||
*/
|
||||
public static MockRestServiceServer bindTo(RestTemplate restTemplate,
|
||||
RequestExpectationManager expectationManager) {
|
||||
MockRestServiceServerBuilder builder = MockRestServiceServer.bindTo(restTemplate);
|
||||
return builder.build(forRestTemplate(restTemplate, expectationManager));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link RequestExpectationManager} to be used for binding with the specified
|
||||
* {@link RestTemplate}. If the {@link RestTemplate} is using a
|
||||
* {@link RootUriTemplateHandler} then a {@link RootUriRequestExpectationManager} is
|
||||
* returned, otherwise the source manager is returned unchanged.
|
||||
* @param restTemplate the source REST template
|
||||
* @param expectationManager the source {@link RequestExpectationManager}
|
||||
* @return a {@link RequestExpectationManager} to be bound to the template
|
||||
*/
|
||||
public static RequestExpectationManager forRestTemplate(RestTemplate restTemplate,
|
||||
RequestExpectationManager expectationManager) {
|
||||
Assert.notNull(restTemplate, "RestTemplate must not be null");
|
||||
UriTemplateHandler templateHandler = restTemplate.getUriTemplateHandler();
|
||||
if (templateHandler instanceof RootUriTemplateHandler) {
|
||||
return new RootUriRequestExpectationManager(
|
||||
((RootUriTemplateHandler) templateHandler).getRootUri(),
|
||||
expectationManager);
|
||||
}
|
||||
return expectationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} wrapper to replace the request URI.
|
||||
*/
|
||||
private static class ReplaceUriClientHttpRequest extends HttpRequestWrapper
|
||||
implements ClientHttpRequest {
|
||||
|
||||
private final URI uri;
|
||||
|
||||
ReplaceUriClientHttpRequest(URI uri, ClientHttpRequest request) {
|
||||
super(request);
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getBody() throws IOException {
|
||||
return getRequest().getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse execute() throws IOException {
|
||||
return getRequest().execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpRequest getRequest() {
|
||||
return (ClientHttpRequest) super.getRequest();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Web client test utilities.
|
||||
*/
|
||||
package org.springframework.boot.test.web.client;
|
||||
@@ -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.web.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
|
||||
import com.gargoylesoftware.htmlunit.Page;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link WebClient} will automatically prefix relative URLs with
|
||||
* <code>localhost:${local.server.port}</code>.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class LocalHostWebClient extends WebClient {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public LocalHostWebClient(Environment environment) {
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P extends Page> P getPage(String url)
|
||||
throws IOException, FailingHttpStatusCodeException, MalformedURLException {
|
||||
if (url.startsWith("/")) {
|
||||
String port = this.environment.getProperty("local.server.port", "8080");
|
||||
url = "http://localhost:" + port + url;
|
||||
}
|
||||
return super.getPage(url);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* HtmlUnit support classes.
|
||||
*/
|
||||
package org.springframework.boot.test.web.htmlunit;
|
||||
@@ -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.web.htmlunit.webdriver;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.BrowserVersion;
|
||||
import org.openqa.selenium.Capabilities;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.web.servlet.htmlunit.webdriver.WebConnectionHtmlUnitDriver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link LocalHostWebConnectionHtmlUnitDriver} will automatically prefix relative URLs
|
||||
* with <code>localhost:${local.server.port}</code>.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class LocalHostWebConnectionHtmlUnitDriver extends WebConnectionHtmlUnitDriver {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public LocalHostWebConnectionHtmlUnitDriver(Environment environment) {
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public LocalHostWebConnectionHtmlUnitDriver(Environment environment,
|
||||
boolean enableJavascript) {
|
||||
super(enableJavascript);
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public LocalHostWebConnectionHtmlUnitDriver(Environment environment,
|
||||
BrowserVersion browserVersion) {
|
||||
super(browserVersion);
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public LocalHostWebConnectionHtmlUnitDriver(Environment environment,
|
||||
Capabilities capabilities) {
|
||||
super(capabilities);
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void get(String url) {
|
||||
if (url.startsWith("/")) {
|
||||
String port = this.environment.getProperty("local.server.port", "8080");
|
||||
url = "http://localhost:" + port + url;
|
||||
}
|
||||
super.get(url);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Selenium support classes.
|
||||
*/
|
||||
package org.springframework.boot.test.web.htmlunit.webdriver;
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.web.reactive;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.codec.CodecCustomizer;
|
||||
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizer} for {@link WebTestClient}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class WebTestClientContextCustomizer implements ContextCustomizer {
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
SpringBootTest annotation = AnnotatedElementUtils
|
||||
.getMergedAnnotation(mergedConfig.getTestClass(), SpringBootTest.class);
|
||||
if (annotation.webEnvironment().isEmbedded()) {
|
||||
registerWebTestClient(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerWebTestClient(ConfigurableApplicationContext context) {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
registerWebTestClient(context, (BeanDefinitionRegistry) context);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerWebTestClient(ConfigurableApplicationContext context,
|
||||
BeanDefinitionRegistry registry) {
|
||||
registry.registerBeanDefinition(WebTestClient.class.getName(),
|
||||
new RootBeanDefinition(WebTestClientFactory.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj != null && obj.getClass().equals(getClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} used to create and configure a {@link WebTestClient}.
|
||||
*/
|
||||
public static class WebTestClientFactory
|
||||
implements FactoryBean<WebTestClient>, ApplicationContextAware {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private WebTestClient object;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return WebTestClient.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebTestClient getObject() throws Exception {
|
||||
if (this.object == null) {
|
||||
this.object = createWebTestClient();
|
||||
}
|
||||
return this.object;
|
||||
}
|
||||
|
||||
private WebTestClient createWebTestClient() {
|
||||
boolean sslEnabled = isSslEnabled(this.applicationContext);
|
||||
String port = this.applicationContext.getEnvironment()
|
||||
.getProperty("local.server.port", "8080");
|
||||
String baseUrl = (sslEnabled ? "https" : "http") + "://localhost:" + port;
|
||||
WebTestClient.Builder builder = WebTestClient.bindToServer();
|
||||
customizeWebTestClientCodecs(builder, this.applicationContext);
|
||||
return builder.baseUrl(baseUrl).build();
|
||||
}
|
||||
|
||||
private boolean isSslEnabled(ApplicationContext context) {
|
||||
try {
|
||||
AbstractReactiveWebServerFactory webServerFactory = context
|
||||
.getBean(AbstractReactiveWebServerFactory.class);
|
||||
return webServerFactory.getSsl() != null
|
||||
&& webServerFactory.getSsl().isEnabled();
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder,
|
||||
ApplicationContext context) {
|
||||
Collection<CodecCustomizer> codecCustomizers = context
|
||||
.getBeansOfType(CodecCustomizer.class).values();
|
||||
if (!CollectionUtils.isEmpty(codecCustomizers)) {
|
||||
clientBuilder.exchangeStrategies(ExchangeStrategies.builder()
|
||||
.codecs((codecs) -> codecCustomizers.forEach(
|
||||
(codecCustomizer) -> codecCustomizer.customize(codecs)))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.web.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
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.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} for {@code WebTestClient}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class WebTestClientContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
private static final String WEB_TEST_CLIENT_CLASS = "org.springframework.web.reactive.function.client.WebClient";
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
if (isWebClientPresent() && AnnotatedElementUtils.findMergedAnnotation(testClass,
|
||||
SpringBootTest.class) != null) {
|
||||
return new WebTestClientContextCustomizer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isWebClientPresent() {
|
||||
return ClassUtils.isPresent(WEB_TEST_CLIENT_CLASS, getClass().getClassLoader());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Spring Test ContextCustomizerFactories
|
||||
org.springframework.test.context.ContextCustomizerFactory=\
|
||||
org.springframework.boot.test.context.ImportsContextCustomizerFactory,\
|
||||
org.springframework.boot.test.context.SpringBootTestContextCustomizerFactory,\
|
||||
org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizerFactory,\
|
||||
org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory,\
|
||||
org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory,\
|
||||
org.springframework.boot.test.web.reactive.WebTestClientContextCustomizerFactory
|
||||
|
||||
# Test Execution Listeners
|
||||
org.springframework.test.context.TestExecutionListener=\
|
||||
org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener,\
|
||||
org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Base class for {@link SpringBootTest} tests configured to start an embedded reactive
|
||||
* container.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Value("${value}")
|
||||
private int value = 0;
|
||||
|
||||
@Autowired
|
||||
private ReactiveWebApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private WebTestClient webClient;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
public ReactiveWebApplicationContext getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runAndTestHttpEndpoint() {
|
||||
assertThat(this.port).isNotEqualTo(8080).isNotEqualTo(0);
|
||||
WebTestClient.bindToServer().baseUrl("http://localhost:" + this.port).build()
|
||||
.get().uri("/").exchange().expectBody(String.class)
|
||||
.isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void injectWebTestClient() {
|
||||
this.webClient.get().uri("/").exchange().expectBody(String.class)
|
||||
.isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void injectTestRestTemplate() {
|
||||
String body = this.restTemplate.getForObject("/", String.class);
|
||||
assertThat(body).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotationAttributesOverridePropertiesFile() throws Exception {
|
||||
assertThat(this.value).isEqualTo(123);
|
||||
}
|
||||
|
||||
protected static class AbstractConfig {
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
private int port = 8080;
|
||||
|
||||
@Bean
|
||||
public HttpHandler httpHandler(ApplicationContext applicationContext) {
|
||||
return WebHttpHandlerBuilder.applicationContext(applicationContext).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveWebServerFactory webServerFactory() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
factory.setPort(this.port);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static PropertySourcesPlaceholderConfigurer propertyPlaceholder() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
public Mono<String> home() {
|
||||
return Mono.just("Hello World");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Base class for {@link SpringBootTest} tests configured to start an embedded web server.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class AbstractSpringBootTestWebServerWebEnvironmentTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Value("${value}")
|
||||
private int value = 0;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private ServletContext servletContext;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
public WebApplicationContext getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
public TestRestTemplate getRestTemplate() {
|
||||
return this.restTemplate;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runAndTestHttpEndpoint() {
|
||||
assertThat(this.port).isNotEqualTo(8080).isNotEqualTo(0);
|
||||
String body = new RestTemplate()
|
||||
.getForObject("http://localhost:" + this.port + "/", String.class);
|
||||
assertThat(body).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void injectTestRestTemplate() {
|
||||
String body = this.restTemplate.getForObject("/", String.class);
|
||||
assertThat(body).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotationAttributesOverridePropertiesFile() throws Exception {
|
||||
assertThat(this.value).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateWebApplicationContextIsSet() {
|
||||
assertThat(this.context).isSameAs(
|
||||
WebApplicationContextUtils.getWebApplicationContext(this.servletContext));
|
||||
}
|
||||
|
||||
protected static class AbstractConfig {
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
private int port = 8080;
|
||||
|
||||
@Bean
|
||||
public DispatcherServlet dispatcherServlet() {
|
||||
return new DispatcherServlet();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletWebServerFactory webServerFactory() {
|
||||
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
|
||||
factory.setPort(this.port);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static PropertySourcesPlaceholderConfigurer propertyPlaceholder() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
public String home() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigFileApplicationContextInitializer}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
@ContextConfiguration(classes = ConfigFileApplicationContextInitializerTests.Config.class, initializers = ConfigFileApplicationContextInitializer.class)
|
||||
public class ConfigFileApplicationContextInitializerTests {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Test
|
||||
public void initializerPopulatesEnvironment() {
|
||||
assertThat(this.environment.getProperty("foo")).isEqualTo("bucket");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.ImportsContextCustomizerFactoryIntegrationTests.ImportedBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ImportsContextCustomizerFactory} and
|
||||
* {@link ImportsContextCustomizer}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@Import(ImportedBean.class)
|
||||
public class ImportsContextCustomizerFactoryIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private ImportedBean bean;
|
||||
|
||||
@Test
|
||||
public void beanWasImported() throws Exception {
|
||||
assertThat(this.bean).isNotNull();
|
||||
}
|
||||
|
||||
@Test(expected = NoSuchBeanDefinitionException.class)
|
||||
public void testItselfIsNotABean() throws Exception {
|
||||
this.context.getBean(getClass());
|
||||
}
|
||||
|
||||
@Component
|
||||
static class ImportedBean {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ImportsContextCustomizerFactory} and {@link ImportsContextCustomizer}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ImportsContextCustomizerFactoryTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private ImportsContextCustomizerFactory factory = new ImportsContextCustomizerFactory();
|
||||
|
||||
@Test
|
||||
public void getContextCustomizerWhenHasNoImportAnnotationShouldReturnNull() {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(TestWithNoImport.class, null);
|
||||
assertThat(customizer).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContextCustomizerWhenHasImportAnnotationShouldReturnCustomizer() {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(TestWithImport.class, null);
|
||||
assertThat(customizer).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContextCustomizerWhenHasMetaImportAnnotationShouldReturnCustomizer() {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(TestWithMetaImport.class, null);
|
||||
assertThat(customizer).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextCustomizerEqualsAndHashCode() throws Exception {
|
||||
ContextCustomizer customizer1 = this.factory
|
||||
.createContextCustomizer(TestWithImport.class, null);
|
||||
ContextCustomizer customizer2 = this.factory
|
||||
.createContextCustomizer(TestWithImport.class, null);
|
||||
ContextCustomizer customizer3 = this.factory
|
||||
.createContextCustomizer(TestWithImportAndMetaImport.class, null);
|
||||
ContextCustomizer customizer4 = this.factory
|
||||
.createContextCustomizer(TestWithSameImportAndMetaImport.class, null);
|
||||
assertThat(customizer1.hashCode()).isEqualTo(customizer1.hashCode());
|
||||
assertThat(customizer1.hashCode()).isEqualTo(customizer2.hashCode());
|
||||
assertThat(customizer1).isEqualTo(customizer1).isEqualTo(customizer2)
|
||||
.isNotEqualTo(customizer3);
|
||||
assertThat(customizer3).isEqualTo(customizer4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContextCustomizerWhenClassHasBeanMethodsShouldThrowException()
|
||||
throws Exception {
|
||||
this.thrown.expect(IllegalStateException.class);
|
||||
this.thrown.expectMessage("Test classes cannot include @Bean methods");
|
||||
this.factory.createContextCustomizer(TestWithImportAndBeanMethod.class, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextCustomizerImportsBeans() throws Exception {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(TestWithImport.class, null);
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
customizer.customizeContext(context, mock(MergedContextConfiguration.class));
|
||||
context.refresh();
|
||||
assertThat(context.getBean(ImportedBean.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selfAnnotatingAnnotationDoesNotCauseStackOverflow() {
|
||||
assertThat(this.factory.createContextCustomizer(
|
||||
TestWithImportAndSelfAnnotatingAnnotation.class, null)).isNotNull();
|
||||
}
|
||||
|
||||
static class TestWithNoImport {
|
||||
|
||||
}
|
||||
|
||||
@Import(ImportedBean.class)
|
||||
static class TestWithImport {
|
||||
|
||||
}
|
||||
|
||||
@MetaImport
|
||||
static class TestWithMetaImport {
|
||||
|
||||
}
|
||||
|
||||
@MetaImport
|
||||
@Import(AnotherImportedBean.class)
|
||||
static class TestWithImportAndMetaImport {
|
||||
|
||||
}
|
||||
|
||||
@MetaImport
|
||||
@Import(AnotherImportedBean.class)
|
||||
static class TestWithSameImportAndMetaImport {
|
||||
|
||||
}
|
||||
|
||||
@Import(ImportedBean.class)
|
||||
static class TestWithImportAndBeanMethod {
|
||||
|
||||
@Bean
|
||||
public String bean() {
|
||||
return "bean";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SelfAnnotating
|
||||
@Import(ImportedBean.class)
|
||||
static class TestWithImportAndSelfAnnotatingAnnotation {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(ImportedBean.class)
|
||||
@interface MetaImport {
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
static class ImportedBean {
|
||||
|
||||
}
|
||||
|
||||
@Component
|
||||
static class AnotherImportedBean {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@SelfAnnotating
|
||||
static @interface SelfAnnotating {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import kotlin.Metadata;
|
||||
import org.junit.Test;
|
||||
import org.spockframework.runtime.model.SpecMetadata;
|
||||
import spock.lang.Issue;
|
||||
import spock.lang.Stepwise;
|
||||
|
||||
import org.springframework.boot.context.annotation.DeterminableImports;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ImportsContextCustomizer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ImportsContextCustomizerTests {
|
||||
|
||||
@Test
|
||||
public void importSelectorsCouldUseAnyAnnotations() throws Exception {
|
||||
assertThat(new ImportsContextCustomizer(FirstImportSelectorAnnotatedClass.class))
|
||||
.isNotEqualTo(new ImportsContextCustomizer(
|
||||
SecondImportSelectorAnnotatedClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void determinableImportSelector() throws Exception {
|
||||
assertThat(new ImportsContextCustomizer(
|
||||
FirstDeterminableImportSelectorAnnotatedClass.class))
|
||||
.isEqualTo(new ImportsContextCustomizer(
|
||||
SecondDeterminableImportSelectorAnnotatedClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizersForTestClassesWithDifferentKotlinMetadataAreEqual() {
|
||||
assertThat(new ImportsContextCustomizer(FirstKotlinAnnotatedTestClass.class))
|
||||
.isEqualTo(new ImportsContextCustomizer(
|
||||
SecondKotlinAnnotatedTestClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizersForTestClassesWithDifferentSpockFrameworkAnnotationsAreEqual() {
|
||||
assertThat(
|
||||
new ImportsContextCustomizer(FirstSpockFrameworkAnnotatedTestClass.class))
|
||||
.isEqualTo(new ImportsContextCustomizer(
|
||||
SecondSpockFrameworkAnnotatedTestClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizersForTestClassesWithDifferentSpockLangAnnotationsAreEqual() {
|
||||
assertThat(new ImportsContextCustomizer(FirstSpockLangAnnotatedTestClass.class))
|
||||
.isEqualTo(new ImportsContextCustomizer(
|
||||
SecondSpockLangAnnotatedTestClass.class));
|
||||
}
|
||||
|
||||
@Import(TestImportSelector.class)
|
||||
@Indicator1
|
||||
static class FirstImportSelectorAnnotatedClass {
|
||||
|
||||
}
|
||||
|
||||
@Import(TestImportSelector.class)
|
||||
@Indicator2
|
||||
static class SecondImportSelectorAnnotatedClass {
|
||||
|
||||
}
|
||||
|
||||
@Import(TestDeterminableImportSelector.class)
|
||||
@Indicator1
|
||||
static class FirstDeterminableImportSelectorAnnotatedClass {
|
||||
|
||||
}
|
||||
|
||||
@Import(TestDeterminableImportSelector.class)
|
||||
@Indicator2
|
||||
static class SecondDeterminableImportSelectorAnnotatedClass {
|
||||
|
||||
}
|
||||
|
||||
@Metadata(d2 = "foo")
|
||||
static class FirstKotlinAnnotatedTestClass {
|
||||
|
||||
}
|
||||
|
||||
@Metadata(d2 = "bar")
|
||||
static class SecondKotlinAnnotatedTestClass {
|
||||
|
||||
}
|
||||
|
||||
@SpecMetadata(filename = "foo", line = 10)
|
||||
static class FirstSpockFrameworkAnnotatedTestClass {
|
||||
|
||||
}
|
||||
|
||||
@SpecMetadata(filename = "bar", line = 10)
|
||||
static class SecondSpockFrameworkAnnotatedTestClass {
|
||||
|
||||
}
|
||||
|
||||
@Stepwise
|
||||
static class FirstSpockLangAnnotatedTestClass {
|
||||
|
||||
}
|
||||
|
||||
@Issue("1234")
|
||||
static class SecondSpockLangAnnotatedTestClass {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Indicator1 {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Indicator2 {
|
||||
|
||||
}
|
||||
|
||||
static class TestImportSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata arg0) {
|
||||
return new String[] {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestDeterminableImportSelector
|
||||
implements ImportSelector, DeterminableImports {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata arg0) {
|
||||
return new String[] { TestConfig.class.getName() };
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> determineImports(AnnotationMetadata metadata) {
|
||||
return Collections.<Object>singleton(TestConfig.class.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.test.context.example.ExampleConfig;
|
||||
import org.springframework.boot.test.context.example.scan.Example;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootConfigurationFinder}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringBootConfigurationFinderTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private SpringBootConfigurationFinder finder = new SpringBootConfigurationFinder();
|
||||
|
||||
@Test
|
||||
public void findFromClassWhenSourceIsNullShouldThrowException() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Source must not be null");
|
||||
this.finder.findFromClass((Class<?>) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromPackageWhenSourceIsNullShouldThrowException() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Source must not be null");
|
||||
this.finder.findFromPackage((String) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromPackageWhenNoConfigurationFoundShouldReturnNull() {
|
||||
Class<?> config = this.finder.findFromPackage("org.springframework.boot");
|
||||
assertThat(config).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromClassWhenConfigurationIsFoundShouldReturnConfiguration() {
|
||||
Class<?> config = this.finder.findFromClass(Example.class);
|
||||
assertThat(config).isEqualTo(ExampleConfig.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromPackageWhenConfigurationIsFoundShouldReturnConfiguration() {
|
||||
Class<?> config = this.finder
|
||||
.findFromPackage("org.springframework.boot.test.context.example.scan");
|
||||
assertThat(config).isEqualTo(ExampleConfig.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link WebAppConfiguration} integration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
@ContextConfiguration(loader = SpringBootContextLoader.class)
|
||||
@WebAppConfiguration
|
||||
public class SpringBootContextLoaderMockMvcTests {
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private ServletContext servletContext;
|
||||
|
||||
private MockMvc mvc;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMockHttpEndpoint() throws Exception {
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk())
|
||||
.andExpect(content().string("Hello World"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateWebApplicationContextIsSet() {
|
||||
assertThat(this.context).isSameAs(
|
||||
WebApplicationContextUtils.getWebApplicationContext(this.servletContext));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@RestController
|
||||
protected static class Config {
|
||||
|
||||
@RequestMapping("/")
|
||||
public String home() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootContextLoader}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class SpringBootContextLoaderTests {
|
||||
|
||||
@Test
|
||||
public void environmentPropertiesSimple() throws Exception {
|
||||
Map<String, Object> config = getEnvironmentProperties(SimpleConfig.class);
|
||||
assertKey(config, "key", "myValue");
|
||||
assertKey(config, "anotherKey", "anotherValue");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentPropertiesSimpleNonAlias() throws Exception {
|
||||
Map<String, Object> config = getEnvironmentProperties(SimpleConfigNonAlias.class);
|
||||
assertKey(config, "key", "myValue");
|
||||
assertKey(config, "anotherKey", "anotherValue");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentPropertiesOverrideDefaults() throws Exception {
|
||||
Map<String, Object> config = getEnvironmentProperties(OverrideConfig.class);
|
||||
assertKey(config, "server.port", "2345");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentPropertiesAppend() throws Exception {
|
||||
Map<String, Object> config = getEnvironmentProperties(AppendConfig.class);
|
||||
assertKey(config, "key", "myValue");
|
||||
assertKey(config, "otherKey", "otherValue");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentPropertiesSeparatorInValue() throws Exception {
|
||||
Map<String, Object> config = getEnvironmentProperties(SameSeparatorInValue.class);
|
||||
assertKey(config, "key", "my=Value");
|
||||
assertKey(config, "anotherKey", "another:Value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void environmentPropertiesAnotherSeparatorInValue() throws Exception {
|
||||
Map<String, Object> config = getEnvironmentProperties(
|
||||
AnotherSeparatorInValue.class);
|
||||
assertKey(config, "key", "my:Value");
|
||||
assertKey(config, "anotherKey", "another=Value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void environmentPropertiesNewLineInValue() throws Exception {
|
||||
// gh-4384
|
||||
Map<String, Object> config = getEnvironmentProperties(NewLineInValue.class);
|
||||
assertKey(config, "key", "myValue");
|
||||
assertKey(config, "variables", "foo=FOO\n bar=BAR");
|
||||
}
|
||||
|
||||
private Map<String, Object> getEnvironmentProperties(Class<?> testClass)
|
||||
throws Exception {
|
||||
TestContext context = new ExposedTestContextManager(testClass)
|
||||
.getExposedTestContext();
|
||||
MergedContextConfiguration config = (MergedContextConfiguration) ReflectionTestUtils
|
||||
.getField(context, "mergedContextConfiguration");
|
||||
return TestPropertySourceUtils
|
||||
.convertInlinedPropertiesToMap(config.getPropertySourceProperties());
|
||||
}
|
||||
|
||||
private void assertKey(Map<String, Object> actual, String key, Object value) {
|
||||
assertThat(actual.containsKey(key)).as("Key '" + key + "' not found").isTrue();
|
||||
assertThat(actual.get(key)).isEqualTo(value);
|
||||
}
|
||||
|
||||
@SpringBootTest({ "key=myValue", "anotherKey:anotherValue" })
|
||||
@ContextConfiguration(classes = Config.class)
|
||||
static class SimpleConfig {
|
||||
|
||||
}
|
||||
|
||||
@SpringBootTest(properties = { "key=myValue", "anotherKey:anotherValue" })
|
||||
@ContextConfiguration(classes = Config.class)
|
||||
static class SimpleConfigNonAlias {
|
||||
|
||||
}
|
||||
|
||||
@SpringBootTest("server.port=2345")
|
||||
@ContextConfiguration(classes = Config.class)
|
||||
static class OverrideConfig {
|
||||
|
||||
}
|
||||
|
||||
@SpringBootTest({ "key=myValue", "otherKey=otherValue" })
|
||||
@ContextConfiguration(classes = Config.class)
|
||||
static class AppendConfig {
|
||||
|
||||
}
|
||||
|
||||
@SpringBootTest({ "key=my=Value", "anotherKey:another:Value" })
|
||||
@ContextConfiguration(classes = Config.class)
|
||||
static class SameSeparatorInValue {
|
||||
|
||||
}
|
||||
|
||||
@SpringBootTest({ "key=my:Value", "anotherKey:another=Value" })
|
||||
@ContextConfiguration(classes = Config.class)
|
||||
static class AnotherSeparatorInValue {
|
||||
|
||||
}
|
||||
|
||||
@SpringBootTest({ "key=myValue", "variables=foo=FOO\n bar=BAR" })
|
||||
@ContextConfiguration(classes = Config.class)
|
||||
static class NewLineInValue {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TestContextManager} which exposes the {@link TestContext}.
|
||||
*/
|
||||
private static class ExposedTestContextManager extends TestContextManager {
|
||||
|
||||
ExposedTestContextManager(Class<?> testClass) {
|
||||
super(testClass);
|
||||
}
|
||||
|
||||
public final TestContext getExposedTestContext() {
|
||||
return super.getTestContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest} with active profiles. See gh-1469.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
@SpringBootTest("spring.config.name=enableother")
|
||||
@ActiveProfiles("override")
|
||||
public class SpringBootTestActiveProfileTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void profiles() throws Exception {
|
||||
assertThat(this.context.getEnvironment().getActiveProfiles())
|
||||
.containsExactly("override");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTestContextHierarchyTests.ChildConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTestContextHierarchyTests.ParentConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest} and {@link ContextHierarchy}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@SpringBootTest
|
||||
@ContextHierarchy({ @ContextConfiguration(classes = ParentConfiguration.class),
|
||||
@ContextConfiguration(classes = ChildConfiguration.class) })
|
||||
@RunWith(SpringRunner.class)
|
||||
public class SpringBootTestContextHierarchyTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ParentConfiguration {
|
||||
|
||||
@Bean
|
||||
MyBean myBean() {
|
||||
return new MyBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ChildConfiguration {
|
||||
|
||||
ChildConfiguration(MyBean myBean) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyBean {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@SpringBootTest} with a custom config name
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(properties = "spring.config.name=custom-config-name")
|
||||
public class SpringBootTestCustomConfigNameTests {
|
||||
|
||||
@Value("${test.foo}")
|
||||
private String foo;
|
||||
|
||||
@Test
|
||||
public void propertyIsLoadedFromConfigFileWithCustomName() {
|
||||
assertThat(this.foo).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestConfiguration {
|
||||
|
||||
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test for {@link SpringBootTest} with a custom inline server.port in a non-embedded web
|
||||
* environment.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(properties = "server.port=12345")
|
||||
public class SpringBootTestCustomPortTests {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Test
|
||||
public void validatePortIsNotOverwritten() {
|
||||
String port = this.environment.getProperty("server.port");
|
||||
assertThat(port).isEqualTo("12345");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest} (detectDefaultConfigurationClasses).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
public class SpringBootTestDefaultConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
private Config config;
|
||||
|
||||
@Test
|
||||
public void nestedConfigClasses() {
|
||||
assertThat(this.config).isNotNull();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest} (detectDefaultConfigurationClasses).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext
|
||||
@SpringBootTest
|
||||
@ContextConfiguration(locations = "classpath:test.groovy")
|
||||
public class SpringBootTestGroovyConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Test
|
||||
public void groovyConfigLoaded() {
|
||||
assertThat(this.foo).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest} finding groovy config.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@DirtiesContext
|
||||
public class SpringBootTestGroovyConventionConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
private String foo;
|
||||
|
||||
@Test
|
||||
public void groovyConfigLoaded() {
|
||||
assertThat(this.foo).isEqualTo("World");
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user