Improve the type-safety of ContextLoader for servlet and reactive web

This commit is contained in:
Andy Wilkinson
2017-07-02 15:08:09 +01:00
committed by Stephane Nicoll
parent 19ddfad63e
commit dd0ce54425
18 changed files with 1238 additions and 958 deletions

View File

@@ -20,22 +20,21 @@ import org.springframework.context.ConfigurableApplicationContext;
/**
* Callback interface used in tests to process a running
* {@link ConfigurableApplicationContext} with the ability to throw a (checked)
* exception.
* {@link ConfigurableApplicationContext} with the ability to throw a (checked) exception.
*
* @author Stephane Nicoll
* @author Andy Wilkinson
* @since 2.0.0
* @param <T> the type of the context that can be consumed
*/
@FunctionalInterface
public interface ContextConsumer {
public interface ContextConsumer<T extends ConfigurableApplicationContext> {
/**
* Performs this operation on the supplied {@link ConfigurableApplicationContext
* ApplicationContext}.
* Performs this operation on the supplied {@code context}.
* @param context the application context to consume
* @throws Throwable any exception that might occur in assertions
*/
void accept(ConfigurableApplicationContext context) throws Throwable;
void accept(T context) throws Throwable;
}

View File

@@ -16,32 +16,15 @@
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.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.boot.web.reactive.context.GenericReactiveWebApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigRegistry;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.mock.web.MockServletContext;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 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:
@@ -91,20 +74,45 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Stephane Nicoll
* @author Andy Wilkinson
* @since 2.0.0
* @param <T> the type of the context to be loaded
*/
public class ContextLoader {
public interface ContextLoader<T extends ConfigurableApplicationContext> {
private final Map<String, String> systemProperties = new HashMap<>();
/**
* Creates a {@code ContextLoader} that will load a standard
* {@link AnnotationConfigApplicationContext}.
*
* @return the context loader
*/
static ContextLoader<AnnotationConfigApplicationContext> standard() {
return new StandardContextLoader<>(
() -> new AnnotationConfigApplicationContext());
}
private final List<String> env = new ArrayList<>();
/**
* Creates a {@code ContextLoader} that will load a
* {@link AnnotationConfigWebApplicationContext}.
*
* @return the context loader
*/
static ContextLoader<AnnotationConfigWebApplicationContext> servletWeb() {
return new StandardContextLoader<>(() -> {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
return context;
});
}
private final Set<Class<?>> userConfigurations = new LinkedHashSet<>();
private final LinkedList<Class<?>> autoConfigurations = new LinkedList<>();
private Supplier<ConfigurableApplicationContext> contextSupplier = () -> new AnnotationConfigApplicationContext();
private ClassLoader classLoader;
/**
* Creates a {@code ContextLoader} that will load a
* {@link GenericReactiveWebApplicationContext}.
*
* @return the context loader
*/
static ContextLoader<GenericReactiveWebApplicationContext> reactiveWeb() {
return new StandardContextLoader<>(
() -> new GenericReactiveWebApplicationContext());
}
/**
* Set the specified system property prior to loading the context and restore its
@@ -114,16 +122,7 @@ public class ContextLoader {
* @param value the value (can be null to remove any existing customization)
* @return this instance
*/
public ContextLoader 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 this;
}
public ContextLoader<T> systemProperty(String key, String value);
/**
* Add the specified property pairs. Key-value pairs can be specified with colon (":")
@@ -133,36 +132,21 @@ public class ContextLoader {
* environment
* @return this instance
*/
public ContextLoader env(String... pairs) {
if (!ObjectUtils.isEmpty(pairs)) {
this.env.addAll(Arrays.asList(pairs));
}
return this;
}
public ContextLoader<T> env(String... pairs);
/**
* Add the specified user configuration classes.
* @param configs the user configuration classes to add
* @return this instance
*/
public ContextLoader config(Class<?>... configs) {
if (!ObjectUtils.isEmpty(configs)) {
this.userConfigurations.addAll(Arrays.asList(configs));
}
return this;
}
public ContextLoader<T> config(Class<?>... configs);
/**
* Add the specified auto-configuration classes.
* @param autoConfigurations the auto-configuration classes to add
* @return this instance
*/
public ContextLoader autoConfig(Class<?>... autoConfigurations) {
if (!ObjectUtils.isEmpty(autoConfigurations)) {
this.autoConfigurations.addAll(Arrays.asList(autoConfigurations));
}
return this;
}
public ContextLoader<T> autoConfig(Class<?>... autoConfigurations);
/**
* Add the specified auto-configurations at the beginning (in that order) so that it
@@ -172,10 +156,7 @@ public class ContextLoader {
* @param autoConfigurations the auto-configuration to add
* @return this instance
*/
public ContextLoader autoConfigFirst(Class<?>... autoConfigurations) {
this.autoConfigurations.addAll(0, Arrays.asList(autoConfigurations));
return this;
}
public ContextLoader<T> autoConfigFirst(Class<?>... autoConfigurations);
/**
* Customize the {@link ClassLoader} that the {@link ApplicationContext} should use.
@@ -185,58 +166,15 @@ public class ContextLoader {
* @return this instance
* @see HidePackagesClassLoader
*/
public ContextLoader classLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}
/**
* Configures the loader to create an {@link ApplicationContext} suitable for use in a
* reactive web application.
* @return this instance
*/
public ContextLoader webReactive() {
this.contextSupplier = () -> {
return new GenericReactiveWebApplicationContext();
};
return this;
}
/**
* Configures the loader to create an {@link ApplicationContext} suitable for use in a
* servlet web application.
* @return this instance
*/
public ContextLoader webServlet() {
this.contextSupplier = () -> {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
return context;
};
return this;
}
public ContextLoader<T> classLoader(ClassLoader classLoader);
/**
* 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.
* this loader. The context is consumed by the specified {@code consumers} and closed
* upon completion.
* @param consumer the consumer of the created {@link ApplicationContext}
*/
public void load(ContextConsumer consumer) {
try (ApplicationContextLifecycleHandler handler = new ApplicationContextLifecycleHandler()) {
try {
ConfigurableApplicationContext ctx = handler.load();
consumer.accept(ctx);
}
catch (RuntimeException ex) {
throw ex;
}
catch (Throwable ex) {
throw new IllegalStateException(
"An unexpected error occurred: " + ex.getMessage(), ex);
}
}
}
public void load(ContextConsumer<T> consumer);
/**
* Create and refresh a new {@link ApplicationContext} based on the current state of
@@ -245,9 +183,7 @@ public class ContextLoader {
* specified {@link Consumer} with no expectation on the type of the exception.
* @param consumer the consumer of the failure
*/
public void loadAndFail(Consumer<Throwable> consumer) {
loadAndFail(Throwable.class, consumer);
}
public void loadAndFail(Consumer<Throwable> consumer);
/**
* Create and refresh a new {@link ApplicationContext} based on the current state of
@@ -257,98 +193,9 @@ public class ContextLoader {
* 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 <T> the expected type of the failure
* @param <E> the expected type of the failure
*/
public <T extends Throwable> void loadAndFail(Class<T> exceptionType,
Consumer<T> 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 ConfigurableApplicationContext createApplicationContext() {
ConfigurableApplicationContext context = ContextLoader.this.contextSupplier.get();
if (this.classLoader != null) {
((DefaultResourceLoader) context).setClassLoader(this.classLoader);
}
if (!ObjectUtils.isEmpty(this.env)) {
TestPropertyValues.of(this.env.toArray(new String[this.env.size()]))
.applyTo(context);
}
AnnotationConfigRegistry registry = ((AnnotationConfigRegistry) context);
if (!ObjectUtils.isEmpty(this.userConfigurations)) {
registry.register(this.userConfigurations
.toArray(new Class<?>[this.userConfigurations.size()]));
}
if (!ObjectUtils.isEmpty(this.autoConfigurations)) {
LinkedHashSet<Class<?>> linkedHashSet = new LinkedHashSet<>(
this.autoConfigurations);
registry.register(
linkedHashSet.toArray(new Class<?>[this.autoConfigurations.size()]));
}
return context;
}
/**
* Handles the lifecycle of the {@link ApplicationContext}.
*/
private class ApplicationContextLifecycleHandler implements Closeable {
private final Map<String, String> customSystemProperties;
private final Map<String, String> previousSystemProperties = new HashMap<>();
private ConfigurableApplicationContext context;
ApplicationContextLifecycleHandler() {
this.customSystemProperties = new HashMap<>(
ContextLoader.this.systemProperties);
}
public ConfigurableApplicationContext load() {
setCustomSystemProperties();
ConfigurableApplicationContext context = createApplicationContext();
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);
}
});
}
}
public <E extends Throwable> void loadAndFail(Class<E> exceptionType,
Consumer<E> consumer);
}

