Throw ContextLoadException on test context load failure
When a test context fails to load, a `ContextLoadException` should be thrown so that Framework can catch it and call any registered `ApplicationContextFailureProcessor`s. Closes gh-31793
This commit is contained in:
@@ -55,6 +55,7 @@ import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextLoadException;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
@@ -116,7 +117,7 @@ public class SpringBootContextLoader extends AbstractContextLoader implements Ao
|
||||
}
|
||||
|
||||
private ApplicationContext loadContext(MergedContextConfiguration mergedConfig, Mode mode,
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext> initializer) {
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext> initializer) throws Exception {
|
||||
assertHasClassesOrLocations(mergedConfig);
|
||||
SpringBootTestAnnotation annotation = SpringBootTestAnnotation.get(mergedConfig);
|
||||
String[] args = annotation.getArgs();
|
||||
@@ -125,15 +126,12 @@ public class SpringBootContextLoader extends AbstractContextLoader implements Ao
|
||||
if (mainMethod != null) {
|
||||
ContextLoaderHook hook = new ContextLoaderHook(mode, initializer,
|
||||
(application) -> configure(mergedConfig, application));
|
||||
return hook.run(() -> ReflectionUtils.invokeMethod(mainMethod, null, new Object[] { args }));
|
||||
return hook.runMain(() -> ReflectionUtils.invokeMethod(mainMethod, null, new Object[] { args }));
|
||||
}
|
||||
SpringApplication application = getSpringApplication();
|
||||
configure(mergedConfig, application);
|
||||
if (mode == Mode.AOT_PROCESSING || mode == Mode.AOT_RUNTIME) {
|
||||
ContextLoaderHook hook = new ContextLoaderHook(mode, initializer, ALREADY_CONFIGURED);
|
||||
return hook.run(() -> application.run(args));
|
||||
}
|
||||
return application.run(args);
|
||||
ContextLoaderHook hook = new ContextLoaderHook(mode, initializer, ALREADY_CONFIGURED);
|
||||
return hook.run(() -> application.run(args));
|
||||
}
|
||||
|
||||
private void assertHasClassesOrLocations(MergedContextConfiguration mergedConfig) {
|
||||
@@ -465,10 +463,10 @@ public class SpringBootContextLoader extends AbstractContextLoader implements Ao
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SpringApplicationHook} used to capture the {@link ApplicationContext} and to
|
||||
* trigger early exit for the {@link Mode#AOT_PROCESSING} mode.
|
||||
* {@link SpringApplicationHook} used to capture {@link ApplicationContext} instances
|
||||
* and to trigger early exit for the {@link Mode#AOT_PROCESSING} mode.
|
||||
*/
|
||||
private class ContextLoaderHook implements SpringApplicationHook {
|
||||
private static class ContextLoaderHook implements SpringApplicationHook {
|
||||
|
||||
private final Mode mode;
|
||||
|
||||
@@ -478,6 +476,8 @@ public class SpringBootContextLoader extends AbstractContextLoader implements Ao
|
||||
|
||||
private final List<ApplicationContext> contexts = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
private final List<ApplicationContext> failedContexts = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
ContextLoaderHook(Mode mode, ApplicationContextInitializer<ConfigurableApplicationContext> initializer,
|
||||
Consumer<SpringApplication> configurer) {
|
||||
this.mode = mode;
|
||||
@@ -506,15 +506,36 @@ public class SpringBootContextLoader extends AbstractContextLoader implements Ao
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(ConfigurableApplicationContext context, Throwable exception) {
|
||||
ContextLoaderHook.this.failedContexts.add(context);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private <T> ApplicationContext run(ThrowingSupplier<T> action) {
|
||||
private <T> ApplicationContext runMain(Runnable action) throws Exception {
|
||||
return run(() -> {
|
||||
action.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private ApplicationContext run(ThrowingSupplier<ConfigurableApplicationContext> action) throws Exception {
|
||||
try {
|
||||
SpringApplication.withHook(this, action);
|
||||
ConfigurableApplicationContext context = SpringApplication.withHook(this, action);
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
catch (AbandonedRunException ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (this.failedContexts.size() == 1) {
|
||||
throw new ContextLoadException(this.failedContexts.get(0), ex);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
List<ApplicationContext> rootContexts = this.contexts.stream()
|
||||
.filter((context) -> context.getParent() == null).toList();
|
||||
Assert.state(!rootContexts.isEmpty(), "No root application context located");
|
||||
|
||||
Reference in New Issue
Block a user