Skip to content

Commit 1020793

Browse files
committed
Output condition evaluation report when app under test fails to start
Closes gh-42185
1 parent 7b9cd51 commit 1020793

File tree

5 files changed

+164
-6
lines changed

5 files changed

+164
-6
lines changed

spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/ConditionReportApplicationContextFailureProcessor.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,7 +29,9 @@
2929
* @author Phillip Webb
3030
* @author Scott Frederick
3131
* @since 3.0.0
32+
* @deprecated in 3.2.11 for removal in 3.6.0
3233
*/
34+
@Deprecated(since = "3.2.11", forRemoval = true)
3335
public class ConditionReportApplicationContextFailureProcessor implements ApplicationContextFailureProcessor {
3436

3537
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure;
18+
19+
import java.util.List;
20+
import java.util.function.Supplier;
21+
22+
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
23+
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportMessage;
24+
import org.springframework.boot.context.event.ApplicationFailedEvent;
25+
import org.springframework.context.ApplicationContext;
26+
import org.springframework.context.ApplicationListener;
27+
import org.springframework.context.ConfigurableApplicationContext;
28+
import org.springframework.context.support.GenericApplicationContext;
29+
import org.springframework.test.context.ContextConfigurationAttributes;
30+
import org.springframework.test.context.ContextCustomizer;
31+
import org.springframework.test.context.ContextCustomizerFactory;
32+
import org.springframework.test.context.MergedContextConfiguration;
33+
34+
/**
35+
* {@link ContextCustomizerFactory} that customizes the {@link ApplicationContext
36+
* application context} such that a {@link ConditionEvaluationReport condition evaluation
37+
* report} is output when the application under test {@link ApplicationFailedEvent fails
38+
* to start}.
39+
*
40+
* @author Andy Wilkinson
41+
*/
42+
class OnFailureConditionReportContextCustomizerFactory implements ContextCustomizerFactory {
43+
44+
@Override
45+
public ContextCustomizer createContextCustomizer(Class<?> testClass,
46+
List<ContextConfigurationAttributes> configAttributes) {
47+
return new OnFailureConditionReportContextCustomizer();
48+
}
49+
50+
static class OnFailureConditionReportContextCustomizer implements ContextCustomizer {
51+
52+
@Override
53+
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
54+
Supplier<ConditionEvaluationReport> reportSupplier;
55+
if (context instanceof GenericApplicationContext) {
56+
ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getBeanFactory());
57+
reportSupplier = () -> report;
58+
}
59+
else {
60+
reportSupplier = () -> ConditionEvaluationReport.get(context.getBeanFactory());
61+
}
62+
context.addApplicationListener(new ApplicationFailureListener(reportSupplier));
63+
}
64+
65+
@Override
66+
public boolean equals(Object obj) {
67+
return (obj != null) && (obj.getClass() == getClass());
68+
}
69+
70+
@Override
71+
public int hashCode() {
72+
return getClass().hashCode();
73+
}
74+
75+
}
76+
77+
private static final class ApplicationFailureListener implements ApplicationListener<ApplicationFailedEvent> {
78+
79+
private final Supplier<ConditionEvaluationReport> reportSupplier;
80+
81+
private ApplicationFailureListener(Supplier<ConditionEvaluationReport> reportSupplier) {
82+
this.reportSupplier = reportSupplier;
83+
}
84+
85+
@Override
86+
public void onApplicationEvent(ApplicationFailedEvent event) {
87+
System.err.println(new ConditionEvaluationReportMessage(this.reportSupplier.get()));
88+
}
89+
90+
}
91+
92+
}

spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Spring Test Context Customizer Factories
22
org.springframework.test.context.ContextCustomizerFactory=\
3+
org.springframework.boot.test.autoconfigure.OnFailureConditionReportContextCustomizerFactory,\
34
org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory,\
45
org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory,\
56
org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizerFactory,\
@@ -13,8 +14,3 @@ org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerRese
1314
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener,\
1415
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener,\
1516
org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener
16-
17-
# Spring Test ApplcationContext Failure Processors
18-
org.springframework.test.context.ApplicationContextFailureProcessor=\
19-
org.springframework.boot.test.autoconfigure.ConditionReportApplicationContextFailureProcessor
20-

spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/ConditionReportApplicationContextFailureProcessorTests.java

+3
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@
3535
*
3636
* @author Phillip Webb
3737
* @author Scott Frederick
38+
* @deprecated since 3.2.11 for removal in 3.6.0
3839
*/
3940
@ExtendWith(OutputCaptureExtension.class)
41+
@Deprecated(since = "3.2.11", forRemoval = true)
42+
@SuppressWarnings("removal")
4043
class ConditionReportApplicationContextFailureProcessorTests {
4144

4245
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.ExtendWith;
21+
22+
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
23+
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
24+
import org.springframework.boot.test.context.SpringBootTest;
25+
import org.springframework.boot.test.system.CapturedOutput;
26+
import org.springframework.boot.test.system.OutputCaptureExtension;
27+
import org.springframework.context.annotation.Bean;
28+
import org.springframework.context.annotation.Configuration;
29+
import org.springframework.test.context.TestContextManager;
30+
31+
import static org.assertj.core.api.Assertions.assertThat;
32+
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
33+
34+
/**
35+
* Tests for {@link OnFailureConditionReportContextCustomizerFactory}.
36+
*
37+
* @author Andy Wilkinson
38+
*/
39+
@ExtendWith(OutputCaptureExtension.class)
40+
class OnFailureConditionReportContextCustomizerFactoryTests {
41+
42+
@Test
43+
void loadFailureShouldPrintReport(CapturedOutput output) {
44+
assertThatIllegalStateException()
45+
.isThrownBy(() -> new TestContextManager(FailingTests.class).getTestContext().getApplicationContext());
46+
assertThat(output).contains("JacksonAutoConfiguration matched");
47+
}
48+
49+
@SpringBootTest
50+
static class FailingTests {
51+
52+
@Configuration(proxyBeanMethods = false)
53+
@ImportAutoConfiguration(JacksonAutoConfiguration.class)
54+
static class TestConfig {
55+
56+
@Bean
57+
String faultyBean() {
58+
throw new IllegalStateException();
59+
}
60+
61+
}
62+
63+
}
64+
65+
}

0 commit comments

Comments
 (0)