diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/AbstractApplicationContextTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AbstractApplicationContextTester.java new file mode 100644 index 0000000000..6bfe9060f5 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AbstractApplicationContextTester.java @@ -0,0 +1,290 @@ +/* + * 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.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.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; +import org.springframework.util.ReflectionUtils; + +/** + * Tester utility design to manage the lifecycle of 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: + * + *
+ * public class MyContextTests {
+ *     private final ApplicationContextTester context = new ApplicationContextTester()
+ *             .withPropertyValues("spring.foo=bar")
+ *             .withUserConfiguration(MyConfiguration.class);
+ * }
+ * + *

+ * 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. + *

+ * Based on the configuration above, a specific test can simulate what will happen when + * the context runs, perhaps with overridden property values: + * + *

+ * @Test
+ * public someTest() {
+ *     this.contex.withPropertyValues("spring.foo=biz").run((loaded) -> {
+ *         assertThat(loaded).containsSingleBean(MyBean.class);
+ *         // other assertions
+ *     });
+ * }
+ *

+ * 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. + *

+ * 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:

+ * @Test
+ * public someTest() {
+ *     this.contex.withPropertyValues("spring.foo=fails").run((loaded) -> {
+ *         assertThat(loaded).getFailure().hasCauseInstanceOf(BadPropertyExcepton.class);
+ *         // other assertions
+ *     });
+ * }
+ *

+ * + * @param The "self" type for this tester + * @param The context type + * @param The application context assertion provider + * @author Stephane Nicoll + * @author Andy Wilkinson + * @author Phillip Webb + * @since 2.0.0 + * @see ApplicationContextTester + * @see WebApplicationContextTester + * @see ReactiveWebApplicationContextTester + * @see ApplicationContextAssert + */ +abstract class AbstractApplicationContextTester, C extends ConfigurableApplicationContext, A extends AssertProviderApplicationContext> { + + private final Supplier contextFactory; + + private final TestPropertyValues environmentProperties; + + private final TestPropertyValues systemProperties; + + private ClassLoader classLoader; + + private ApplicationContext parent; + + private final List configurations = new ArrayList<>(); + + /** + * Create a new {@link AbstractApplicationContextTester} instance. + * @param contextFactory the factory used to create the actual context + */ + protected AbstractApplicationContextTester(Supplier contextFactory) { + Assert.notNull(contextFactory, "ContextFactory must not be null"); + this.contextFactory = contextFactory; + this.environmentProperties = TestPropertyValues.empty(); + this.systemProperties = TestPropertyValues.empty(); + } + + /** + * 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 this instance + * @see TestPropertyValues + * @see #withSystemProperties(String...) + */ + public SELF withPropertyValues(String... pairs) { + Arrays.stream(pairs).forEach(this.environmentProperties::and); + return self(); + } + + /** + * Add the specified {@link Environment} property. + * @param name the name of the property + * @param value the value of the property + * @return this instance + * @see TestPropertyValues + * @see #withSystemProperties(String...) + */ + public SELF withPropertyValue(String name, String value) { + this.environmentProperties.and(name, value); + return self(); + } + + /** + * 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 this instance + * @see TestPropertyValues + * @see #withSystemProperties(String...) + */ + public SELF withSystemProperties(String... pairs) { + Arrays.stream(pairs).forEach(this.systemProperties::and); + return self(); + } + + /** + * Add the specified {@link System} property. System properties are added before the + * context is {@link #run(ContextConsumer) run} and restored when the context is + * closed. + * @param name the property name + * @param value the property value + * @return this instance + * @see TestPropertyValues + * @see #withSystemProperties(String...) + */ + public SELF withSystemProperty(String name, String value) { + this.systemProperties.and(name, value); + return self(); + } + + /** + * 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 this instance + * @see HidePackagesClassLoader + */ + public SELF withClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + return self(); + } + + /** + * Configure the {@link ConfigurableApplicationContext#setParent(ApplicationContext) + * parent} of the {@link ApplicationContext}. + * @param parent the parent + * @return this instance + */ + public SELF withParent(ApplicationContext parent) { + this.parent = parent; + return self(); + } + + /** + * Register the specified user configuration classes with the + * {@link ApplicationContext}. + * @param configurationClasses the user configuration classes to add + * @return this instance + */ + 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 this instance + */ + public SELF withConfiguration(Configurations configurations) { + Assert.notNull(configurations, "Configurations must not be null"); + this.configurations.add(configurations); + return self(); + } + + @SuppressWarnings("unchecked") + protected final SELF self() { + return (SELF) this; + } + + /** + * 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} + */ + public void run(ContextConsumer consumer) { + this.systemProperties.applyToSystemProperties(() -> { + try (A context = createAssertableContext()) { + accept(consumer, context); + } + return null; + }); + } + + @SuppressWarnings("unchecked") + private A createAssertableContext() { + ResolvableType resolvableType = ResolvableType + .forClass(AbstractApplicationContextTester.class, getClass()); + Class assertType = (Class) resolvableType.resolveGeneric(1); + Class contextType = (Class) resolvableType.resolveGeneric(2); + return AssertProviderApplicationContext.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 consumer, A context) { + try { + consumer.accept(context); + } + catch (Throwable ex) { + ReflectionUtils.rethrowRuntimeException(ex); + } + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/AbstractContextLoader.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AbstractContextLoader.java deleted file mode 100644 index adb8987b83..0000000000 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/AbstractContextLoader.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * 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.io.Closeable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Supplier; - -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.io.DefaultResourceLoader; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Base implementation of {@link ContextLoader}. - * - * @param the type of the context to be loaded - * @param the type of the loader - * @author Stephane Nicoll - * @author Andy Wilkinson - */ -class AbstractContextLoader> - implements ContextLoader { - - private final Map systemProperties = new LinkedHashMap<>(); - - private final List env = new ArrayList<>(); - - private final Set> userConfigurations = new LinkedHashSet<>(); - - private final LinkedList> autoConfigurations = new LinkedList<>(); - - private final Supplier contextSupplier; - - private ClassLoader classLoader; - - private ApplicationContext parent; - - protected AbstractContextLoader(Supplier contextSupplier) { - this.contextSupplier = contextSupplier; - } - - /** - * Set the specified system property prior to loading the context and restore its - * previous value once the consumer has been invoked and the context closed. If the - * {@code value} is {@code null} this removes any prior customization for that key. - * @param key the system property - * @param value the value (can be null to remove any existing customization) - * @return this instance - */ - @Override - public L systemProperty(String key, String value) { - Assert.notNull(key, "Key must not be null"); - if (value != null) { - this.systemProperties.put(key, value); - } - else { - this.systemProperties.remove(key); - } - return self(); - } - - /** - * Add the specified 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 this instance - */ - @Override - public L env(String... pairs) { - if (!ObjectUtils.isEmpty(pairs)) { - this.env.addAll(Arrays.asList(pairs)); - } - return self(); - } - - /** - * Add the specified user configuration classes. - * @param configs the user configuration classes to add - * @return this instance - */ - @Override - public L config(Class... configs) { - if (!ObjectUtils.isEmpty(configs)) { - this.userConfigurations.addAll(Arrays.asList(configs)); - } - return self(); - } - - @Override - public L parent(ApplicationContext parent) { - this.parent = parent; - return self(); - } - - /** - * Add the specified auto-configuration classes. - * @param autoConfigurations the auto-configuration classes to add - * @return this instance - */ - @Override - public L autoConfig(Class... autoConfigurations) { - if (!ObjectUtils.isEmpty(autoConfigurations)) { - this.autoConfigurations.addAll(Arrays.asList(autoConfigurations)); - } - return self(); - } - - /** - * Add the specified auto-configurations at the beginning (in that order) so that it - * is applied before any other existing auto-configurations, but after any user - * configuration. If {@code A} and {@code B} are specified, {@code A} will be - * processed, then {@code B} and finally the rest of the existing auto-configuration. - * @param autoConfigurations the auto-configuration to add - * @return this instance - */ - @Override - public L autoConfigFirst(Class... autoConfigurations) { - this.autoConfigurations.addAll(0, Arrays.asList(autoConfigurations)); - return self(); - } - - /** - * 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 this instance - * @see HidePackagesClassLoader - */ - @Override - public L classLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - return self(); - } - - @SuppressWarnings("unchecked") - protected final L self() { - return (L) this; - } - - /** - * Create and refresh a new {@link ApplicationContext} based on the current state of - * this loader. The context is consumed by the specified {@link ContextConsumer} and - * closed upon completion. - * @param consumer the consumer of the created {@link ApplicationContext} - */ - @Override - public void load(ContextConsumer consumer) { - doLoad(consumer::accept); - } - - protected void doLoad(ContextHandler contextHandler) { - try (ApplicationContextLifecycleHandler handler = new ApplicationContextLifecycleHandler()) { - try { - T ctx = handler.load(); - contextHandler.handle(ctx); - } - catch (RuntimeException | AssertionError ex) { - throw ex; - } - catch (Throwable ex) { - throw new IllegalStateException( - "An unexpected error occurred: " + ex.getMessage(), ex); - } - } - } - - /** - * Create and refresh a new {@link ApplicationContext} based on the current state of - * this loader that this expected to fail. If the context does not fail, an - * {@link AssertionError} is thrown. Otherwise the exception is consumed by the - * specified {@link Consumer} with no expectation on the type of the exception. - * @param consumer the consumer of the failure - */ - @Override - public void loadAndFail(Consumer consumer) { - loadAndFail(Throwable.class, consumer); - } - - /** - * Create and refresh a new {@link ApplicationContext} based on the current state of - * this loader that this expected to fail. If the context does not fail, an - * {@link AssertionError} is thrown. If the exception does not match the specified - * {@code exceptionType}, an {@link AssertionError} is thrown as well. If the - * exception type matches, it is consumed by the specified {@link Consumer}. - * @param exceptionType the expected type of the failure - * @param consumer the consumer of the failure - * @param the expected type of the failure - */ - @Override - public void loadAndFail(Class exceptionType, - Consumer consumer) { - try (ApplicationContextLifecycleHandler handler = new ApplicationContextLifecycleHandler()) { - handler.load(); - throw new AssertionError("ApplicationContext should have failed"); - } - catch (Throwable ex) { - assertThat(ex).as("Wrong application context failure exception") - .isInstanceOf(exceptionType); - consumer.accept(exceptionType.cast(ex)); - } - } - - private T configureApplicationContext() { - T context = AbstractContextLoader.this.contextSupplier.get(); - if (this.parent != null) { - context.setParent(this.parent); - } - if (this.classLoader != null) { - Assert.isInstanceOf(DefaultResourceLoader.class, context); - ((DefaultResourceLoader) context).setClassLoader(this.classLoader); - } - if (!ObjectUtils.isEmpty(this.env)) { - TestPropertyValues.of(this.env.toArray(new String[this.env.size()])) - .applyTo(context); - } - if (!ObjectUtils.isEmpty(this.userConfigurations)) { - ((AnnotationConfigRegistry) context).register(this.userConfigurations - .toArray(new Class[this.userConfigurations.size()])); - } - if (!ObjectUtils.isEmpty(this.autoConfigurations)) { - LinkedHashSet> linkedHashSet = new LinkedHashSet<>( - this.autoConfigurations); - ((AnnotationConfigRegistry) context).register( - linkedHashSet.toArray(new Class[this.autoConfigurations.size()])); - } - return context; - } - - /** - * An internal callback interface that handles a concrete {@link ApplicationContext} - * type. - * @param the type of the application context - */ - protected interface ContextHandler { - - void handle(T context) throws Throwable; - - } - - /** - * Handles the lifecycle of the {@link ApplicationContext}. - */ - private class ApplicationContextLifecycleHandler implements Closeable { - - private final Map customSystemProperties; - - private final Map previousSystemProperties = new HashMap<>(); - - private ConfigurableApplicationContext context; - - ApplicationContextLifecycleHandler() { - this.customSystemProperties = new HashMap<>( - AbstractContextLoader.this.systemProperties); - } - - public T load() { - setCustomSystemProperties(); - T context = configureApplicationContext(); - context.refresh(); - this.context = context; - return context; - } - - @Override - public void close() { - try { - if (this.context != null) { - this.context.close(); - } - } - finally { - unsetCustomSystemProperties(); - } - } - - private void setCustomSystemProperties() { - this.customSystemProperties.forEach((key, value) -> { - String previous = System.setProperty(key, value); - this.previousSystemProperties.put(key, previous); - }); - } - - private void unsetCustomSystemProperties() { - this.previousSystemProperties.forEach((key, value) -> { - if (value != null) { - System.setProperty(key, value); - } - else { - System.clearProperty(key); - } - }); - } - - } - -} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ApplicationContextAssert.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ApplicationContextAssert.java new file mode 100644 index 0000000000..364cfbb4fb --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ApplicationContextAssert.java @@ -0,0 +1,374 @@ +/* + * 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.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.context.ApplicationContext; +import org.springframework.util.Assert; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * AspectJ {@link org.assertj.core.api.Assert assertions} that can be applied to an + * {@link ApplicationContext}. + * + * @param The application context type + * @author Phillip Webb + * @since 2.0.0 + * @see ApplicationContextTester + * @see AssertableApplicationContext + */ +public class ApplicationContextAssert + extends AbstractAssert, 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. + *