View File

@@ -0,0 +1,300 @@
/*
* 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.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;
/**
* Standard implementation of {@link ContextLoader}.
*
* @author Stephane Nicoll
* @author Andy Wilkinson
* @param <T> the type of the context to be loaded
*/
final class StandardContextLoader<T extends ConfigurableApplicationContext & AnnotationConfigRegistry>
implements ContextLoader<T> {
private final Map<String, String> systemProperties = new HashMap<>();
private final List<String> env = new ArrayList<>();
private final Set<Class<?>> userConfigurations = new LinkedHashSet<>();
private final LinkedList<Class<?>> autoConfigurations = new LinkedList<>();
private final Supplier<T> contextSupplier;
private ClassLoader classLoader;
StandardContextLoader(Supplier<T> 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 StandardContextLoader<T> 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 this;
}
/**
* 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 StandardContextLoader<T> env(String... pairs) {
if (!ObjectUtils.isEmpty(pairs)) {
this.env.addAll(Arrays.asList(pairs));
}
return this;
}
/**
* Add the specified user configuration classes.
* @param configs the user configuration classes to add
* @return this instance
*/
@Override
public StandardContextLoader<T> config(Class<?>... configs) {
if (!ObjectUtils.isEmpty(configs)) {
this.userConfigurations.addAll(Arrays.asList(configs));
}
return this;
}
/**
* Add the specified auto-configuration classes.
* @param autoConfigurations the auto-configuration classes to add
* @return this instance
*/
@Override
public StandardContextLoader<T> autoConfig(Class<?>... autoConfigurations) {
if (!ObjectUtils.isEmpty(autoConfigurations)) {
this.autoConfigurations.addAll(Arrays.asList(autoConfigurations));
}
return this;
}
/**
* 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 StandardContextLoader<T> autoConfigFirst(Class<?>... autoConfigurations) {
this.autoConfigurations.addAll(0, Arrays.asList(autoConfigurations));
return this;
}
/**
* 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 StandardContextLoader<T> classLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return 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<T> consumer) {
try (ApplicationContextLifecycleHandler handler = new ApplicationContextLifecycleHandler()) {
try {
T ctx = handler.load();
consumer.accept(ctx);
}
catch (RuntimeException 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<Throwable> 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 <E> the expected type of the failure
*/
@Override
public <E extends Throwable> void loadAndFail(Class<E> exceptionType,
Consumer<E> 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 = StandardContextLoader.this.contextSupplier.get();
if (this.classLoader != null) {
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.classLoader);
}
else {
throw new IllegalStateException("Cannot configure ClassLoader: " + context
+ " is not a DefaultResourceLoader sub-class");
}
}
if (!ObjectUtils.isEmpty(this.env)) {
TestPropertyValues.of(this.env.toArray(new String[this.env.size()]))
.applyTo(context);
}
if (!ObjectUtils.isEmpty(this.userConfigurations)) {
context.register(this.userConfigurations
.toArray(new Class<?>[this.userConfigurations.size()]));
}
if (!ObjectUtils.isEmpty(this.autoConfigurations)) {
LinkedHashSet<Class<?>> linkedHashSet = new LinkedHashSet<>(
this.autoConfigurations);
context.register(
linkedHashSet.toArray(new Class<?>[this.autoConfigurations.size()]));
}
return context;
}
/**
* Handles the lifecycle of the {@link ApplicationContext}.
*/
private class ApplicationContextLifecycleHandler implements Closeable {
private final Map<String, String> customSystemProperties;
private final Map<String, String> previousSystemProperties = new HashMap<>();
private ConfigurableApplicationContext context;
ApplicationContextLifecycleHandler() {
this.customSystemProperties = new HashMap<>(
StandardContextLoader.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);
}
});
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.test.rule;
package org.springframework.boot.test.context;
import java.util.UUID;
@@ -24,27 +24,28 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.test.context.ContextLoader;
import org.springframework.boot.test.context.HidePackagesClassLoader;
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.test.context.support.AbstractContextLoader;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
/**
* Tests for {@link ContextLoader}.
* Tests for {@link AbstractContextLoader}.
*
* @author Stephane Nicoll
*/
public class ContextLoaderTests {
public class StandardContextLoaderTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private final ContextLoader contextLoader = new ContextLoader();
private final StandardContextLoader<AnnotationConfigApplicationContext> contextLoader = new StandardContextLoader<>(
() -> new AnnotationConfigApplicationContext());
@Test
public void systemPropertyIsSetAndRemoved() {
@@ -61,9 +62,9 @@ public class ContextLoaderTests {
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 -> {
});
this.contextLoader.systemProperty(key, "value").config(ConfigC.class)
.loadAndFail(e -> {
});
assertThat(System.getProperties().containsKey(key)).isFalse();
}
@@ -87,10 +88,10 @@ public class ContextLoaderTests {
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();
});
this.contextLoader.systemProperty(key, "value").systemProperty(key, null)
.load(context -> {
assertThat(System.getProperties().containsKey(key)).isFalse();
});
}
@Test
@@ -102,8 +103,8 @@ public class ContextLoaderTests {
@Test
public void envIsAdditive() {
this.contextLoader.env("test.foo=1").env("test.bar=2").load(context -> {
ConfigurableEnvironment environment = context.getBean(
ConfigurableEnvironment.class);
ConfigurableEnvironment environment = context
.getBean(ConfigurableEnvironment.class);
assertThat(environment.getProperty("test.foo", Integer.class)).isEqualTo(1);
assertThat(environment.getProperty("test.bar", Integer.class)).isEqualTo(2);
});
@@ -111,45 +112,42 @@ public class ContextLoaderTests {
@Test
public void envOverridesExistingKey() {
this.contextLoader.env("test.foo=1").env("test.foo=2").load(context ->
assertThat(context.getBean(ConfigurableEnvironment.class)
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"));
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"));
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();
});
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"));
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 -> {
.autoConfigFirst(AutoConfigA.class, AutoConfigB.class).load(context -> {
assertThat(context.getBean("a")).isEqualTo("a");
assertThat(context.getBean("b")).isEqualTo(1);
});
@@ -157,18 +155,18 @@ public class ContextLoaderTests {
@Test
public void autoConfigurationIsAdditive() {
this.contextLoader.autoConfig(AutoConfigA.class)
.autoConfig(AutoConfigB.class).load(context -> {
assertThat(context.containsBean("a")).isTrue();
assertThat(context.containsBean("b")).isTrue();
});
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'"));
this.contextLoader.config(ConfigC.class).loadAndFail(BeanCreationException.class,
ex -> assertThat(ex.getMessage())
.contains("Error creating bean with name 'c'"));
}
@Test
@@ -182,16 +180,19 @@ public class ContextLoaderTests {
@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
}
});
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
}
});
}
@Configuration