+ * Example:

+	 * assertThat(context).hasBean("fooBean"); 
+ * @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 hasBean(String name) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nto have bean named:%n <%s>%nbut context failed to start", + getApplicationContext(), 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. + *

+ * Example:

+	 * assertThat(context).hasSingleBean(Foo.class); 
+ * @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 hasSingleBean(Class type) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nto have a single bean of type:%n <%s>%nbut context failed to start", + getApplicationContext(), 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. + *

+ * Example:

+	 * assertThat(context).doesNotHaveBean(Foo.class); 
+ * @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 doesNotHaveBean(Class type) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nnot to have any beans of type:%n <%s>%nbut context failed to start", + getApplicationContext(), 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. + *

+ * Example:

+	 * assertThat(context).doesNotHaveBean("fooBean"); 
+ * @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 doesNotHaveBean(String name) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nnot to have any beans of name:%n <%s>%nbut context failed to start", + getApplicationContext(), 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. + *

+ * Example:

+	 * assertThat(context).getBeanNames(Foo.class).containsOnly("fooBean"); 
+ * @param 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 AbstractObjectArrayAssert getBeanNames(Class type) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nto get beans names with type:%n <%s>%nbut context failed to start", + getApplicationContext(), 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. + *

+ * Example:

+	 * assertThat(context).getBean(Foo.class).isInstanceOf(DefaultFoo.class);
+	 * assertThat(context).getBean(Bar.class).isNull();
+ * @param 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 AbstractObjectAssert getBean(Class type) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nto contain bean of type:%n <%s>%nbut context failed to start", + getApplicationContext(), 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. + *

+ * Example:

+	 * assertThat(context).getBean("foo").isInstanceOf(Foo.class);
+	 * assertThat(context).getBean("foo").isNull();
+ * @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 getBean(String name) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nto contain a bean of name:%n <%s>%nbut context failed to start", + getApplicationContext(), 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. + *

+ * Example:

+	 * assertThat(context).getBean("foo", Foo.class).isInstanceOf(DefaultFoo.class);
+	 * assertThat(context).getBean("foo", Foo.class).isNull();
+ * @param 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 AbstractObjectAssert getBean(String name, Class type) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nto contain a bean of name:%n <%s> (%s)%nbut context failed to start", + getApplicationContext(), 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. + *

+ * Example:

+	 * assertThat(context).getBeans(Foo.class).containsKey("foo");
+	 * 
+ * @param 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 MapAssert getBeans(Class type) { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nto get beans of type:%n <%s> (%s)%nbut context failed to start", + getApplicationContext(), type, 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. + *

+ * Example:

+	 * assertThat(context).getFailure().containsMessage("missing bean");
+	 * 
+ * @return assertions on the cause of the failure + * @throws AssertionError if the application context started without a failure + */ + public AbstractThrowableAssert getFailure() { + hasFailed(); + return assertThat(this.startupFailure); + } + + /** + * Verifies that the application has failed to start. + *

+ * Example:

 assertThat(context).hasFailed();
+	 * 
+ * @return {@code this} assertion object. + * @throws AssertionError if the application context started without a failure + */ + public ApplicationContextAssert 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. + *

+ * Example:

 assertThat(context).hasNotFailed();
+	 * 
+ * @return {@code this} assertion object. + * @throws AssertionError if the application context failed to start + */ + public ApplicationContextAssert hasNotFailed() { + if (this.startupFailure != null) { + throwAssertionError(new BasicErrorMessageFactory( + "%nExpecting:%n <%s>%nto have not failed:%nbut context failed to start", + getApplicationContext())); + } + return this; + } + + protected final C getApplicationContext() { + return this.actual; + } + + protected final Throwable getStartupFailure() { + return this.startupFailure; + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ApplicationContextTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ApplicationContextTester.java new file mode 100644 index 0000000000..f133c27d84 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ApplicationContextTester.java @@ -0,0 +1,56 @@ +/* + * 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.function.Supplier; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +/** + * A {@link AbstractApplicationContextTester ApplicationContext tester} for a standard, + * non-web environment {@link ConfigurableApplicationContext}. + *

+ * See {@link AbstractApplicationContextTester} for details. + * + * @author Stephane Nicoll + * @author Andy Wilkinson + * @author Phillip Webb + * @since 2.0.0 + */ +public class ApplicationContextTester extends + AbstractApplicationContextTester { + + /** + * Create a new {@link ApplicationContextTester} instance using an + * {@link AnnotationConfigApplicationContext} as the underlying source. + */ + public ApplicationContextTester() { + this(AnnotationConfigApplicationContext::new); + } + + /** + * Create a new {@link ApplicationContextTester} instance using the specified + * {@code contextFactory} as the underlying source. + * @param contextFactory a supplier that returns a new instance on each call + */ + public ApplicationContextTester( + Supplier contextFactory) { + super(contextFactory); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertProviderApplicationContext.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertProviderApplicationContext.java new file mode 100644 index 0000000000..7ea2860d7b --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertProviderApplicationContext.java @@ -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; + +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. + *

+ * Assertions can be applied using the standard AssertJ {@code assertThat(...)} style (see + * {@link ApplicationContextAssert} for a complete list). For example:

+ * assertThat(applicationContext).hasSingleBean(MyBean.class);
+ * 
+ *

+ * If the original {@link ApplicationContext} is needed for any reason the + * {@link #getSourceApplicationContext()} method can be used. + *

+ * Any {@link ApplicationContext} method called on a context that has failed to start will + * throw an {@link IllegalStateException}. + * + * @param The application context type + * @author Phillip Webb + * @see AssertableApplicationContext + * @see AssertableWebApplicationContext + * @see AssertableReactiveWebApplicationContext + * @see ApplicationContextAssert + */ +interface AssertProviderApplicationContext extends + ApplicationContext, AssertProvider>, Closeable { + + /** + * Return an assert for AspectJ. + * @return an AspectJ assert + * @deprecated use standard AssertJ {@code assertThat(context)...} calls instead. + */ + @Deprecated + @Override + ApplicationContextAssert 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 the context type + * @param requiredType the required context type + * @return the source application context + * @throws IllegalStateException if the source context failed to start + */ + T getSourceApplicationContext(Class 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 AssertProviderApplicationContext} instance. + * @param the assert provider type + * @param the context type + * @param type the type of {@link AssertProviderApplicationContext} 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 AssertProviderApplicationContext} instance + */ + @SuppressWarnings("unchecked") + static , C extends ApplicationContext> T get( + Class type, Class contextType, + Supplier 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)); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertProviderApplicationContextInvocationHandler.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertProviderApplicationContextInvocationHandler.java new file mode 100644 index 0000000000..64feba0fc2 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertProviderApplicationContextInvocationHandler.java @@ -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; + +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 AssertProviderApplicationContext} 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; + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableApplicationContext.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableApplicationContext.java new file mode 100644 index 0000000000..8ac8fe82cc --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableApplicationContext.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.context; + +import java.util.function.Supplier; + +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. + *

+ * See {@link AssertProviderApplicationContext} for more details. + * + * @author Phillip Webb + * @since 2.0.0 + * @see ApplicationContextTester + * @see ApplicationContext + */ +public interface AssertableApplicationContext + extends AssertProviderApplicationContext { + + /** + * 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 contextSupplier) { + return AssertProviderApplicationContext.get(AssertableApplicationContext.class, + ConfigurableApplicationContext.class, contextSupplier); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableReactiveWebApplicationContext.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableReactiveWebApplicationContext.java new file mode 100644 index 0000000000..86e5703d4f --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableReactiveWebApplicationContext.java @@ -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; + +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. + *

+ * See {@link AssertProviderApplicationContext} for more details. + * + * @author Phillip Webb + * @since 2.0.0 + * @see ReactiveWebApplicationContext + * @see ReactiveWebApplicationContext + */ +public interface AssertableReactiveWebApplicationContext extends + AssertProviderApplicationContext, + 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 contextSupplier) { + return AssertProviderApplicationContext.get( + AssertableReactiveWebApplicationContext.class, + ConfigurableReactiveWebApplicationContext.class, contextSupplier); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableWebApplicationContext.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableWebApplicationContext.java new file mode 100644 index 0000000000..a5815c332c --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/AssertableWebApplicationContext.java @@ -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; + +import java.util.function.Supplier; + +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. + *

+ * See {@link AssertProviderApplicationContext} for more details. + * + * @author Phillip Webb + * @since 2.0.0 + * @see WebApplicationContextTester + * @see WebApplicationContext + */ +public interface AssertableWebApplicationContext + extends AssertProviderApplicationContext, + 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 contextSupplier) { + return AssertProviderApplicationContext.get(AssertableWebApplicationContext.class, + ConfigurableWebApplicationContext.class, contextSupplier); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ContextConsumer.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ContextConsumer.java index aa6fb8fb77..f4523d9748 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ContextConsumer.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ContextConsumer.java @@ -16,24 +16,26 @@ package org.springframework.boot.test.context; -import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.ApplicationContext; /** - * Callback interface used in tests to process a running - * {@link ConfigurableApplicationContext} with the ability to throw a (checked) exception. + * Callback interface used to process an {@link ApplicationContext} with the ability to + * throw a (checked) exception. * * @author Stephane Nicoll * @author Andy Wilkinson + * @param The application context type * @since 2.0.0 + * @see AbstractApplicationContextTester */ @FunctionalInterface -public interface ContextConsumer { +public interface ContextConsumer { /** * 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(ConfigurableApplicationContext context) throws Throwable; + void accept(C context) throws Throwable; } diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ContextLoader.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ContextLoader.java deleted file mode 100644 index 421ba80814..0000000000 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ContextLoader.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.function.Consumer; - -import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.mock.web.MockServletContext; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; - -/** - * Manage the lifecycle of an {@link ApplicationContext}. Such helper is best used as a - * field of a test class, describing the shared configuration required for the test: - * - *

- * public class FooAutoConfigurationTests {
- *
- *     private final ContextLoader contextLoader = ContextLoader.standard()
- *             .autoConfig(FooAutoConfiguration.class).env("spring.foo=bar");
- *
- * }
- * - *

- * The initialization above makes sure to register {@code FooAutoConfiguration} for all - * tests and set the {@code spring.foo} property to {@code bar} unless specified - * otherwise. - * - *

- * Based on the configuration above, a specific test can simulate what would happen if the - * user customizes a property and/or provides its own configuration: - * - *

- * public class FooAutoConfigurationTests {
- *
- *     @Test
- *     public someTest() {
- *         this.contextLoader.config(UserConfig.class).env("spring.foo=biz")
- *                 .load(context -> {
- *            			// assertions using the context
- *         });
- *     }
- *
- * }
- * - *

- * The test above includes an extra {@code UserConfig} class that is guaranteed to be - * processed before any auto-configuration. Also, {@code spring.foo} has - * been overwritten to {@code biz}. The {@link #load(ContextConsumer) load} method takes a - * consumer that can use the context to assert its state. Upon completion, the context is - * automatically closed. - * - *

- * Web environment can easily be simulated using the {@link #servletWeb()} and - * {@link #reactiveWeb()} factory methods. - * - *

- * If a failure scenario has to be tested, {@link #loadAndFail(Consumer)} can be used - * instead: it expects the startup of the context to fail and call the {@link Consumer} - * with the exception for further assertions. - * - * @author Stephane Nicoll - * @author Andy Wilkinson - * @since 2.0.0 - */ -public interface ContextLoader { - - /** - * Set the specified system property prior to loading the context and restore its - * previous value once the consumer has been invoked and the context closed. If the - * {@code value} is {@code null} this removes any prior customization for that key. - * @param key the system property - * @param value the value (can be null to remove any existing customization) - * @return this instance - */ - ContextLoader systemProperty(String key, String value); - - /** - * Add the specified 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 this instance - */ - ContextLoader env(String... pairs); - - /** - * Add the specified user configuration classes. - * @param configs the user configuration classes to add - * @return this instance - */ - ContextLoader config(Class... configs); - - /** - * Add the specified auto-configuration classes. - * @param autoConfigurations the auto-configuration classes to add - * @return this instance - */ - ContextLoader autoConfig(Class... autoConfigurations); - - /** - * Add the specified auto-configurations at the beginning (in that order) so that it - * is applied before any other existing auto-configurations, but after any user - * configuration. If {@code A} and {@code B} are specified, {@code A} will be - * processed, then {@code B} and finally the rest of the existing auto-configuration. - * @param autoConfigurations the auto-configuration to add - * @return this instance - */ - ContextLoader autoConfigFirst(Class... autoConfigurations); - - /** - * 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 this instance - * @see HidePackagesClassLoader - */ - ContextLoader classLoader(ClassLoader classLoader); - - /** - * Configure the - * {@link org.springframework.context.ConfigurableApplicationContext#setParent(ApplicationContext) - * parent} of the {@link ApplicationContext}. - * - * @param parent the parent - * @return this instance - */ - ContextLoader parent(ApplicationContext parent); - - /** - * 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} - */ - void load(ContextConsumer consumer); - - /** - * Create and refresh a new {@link ApplicationContext} based on the current state of - * this loader that this expected to fail. If the context does not fail, an - * {@link AssertionError} is thrown. Otherwise the exception is consumed by the - * specified {@link Consumer} with no expectation on the type of the exception. - * @param consumer the consumer of the failure - */ - void loadAndFail(Consumer consumer); - - /** - * Create and refresh a new {@link ApplicationContext} based on the current state of - * this loader that this expected to fail. If the context does not fail, an - * {@link AssertionError} is thrown. If the exception does not match the specified - * {@code exceptionType}, an {@link AssertionError} is thrown as well. If the - * exception type matches, it is consumed by the specified {@link Consumer}. - * @param exceptionType the expected type of the failure - * @param consumer the consumer of the failure - * @param the expected type of the failure - */ - void loadAndFail(Class exceptionType, Consumer consumer); - - /** - * Creates a {@code ContextLoader} that will load a standard - * {@link AnnotationConfigApplicationContext}. - * @return the context loader - */ - static StandardContextLoader standard() { - return new StandardContextLoader(AnnotationConfigApplicationContext::new); - } - - /** - * Creates a {@code ContextLoader} that will load a - * {@link AnnotationConfigWebApplicationContext}. - * @return the context loader - */ - static ServletWebContextLoader servletWeb() { - return new ServletWebContextLoader(() -> { - AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); - context.setServletContext(new MockServletContext()); - return context; - }); - } - - /** - * Creates a {@code ContextLoader} that will load a - * {@link GenericReactiveWebApplicationContext}. - * @return the context loader - */ - static ReactiveWebContextLoader reactiveWeb() { - return new ReactiveWebContextLoader(GenericReactiveWebApplicationContext::new); - } - -} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebApplicationContextTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebApplicationContextTester.java new file mode 100644 index 0000000000..3044d6bf6b --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebApplicationContextTester.java @@ -0,0 +1,56 @@ +/* + * 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.function.Supplier; + +import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext; +import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext; + +/** + * A {@link AbstractApplicationContextTester ApplicationContext tester} for a + * {@link ConfigurableReactiveWebApplicationContext}. + *

+ * See {@link AbstractApplicationContextTester} for details. + * + * @author Andy Wilkinson + * @author Stephane Nicoll + * @author Phillip Webb + * @since 2.0.0 + */ +public final class ReactiveWebApplicationContextTester extends + AbstractApplicationContextTester { + + /** + * Create a new {@link ReactiveWebApplicationContextTester} instance using a + * {@link GenericReactiveWebApplicationContext} as the underlying source. + */ + public ReactiveWebApplicationContextTester() { + this(GenericReactiveWebApplicationContext::new); + } + + /** + * Create a new {@link ApplicationContextTester} instance using the specified + * {@code contextFactory} as the underlying source. + * @param contextFactory a supplier that returns a new instance on each call + */ + public ReactiveWebApplicationContextTester( + Supplier contextFactory) { + super(contextFactory); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ServletWebContextLoader.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ServletWebContextLoader.java deleted file mode 100644 index 0ce2546b3a..0000000000 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ServletWebContextLoader.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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.function.Supplier; - -import org.springframework.web.context.ConfigurableWebApplicationContext; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; - -/** - * A {@link ContextLoader} that simulates a {@link AnnotationConfigWebApplicationContext} - * which can be useful to test components that require a servlet web application. - * - * @author Andy Wilkinson - * @author Stephane Nicoll - * @since 2.0.0 - */ -public final class ServletWebContextLoader extends - AbstractContextLoader { - - ServletWebContextLoader( - Supplier contextSupplier) { - super(contextSupplier); - } - - /** - * Create and refresh a new {@link ConfigurableWebApplicationContext} based on the - * current state of this loader. The context is consumed by the specified - * {@link WebMvcContextConsumer consumer} and closed upon completion. - * @param consumer the consumer of the created - * {@link ConfigurableWebApplicationContext} - */ - public void loadWeb(WebMvcContextConsumer consumer) { - doLoad(consumer::accept); - } - -} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/WebApplicationContextTester.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/WebApplicationContextTester.java new file mode 100644 index 0000000000..98101f6e8b --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/WebApplicationContextTester.java @@ -0,0 +1,75 @@ +/* + * 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.function.Supplier; + +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 AbstractApplicationContextTester ApplicationContext tester} for a Servlet + * based {@link ConfigurableWebApplicationContext}. + *

+ * See {@link AbstractApplicationContextTester} for details. + * + * @author Andy Wilkinson + * @author Stephane Nicoll + * @author Phillip Webb + * @since 2.0.0 + */ +public final class WebApplicationContextTester extends + AbstractApplicationContextTester { + + /** + * Create a new {@link WebApplicationContextTester} instance using an + * {@link AnnotationConfigWebApplicationContext} with a {@link MockServletContext} as + * the underlying source. + * @see #withMockServletContext(Supplier) + */ + public WebApplicationContextTester() { + this(withMockServletContext(AnnotationConfigWebApplicationContext::new)); + } + + /** + * Create a new {@link WebApplicationContextTester} instance using the specified + * {@code contextFactory} as the underlying source. + * @param contextFactory a supplier that returns a new instance on each call + */ + public WebApplicationContextTester( + Supplier contextFactory) { + super(contextFactory); + } + + /** + * 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 withMockServletContext( + Supplier contextFactory) { + return (contextFactory == null ? null : () -> { + ConfigurableWebApplicationContext context = contextFactory.get(); + context.setServletContext(new MockServletContext()); + return context; + }); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractApplicationContextTesterTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractApplicationContextTesterTests.java new file mode 100644 index 0000000000..8b49573434 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractApplicationContextTesterTests.java @@ -0,0 +1,194 @@ +/* + * 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.UUID; + +import com.google.gson.Gson; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.boot.context.annotation.UserConfigurations; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.util.ClassUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +/** + * Abstract tests for {@link AbstractApplicationContextTester} implementations. + * + * @param The tester type + * @param the context type + * @param the assertable context type + * @author Stephane Nicoll + * @author Phillip Webb + */ +public abstract class AbstractApplicationContextTesterTests, C extends ConfigurableApplicationContext, A extends AssertProviderApplicationContext> { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Test + public void runWithSystemPropertiesShouldSetAndRemoveProperties() { + String key = "test." + UUID.randomUUID().toString(); + assertThat(System.getProperties().containsKey(key)).isFalse(); + get().withSystemProperties(key + "=value").run(loaded -> { + assertThat(System.getProperties()).containsEntry(key, "value"); + }); + assertThat(System.getProperties().containsKey(key)).isFalse(); + } + + @Test + public void runWithSystemPropertiesWhenContextFailsShouldRemoveProperties() + throws Exception { + String key = "test." + UUID.randomUUID().toString(); + assertThat(System.getProperties().containsKey(key)).isFalse(); + get().withSystemProperties(key + "=value") + .withUserConfiguration(FailingConfig.class).run(loaded -> { + assertThat(loaded).hasFailed(); + }); + assertThat(System.getProperties().containsKey(key)).isFalse(); + } + + @Test + public void runWithSystemPropertiesShouldRestoreOriginalProperties() + throws Exception { + String key = "test." + UUID.randomUUID().toString(); + System.setProperty(key, "value"); + try { + assertThat(System.getProperties().getProperty(key)).isEqualTo("value"); + get().withSystemProperties(key + "=newValue").run(loaded -> { + assertThat(System.getProperties()).containsEntry(key, "newValue"); + }); + assertThat(System.getProperties().getProperty(key)).isEqualTo("value"); + } + finally { + System.clearProperty(key); + } + } + + @Test + public void runWithSystemPropertiesWhenValueIsNullShouldRemoveProperty() + throws Exception { + String key = "test." + UUID.randomUUID().toString(); + System.setProperty(key, "value"); + try { + assertThat(System.getProperties().getProperty(key)).isEqualTo("value"); + get().withSystemProperty(key, null).run(loaded -> { + assertThat(System.getProperties()).doesNotContainKey(key); + }); + assertThat(System.getProperties().getProperty(key)).isEqualTo("value"); + } + finally { + System.clearProperty(key); + } + } + + @Test + public void runWithMultiplePropertyValuesShouldAllAllValues() throws Exception { + get().withPropertyValues("test.foo=1").withPropertyValues("test.bar=2") + .run(loaded -> { + Environment environment = loaded.getEnvironment(); + assertThat(environment.getProperty("test.foo")).isEqualTo("1"); + assertThat(environment.getProperty("test.bar")).isEqualTo("2"); + }); + } + + @Test + public void runWithPropertyValuesWhenHasExistingShouldReplaceValue() + throws Exception { + get().withPropertyValues("test.foo=1").withPropertyValues("test.foo=2") + .run(loaded -> { + Environment environment = loaded.getEnvironment(); + assertThat(environment.getProperty("test.foo")).isEqualTo("2"); + }); + } + + @Test + public void runWithConfigurationsShouldRegisterConfigurations() throws Exception { + get().withUserConfiguration(FooConfig.class) + .run((loaded) -> assertThat(loaded).hasBean("foo")); + } + + @Test + public void runWithMultipleConfigurationsShouldRegisterAllConfigurations() + throws Exception { + get().withUserConfiguration(FooConfig.class) + .withConfiguration(UserConfigurations.of(BarConfig.class)) + .run((loaded) -> assertThat(loaded).hasBean("foo").hasBean("bar")); + } + + @Test + public void runWithFailedContextShouldReturnFailedAssertableContext() + throws Exception { + get().withUserConfiguration(FailingConfig.class) + .run((loaded) -> assertThat(loaded).hasFailed()); + } + + @Test + public void runWithClassLoaderShouldSetClassLoader() throws Exception { + get().withClassLoader( + new HidePackagesClassLoader(Gson.class.getPackage().getName())) + .run((loaded) -> { + try { + ClassUtils.forName(Gson.class.getName(), loaded.getClassLoader()); + fail("Should have thrown a ClassNotFoundException"); + } + catch (ClassNotFoundException e) { + // expected + } + }); + } + + protected abstract T get(); + + @Configuration + static class FailingConfig { + + @Bean + public String foo() { + throw new IllegalStateException("Failed"); + } + + } + + @Configuration + static class FooConfig { + + @Bean + public String foo() { + return "foo"; + } + + } + + @Configuration + static class BarConfig { + + @Bean + public String bar() { + return "bar"; + } + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/ApplicationContextAssertTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/ApplicationContextAssertTests.java new file mode 100644 index 0000000000..798286612b --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/ApplicationContextAssertTests.java @@ -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.context; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.StaticApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ApplicationContextAssert}. + * + * @author Phillip Webb + */ +public class ApplicationContextAssertTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private StaticApplicationContext context = new StaticApplicationContext(); + + private RuntimeException failure = new RuntimeException(); + + @Test + public void createWhenApplicationContextIsNullShouldThrowException() + throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("ApplicationContext must not be null"); + new ApplicationContextAssert<>(null, null); + } + + @Test + public void createWhenHasApplicationContextShouldSetActual() throws Exception { + assertThat(getAssert(this.context).getSourceApplicationContext()) + .isSameAs(this.context); + } + + @Test + public void createWhenHasExceptionShouldSetFailure() throws Exception { + assertThat(getAssert(this.failure)).getFailure().isSameAs(this.failure); + } + + @Test + public void hasBeanWhenHasBeanShouldPass() throws Exception { + this.context.registerSingleton("foo", Foo.class); + assertThat(getAssert(this.context)).hasBean("foo"); + } + + @Test + public void hasBeanWhenHasNoBeanShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("no such bean"); + assertThat(getAssert(this.context)).hasBean("foo"); + } + + @Test + public void hasBeanWhenNotStartedShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).hasBean("foo"); + } + + @Test + public void hasSingleBeanWhenHasSingleBeanShouldPass() throws Exception { + this.context.registerSingleton("foo", Foo.class); + assertThat(getAssert(this.context)).hasSingleBean(Foo.class); + } + + @Test + public void hasSingleBeanWhenHasNoBeansShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("no beans of that type"); + assertThat(getAssert(this.context)).hasSingleBean(Foo.class); + } + + @Test + public void hasSingleBeanWhenHasMultipleShouldFail() throws Exception { + this.context.registerSingleton("foo", Foo.class); + this.context.registerSingleton("bar", Foo.class); + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("but found:"); + assertThat(getAssert(this.context)).hasSingleBean(Foo.class); + } + + @Test + public void hasSingleBeanWhenFailedToStartShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).hasSingleBean(Foo.class); + } + + @Test + public void doesNotHaveBeanOfTypeWhenHasNoBeanOfTypeShouldPass() throws Exception { + assertThat(getAssert(this.context)).doesNotHaveBean(Foo.class); + } + + @Test + public void doesNotHaveBeanOfTypeWhenHasBeanOfTypeShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("but found"); + this.context.registerSingleton("foo", Foo.class); + assertThat(getAssert(this.context)).doesNotHaveBean(Foo.class); + } + + @Test + public void doesNotHaveBeanOfTypeWhenFailedToStartShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).doesNotHaveBean(Foo.class); + } + + @Test + public void doesNotHaveBeanOfNameWhenHasNoBeanOfTypeShouldPass() throws Exception { + assertThat(getAssert(this.context)).doesNotHaveBean("foo"); + } + + @Test + public void doesNotHaveBeanOfNameWhenHasBeanOfTypeShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("but found"); + this.context.registerSingleton("foo", Foo.class); + assertThat(getAssert(this.context)).doesNotHaveBean("foo"); + } + + @Test + public void doesNotHaveBeanOfNameWhenFailedToStartShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).doesNotHaveBean("foo"); + } + + @Test + public void getBeanNamesWhenHasNamesShouldReturnNamesAssert() throws Exception { + this.context.registerSingleton("foo", Foo.class); + this.context.registerSingleton("bar", Foo.class); + assertThat(getAssert(this.context)).getBeanNames(Foo.class).containsOnly("foo", + "bar"); + } + + @Test + public void getBeanNamesWhenHasNoNamesShouldReturnEmptyAssert() throws Exception { + assertThat(getAssert(this.context)).getBeanNames(Foo.class).isEmpty(); + } + + @Test + public void getBeanNamesWhenFailedToStartShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).doesNotHaveBean("foo"); + } + + @Test + public void getBeanOfTypeWhenHasBeanShouldReturnBeanAssert() throws Exception { + this.context.registerSingleton("foo", Foo.class); + assertThat(getAssert(this.context)).getBean(Foo.class).isNotNull(); + } + + @Test + public void getBeanOfTypeWhenHasNoBeanShouldReturnNullAssert() throws Exception { + assertThat(getAssert(this.context)).getBean(Foo.class).isNull(); + } + + @Test + public void getBeanOfTypeWhenHasMultipleBeansShouldFail() throws Exception { + this.context.registerSingleton("foo", Foo.class); + this.context.registerSingleton("bar", Foo.class); + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("but found"); + assertThat(getAssert(this.context)).getBean(Foo.class); + } + + @Test + public void getBeanOfTypeWhenFailedToStartShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).getBean(Foo.class); + } + + @Test + public void getBeanOfNameWhenHasBeanShouldReturnBeanAssert() throws Exception { + this.context.registerSingleton("foo", Foo.class); + assertThat(getAssert(this.context)).getBean("foo").isNotNull(); + } + + @Test + public void getBeanOfNameWhenHasNoBeanOfNameShouldReturnNullAssert() + throws Exception { + assertThat(getAssert(this.context)).getBean("foo").isNull(); + } + + @Test + public void getBeanOfNameWhenFailedToStartShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).getBean("foo"); + } + + @Test + public void getBeanOfNameAndTypeWhenHasBeanShouldReturnBeanAssert() throws Exception { + this.context.registerSingleton("foo", Foo.class); + assertThat(getAssert(this.context)).getBean("foo", Foo.class).isNotNull(); + } + + @Test + public void getBeanOfNameAndTypeWhenHasNoBeanOfNameShouldReturnNullAssert() + throws Exception { + assertThat(getAssert(this.context)).getBean("foo", Foo.class).isNull(); + } + + @Test + public void getBeanOfNameAndTypeWhenHasNoBeanOfNameButDifferentTypeShouldFail() + throws Exception { + this.context.registerSingleton("foo", Foo.class); + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("of type"); + assertThat(getAssert(this.context)).getBean("foo", String.class); + } + + @Test + public void getBeanOfNameAndTypeWhenFailedToStartShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).getBean("foo", Foo.class); + } + + @Test + public void getBeansWhenHasBeansShouldReturnMapAssert() throws Exception { + this.context.registerSingleton("foo", Foo.class); + this.context.registerSingleton("bar", Foo.class); + assertThat(getAssert(this.context)).getBeans(Foo.class).hasSize(2) + .containsKeys("foo", "bar"); + } + + @Test + public void getBeansWhenHasNoBeansShouldReturnEmptyMapAssert() throws Exception { + assertThat(getAssert(this.context)).getBeans(Foo.class).isEmpty(); + } + + @Test + public void getBeansWhenFailedToStartShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("failed to start"); + assertThat(getAssert(this.failure)).getBeans(Foo.class); + } + + @Test + public void getFailureWhenFailedShouldReturnFailure() throws Exception { + assertThat(getAssert(this.failure)).getFailure().isSameAs(this.failure); + } + + @Test + public void getFailureWhenDidNotFailShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("context started"); + assertThat(getAssert(this.context)).getFailure(); + } + + @Test + public void hasFailedWhenFailedShouldPass() throws Exception { + assertThat(getAssert(this.failure)).hasFailed(); + } + + @Test + public void hasFailedWhenNotFailedShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("to have failed"); + assertThat(getAssert(this.context)).hasFailed(); + } + + @Test + public void hasNotFailedWhenFailedShouldFail() throws Exception { + this.thrown.expect(AssertionError.class); + this.thrown.expectMessage("to have not failed"); + assertThat(getAssert(this.failure)).hasNotFailed(); + } + + @Test + public void hasNotFailedWhenNotFailedShouldPass() throws Exception { + assertThat(getAssert(this.context)).hasNotFailed(); + } + + private AssertableApplicationContext getAssert( + ConfigurableApplicationContext applicationContext) { + return AssertableApplicationContext.get(() -> applicationContext); + } + + private AssertableApplicationContext getAssert(RuntimeException failure) { + return AssertableApplicationContext.get(() -> { + throw failure; + }); + } + + private static class Foo { + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/StandardContextLoader.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/ApplicationContextTesterTests.java similarity index 59% rename from spring-boot-test/src/main/java/org/springframework/boot/test/context/StandardContextLoader.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/context/ApplicationContextTesterTests.java index a58fa9f75a..01c5f1e3b3 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/StandardContextLoader.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/ApplicationContextTesterTests.java @@ -16,23 +16,20 @@ package org.springframework.boot.test.context; -import java.util.function.Supplier; - -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; /** - * A {@link ContextLoader} that simulates a standard, non web environment. + * Tests for {@link ApplicationContextTester}. * * @author Stephane Nicoll - * @author Andy Wilkinson - * @since 2.0.0 + * @author Phillip Webb */ -public class StandardContextLoader extends - AbstractContextLoader { +public class ApplicationContextTesterTests extends + AbstractApplicationContextTesterTests { - public StandardContextLoader( - Supplier contextSupplier) { - super(contextSupplier); + @Override + protected ApplicationContextTester get() { + return new ApplicationContextTester(); } } diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertProviderApplicationContextTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertProviderApplicationContextTests.java new file mode 100644 index 0000000000..5d5cdad937 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertProviderApplicationContextTests.java @@ -0,0 +1,245 @@ +/* + * 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.function.Supplier; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.StaticApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link AssertProviderApplicationContext} and + * {@link AssertProviderApplicationContextInvocationHandler}. + * + * @author Phillip Webb + */ +public class AssertProviderApplicationContextTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Mock + private ConfigurableApplicationContext mockContext; + + private RuntimeException startupFailure; + + private Supplier mockContextSupplier; + + private Supplier startupFailureSupplier; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + this.startupFailure = new RuntimeException(); + this.mockContextSupplier = () -> this.mockContext; + this.startupFailureSupplier = () -> { + throw this.startupFailure; + }; + } + + @Test + public void getWhenTypeIsNullShouldThrowExecption() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Type must not be null"); + AssertProviderApplicationContext.get(null, ApplicationContext.class, + this.mockContextSupplier); + } + + @Test + public void getWhenTypeIsClassShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Type must not be null"); + AssertProviderApplicationContext.get(null, ApplicationContext.class, + this.mockContextSupplier); + } + + @Test + public void getWhenContextTypeIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Type must be an interface"); + AssertProviderApplicationContext.get( + TestAssertProviderApplicationContextClass.class, ApplicationContext.class, + this.mockContextSupplier); + } + + @Test + public void getWhenContextTypeIsClassShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("ContextType must not be null"); + AssertProviderApplicationContext.get(TestAssertProviderApplicationContext.class, + null, this.mockContextSupplier); + } + + @Test + public void getWhenSupplierIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("ContextType must be an interface"); + AssertProviderApplicationContext.get(TestAssertProviderApplicationContext.class, + StaticApplicationContext.class, this.mockContextSupplier); + } + + @Test + public void getWhenContextStartsShouldReturnProxyThatCallsRealMethods() + throws Exception { + AssertProviderApplicationContext context = get( + this.mockContextSupplier); + assertThat((Object) context).isNotNull(); + context.getBean("foo"); + verify(this.mockContext).getBean("foo"); + } + + @Test + public void getWhenContextFailsShouldReturnProxyThatThrowsExceptions() + throws Exception { + AssertProviderApplicationContext context = get( + this.startupFailureSupplier); + assertThat((Object) context).isNotNull(); + expectStartupFailure(); + context.getBean("foo"); + } + + @Test + public void getSourceContextWhenContextStartsShouldReturnSourceContext() + throws Exception { + AssertProviderApplicationContext context = get( + this.mockContextSupplier); + assertThat(context.getSourceApplicationContext()).isSameAs(this.mockContext); + } + + @Test + public void getSourceContextWhenContextFailsShouldThrowException() throws Exception { + AssertProviderApplicationContext context = get( + this.startupFailureSupplier); + expectStartupFailure(); + context.getSourceApplicationContext(); + } + + @Test + public void getSourceContextOfTypeWhenContextStartsShouldReturnSourceContext() + throws Exception { + AssertProviderApplicationContext context = get( + this.mockContextSupplier); + assertThat(context.getSourceApplicationContext(ApplicationContext.class)) + .isSameAs(this.mockContext); + } + + @Test + public void getSourceContextOfTypeWhenContextFailsToStartShouldThrowException() + throws Exception { + AssertProviderApplicationContext context = get( + this.startupFailureSupplier); + expectStartupFailure(); + context.getSourceApplicationContext(ApplicationContext.class); + } + + @Test + public void getStartupFailureWhenContextStartsShouldReturnNull() throws Exception { + AssertProviderApplicationContext context = get( + this.mockContextSupplier); + assertThat(context.getStartupFailure()).isNull(); + } + + @Test + public void getStartupFailureWhenContextFailsToStartShouldReturnException() + throws Exception { + AssertProviderApplicationContext context = get( + this.startupFailureSupplier); + assertThat(context.getStartupFailure()).isEqualTo(this.startupFailure); + } + + @Test + public void assertThatWhenContextStartsShouldReturnAssertions() throws Exception { + AssertProviderApplicationContext context = get( + this.mockContextSupplier); + ApplicationContextAssert contextAssert = assertThat(context); + assertThat(contextAssert.getApplicationContext()).isSameAs(context); + assertThat(contextAssert.getStartupFailure()).isNull(); + } + + @Test + public void assertThatWhenContextFailsShouldReturnAssertions() throws Exception { + AssertProviderApplicationContext context = get( + this.startupFailureSupplier); + ApplicationContextAssert contextAssert = assertThat(context); + assertThat(contextAssert.getApplicationContext()).isSameAs(context); + assertThat(contextAssert.getStartupFailure()).isSameAs(this.startupFailure); + } + + @Test + public void toStringWhenContextStartsShouldReturnSimpleString() throws Exception { + AssertProviderApplicationContext context = get( + this.mockContextSupplier); + assertThat(context.toString()) + .startsWith( + "Started application org.springframework.context.ConfigurableApplicationContext$MockitoMock") + .endsWith("[id=,applicationName=,beanDefinitionCount=0]"); + } + + @Test + public void toStringWhenContextFailsToStartShouldReturnSimpleString() + throws Exception { + AssertProviderApplicationContext context = get( + this.startupFailureSupplier); + assertThat(context.toString()).isEqualTo("Unstarted application context " + + "org.springframework.context.ApplicationContext" + + "[startupFailure=java.lang.RuntimeException]"); + } + + @Test + public void closeShouldCloseContext() throws Exception { + AssertProviderApplicationContext context = get( + this.mockContextSupplier); + context.close(); + verify(this.mockContext).close(); + } + + private void expectStartupFailure() { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("failed to start"); + this.thrown.expectCause(equalTo(this.startupFailure)); + } + + private AssertProviderApplicationContext get( + Supplier contextSupplier) { + return AssertProviderApplicationContext.get( + TestAssertProviderApplicationContext.class, ApplicationContext.class, + contextSupplier); + } + + private interface TestAssertProviderApplicationContext + extends AssertProviderApplicationContext { + + } + + private abstract static class TestAssertProviderApplicationContextClass + implements TestAssertProviderApplicationContext { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableApplicationContextTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableApplicationContextTests.java new file mode 100644 index 0000000000..9d1e4a7ca0 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableApplicationContextTests.java @@ -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; + +import org.junit.Test; + +import org.springframework.context.ConfigurableApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link AssertableApplicationContext}. + * + * @author Phillip Webb + * @see AssertProviderApplicationContextTests + */ +public class AssertableApplicationContextTests { + + @Test + public void getShouldReturnProxy() { + AssertableApplicationContext context = AssertableApplicationContext + .get(() -> mock(ConfigurableApplicationContext.class)); + assertThat(context).isInstanceOf(ConfigurableApplicationContext.class); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableReactiveWebApplicationContextTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableReactiveWebApplicationContextTests.java new file mode 100644 index 0000000000..4f5b49196d --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableReactiveWebApplicationContextTests.java @@ -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; + +import org.junit.Test; + +import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link AssertableReactiveWebApplicationContext}. + * + * @author Phillip Webb + * @see AssertProviderApplicationContextTests + */ +public class AssertableReactiveWebApplicationContextTests { + + @Test + public void getShouldReturnProxy() { + AssertableReactiveWebApplicationContext context = AssertableReactiveWebApplicationContext + .get(() -> mock(ConfigurableReactiveWebApplicationContext.class)); + assertThat(context).isInstanceOf(ConfigurableReactiveWebApplicationContext.class); + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/WebMvcContextConsumer.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableWebApplicationContextTests.java similarity index 57% rename from spring-boot-test/src/main/java/org/springframework/boot/test/context/WebMvcContextConsumer.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableWebApplicationContextTests.java index b982a0533d..b49effc0a9 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/WebMvcContextConsumer.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AssertableWebApplicationContextTests.java @@ -16,25 +16,26 @@ package org.springframework.boot.test.context; +import org.junit.Test; + import org.springframework.web.context.ConfigurableWebApplicationContext; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + /** - * Specialized callback interface used in tests to process a running - * {@link ConfigurableWebApplicationContext} with the ability to throw a (checked) - * exception. + * Tests for {@link AssertableWebApplicationContext}. * - * @author Stephane Nicoll - * @see ContextConsumer - * @since 2.0.0 + * @author Phillip Webb + * @see AssertProviderApplicationContextTests */ -@FunctionalInterface -public interface WebMvcContextConsumer { +public class AssertableWebApplicationContextTests { - /** - * 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(ConfigurableWebApplicationContext context) throws Throwable; + @Test + public void getShouldReturnProxy() { + AssertableWebApplicationContext context = AssertableWebApplicationContext + .get(() -> mock(ConfigurableWebApplicationContext.class)); + assertThat(context).isInstanceOf(ConfigurableWebApplicationContext.class); + } } diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebContextLoader.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/ReactiveWebApplicationContextTesterTests.java similarity index 54% rename from spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebContextLoader.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/context/ReactiveWebApplicationContextTesterTests.java index 77d671d8bc..6a7d7753f4 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebContextLoader.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/ReactiveWebApplicationContextTesterTests.java @@ -16,24 +16,20 @@ package org.springframework.boot.test.context; -import java.util.function.Supplier; - -import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext; +import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext; /** - * A {@link ContextLoader} that simulates a {@link GenericReactiveWebApplicationContext} - * which can be useful to test components that require a reactive web application. + * Tests for {@link ReactiveWebApplicationContextTester}. * - * @author Andy Wilkinson * @author Stephane Nicoll - * @since 2.0.0 + * @author Phillip Webb */ -public final class ReactiveWebContextLoader extends - AbstractContextLoader { +public class ReactiveWebApplicationContextTesterTests extends + AbstractApplicationContextTesterTests { - ReactiveWebContextLoader( - Supplier contextSupplier) { - super(contextSupplier); + @Override + protected ReactiveWebApplicationContextTester get() { + return new ReactiveWebApplicationContextTester(); } } diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/StandardContextLoaderTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/StandardContextLoaderTests.java deleted file mode 100644 index bd91cf6e4b..0000000000 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/context/StandardContextLoaderTests.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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.UUID; - -import com.google.gson.Gson; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.util.ClassUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; - -/** - * Tests for {@link StandardContextLoader}. - * - * @author Stephane Nicoll - */ -public class StandardContextLoaderTests { - - @Rule - public final ExpectedException thrown = ExpectedException.none(); - - private final StandardContextLoader contextLoader = new StandardContextLoader( - AnnotationConfigApplicationContext::new); - - @Test - public void systemPropertyIsSetAndRemoved() { - String key = "test." + UUID.randomUUID().toString(); - assertThat(System.getProperties().containsKey(key)).isFalse(); - this.contextLoader.systemProperty(key, "value").load(context -> { - assertThat(System.getProperties().containsKey(key)).isTrue(); - assertThat(System.getProperties().getProperty(key)).isEqualTo("value"); - }); - assertThat(System.getProperties().containsKey(key)).isFalse(); - } - - @Test - public void systemPropertyIsRemovedIfContextFailed() { - String key = "test." + UUID.randomUUID().toString(); - assertThat(System.getProperties().containsKey(key)).isFalse(); - this.contextLoader.systemProperty(key, "value").config(ConfigC.class) - .loadAndFail(e -> { - }); - assertThat(System.getProperties().containsKey(key)).isFalse(); - } - - @Test - public void systemPropertyIsRestoredToItsOriginalValue() { - String key = "test." + UUID.randomUUID().toString(); - System.setProperty(key, "value"); - try { - assertThat(System.getProperties().getProperty(key)).isEqualTo("value"); - this.contextLoader.systemProperty(key, "newValue").load(context -> { - assertThat(System.getProperties().getProperty(key)).isEqualTo("newValue"); - }); - assertThat(System.getProperties().getProperty(key)).isEqualTo("value"); - } - finally { - System.clearProperty(key); - } - } - - @Test - public void systemPropertyCanBeSetToNullValue() { - String key = "test." + UUID.randomUUID().toString(); - assertThat(System.getProperties().containsKey(key)).isFalse(); - this.contextLoader.systemProperty(key, "value").systemProperty(key, null) - .load(context -> { - assertThat(System.getProperties().containsKey(key)).isFalse(); - }); - } - - @Test - public void systemPropertyNeedNonNullKey() { - this.thrown.expect(IllegalArgumentException.class); - this.contextLoader.systemProperty(null, "value"); - } - - @Test - public void envIsAdditive() { - this.contextLoader.env("test.foo=1").env("test.bar=2").load(context -> { - ConfigurableEnvironment environment = context - .getBean(ConfigurableEnvironment.class); - assertThat(environment.getProperty("test.foo", Integer.class)).isEqualTo(1); - assertThat(environment.getProperty("test.bar", Integer.class)).isEqualTo(2); - }); - } - - @Test - public void envOverridesExistingKey() { - this.contextLoader.env("test.foo=1").env("test.foo=2") - .load(context -> assertThat(context.getBean(ConfigurableEnvironment.class) - .getProperty("test.foo", Integer.class)).isEqualTo(2)); - } - - @Test - public void configurationIsProcessedInOrder() { - this.contextLoader.config(ConfigA.class, AutoConfigA.class).load( - context -> assertThat(context.getBean("a")).isEqualTo("autoconfig-a")); - } - - @Test - public void configurationIsProcessedBeforeAutoConfiguration() { - this.contextLoader.autoConfig(AutoConfigA.class).config(ConfigA.class).load( - context -> assertThat(context.getBean("a")).isEqualTo("autoconfig-a")); - } - - @Test - public void configurationIsAdditive() { - this.contextLoader.config(AutoConfigA.class).config(AutoConfigB.class) - .load(context -> { - assertThat(context.containsBean("a")).isTrue(); - assertThat(context.containsBean("b")).isTrue(); - }); - } - - @Test - public void autoConfigureFirstIsAppliedProperly() { - this.contextLoader.autoConfig(ConfigA.class).autoConfigFirst(AutoConfigA.class) - .load(context -> assertThat(context.getBean("a")).isEqualTo("a")); - } - - @Test - public void autoConfigureFirstWithSeveralConfigsIsAppliedProperly() { - this.contextLoader.autoConfig(ConfigA.class, ConfigB.class) - .autoConfigFirst(AutoConfigA.class, AutoConfigB.class).load(context -> { - assertThat(context.getBean("a")).isEqualTo("a"); - assertThat(context.getBean("b")).isEqualTo(1); - }); - } - - @Test - public void autoConfigurationIsAdditive() { - this.contextLoader.autoConfig(AutoConfigA.class).autoConfig(AutoConfigB.class) - .load(context -> { - assertThat(context.containsBean("a")).isTrue(); - assertThat(context.containsBean("b")).isTrue(); - }); - } - - @Test - public void loadAndFailWithExpectedException() { - this.contextLoader.config(ConfigC.class).loadAndFail(BeanCreationException.class, - ex -> assertThat(ex.getMessage()) - .contains("Error creating bean with name 'c'")); - } - - @Test - public void loadAndFailWithWrongException() { - this.thrown.expect(AssertionError.class); - this.thrown.expectMessage("Wrong application context failure exception"); - this.contextLoader.config(ConfigC.class) - .loadAndFail(IllegalArgumentException.class, ex -> { - }); - } - - @Test - public void classLoaderIsUsed() { - this.contextLoader - .classLoader( - new HidePackagesClassLoader(Gson.class.getPackage().getName())) - .load(context -> { - try { - ClassUtils.forName(Gson.class.getName(), - context.getClassLoader()); - fail("Should have thrown a ClassNotFoundException"); - } - catch (ClassNotFoundException e) { - // expected - } - }); - } - - @Test - public void assertionErrorsAreAvailableAsIs() { - try { - this.contextLoader.load(context -> { - fail("This is expected"); - }); - } - catch (AssertionError ex) { - assertThat(ex.getMessage()).isEqualTo("This is expected"); - } - } - - @Configuration - static class ConfigA { - - @Bean - public String a() { - return "a"; - } - - } - - @Configuration - static class ConfigB { - - @Bean - public Integer b() { - return 1; - } - - } - - @Configuration - static class AutoConfigA { - - @Bean - public String a() { - return "autoconfig-a"; - } - - } - - @Configuration - static class AutoConfigB { - - @Bean - public Integer b() { - return 42; - } - - } - - @Configuration - static class ConfigC { - - @Bean - public String c(Integer value) { - return String.valueOf(value); - } - } - -} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/WebApplicationContextTesterTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/WebApplicationContextTesterTests.java new file mode 100644 index 0000000000..4b37ec6345 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/WebApplicationContextTesterTests.java @@ -0,0 +1,46 @@ +/* + * 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.springframework.mock.web.MockServletContext; +import org.springframework.web.context.ConfigurableWebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WebApplicationContextTester}. + * + * @author Stephane Nicoll + * @author Phillip Webb + */ +public class WebApplicationContextTesterTests extends + AbstractApplicationContextTesterTests { + + @Test + public void contextShouldHaveMockServletContext() throws Exception { + get().run((loaded) -> assertThat(loaded.getServletContext()) + .isInstanceOf(MockServletContext.class)); + } + + @Override + protected WebApplicationContextTester get() { + return new WebApplicationContextTester(); + } + +}