How can you access the ConditionEvaluationReport programmatically, e.g. in a test, instead of reading logs?
answer
- ConditionEvaluationReport.get(beanFactory)
- getConditionAndOutcomesBySource / getExclusions
- ApplicationContextRunner + AutoConfigurations.of
- FilteredClassLoader to fake a missing class
- Actuator /conditions endpoint
basics
~10 sThe report is itself a bean. Call ConditionEvaluationReport.get(beanFactory) to obtain it, then inspect getConditionAndOutcomesBySource(). In tests, ApplicationContextRunner exposes it via assertThat(context).getBean(...) or by inspecting the runner's context so you can assert on matches.
solid answer
~30 sConditionEvaluationReport is stored in the bean factory; retrieve it with the static ConditionEvaluationReport.get(ConfigurableListableBeanFactory). It exposes getConditionAndOutcomesBySource() (a map keyed by source class → conditions and their ConditionOutcome), getExclusions(), and getUnconditionalClasses(). This lets you build custom diagnostics or assert on decisions in tests. The idiomatic test tool is ApplicationContextRunner (WebApplicationContextRunner / ReactiveWebApplicationContextRunner for web): you configure it with .withConfiguration(AutoConfigurations.of(...)), run it, and assert via AssertableApplicationContext — e.g. assertThat(context).hasSingleBean(Foo.class) or .doesNotHaveBean(...). That verifies the condition outcome you care about without parsing logs. You can also expose the report through the Actuator 'conditions' endpoint (management.endpoints.web.exposure.include=conditions) for a running app.
code
java · 22 linesimport org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.FilteredClassLoader;
import static org.assertj.core.api.Assertions.assertThat;
class MyAutoConfigurationTests {
private final ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(MyAutoConfiguration.class));
@Test
void createsBeanWhenPropertyEnabled() {
runner.withPropertyValues("my.feature.enabled=true")
.run(ctx -> assertThat(ctx).hasSingleBean(MyService.class));
}
@Test
void backsOffWhenClassAbsent() {
runner.withClassLoader(new FilteredClassLoader(MyService.class))
.run(ctx -> assertThat(ctx).doesNotHaveBean(MyService.class));
}
}go deeper
Awareness that the report can be reached programmatically and via Actuator, even if not the exact API.
Know the Actuator conditions endpoint and the ApplicationContextRunner basics.
Fluent with ApplicationContextRunner, FilteredClassLoader, and ConditionEvaluationReport.get for tests/tooling.
Choose the right tool per situation and reason about per-context reports in hierarchies.
## The report is a bean After the context refreshes, a `ConditionEvaluationReport` object lives in the bean factory. Spring Boot's own logging listener just formats it. You can grab it yourself: ```java ConditionEvaluationReport report = ConditionEvaluationReport.get( (ConfigurableListableBeanFactory) context.getBeanFactory()); ``` Key methods: - `getConditionAndOutcomesBySource()` → `Map<String, ConditionAndOutcomes>` keyed by the source (config class / bean method). Each `ConditionAndOutcomes` iterates `ConditionAndOutcome` pairs; `isFullMatch()` tells you if the source matched. Each `ConditionOutcome` has `isMatch()` and `getMessage()`. - `getExclusions()` → set of excluded class names. - `getUnconditionalClasses()` → set of always-applied class names. - `getParent()` → report of the parent context, if any (relevant with a hierarchy). ## Testing: ApplicationContextRunner The modern, preferred way to test conditions is **`ApplicationContextRunner`** (part of `spring-boot-test`). It spins up a throwaway context lazily and gives you an `AssertableApplicationContext`: ```java new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(MyAutoConfiguration.class)) .withPropertyValues("my.feature.enabled=true") .run(context -> assertThat(context).hasSingleBean(MyService.class)); ``` Variants: `WebApplicationContextRunner` (servlet) and `ReactiveWebApplicationContextRunner` (reactive). You can add user beans (`.withUserConfiguration(...)`), classpath filters (`.withClassLoader(new FilteredClassLoader(SomeClass.class))` to simulate a missing dependency for `@ConditionalOnClass`), and property values. Assertions: `hasSingleBean`, `doesNotHaveBean`, `hasBean`, `getFailure`, etc. This is exactly how Spring Boot tests its own auto-configs. You can even reach the report inside the runner: ```java .run(context -> { ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getSourceApplicationContext().getBeanFactory()); // assert on report.getConditionAndOutcomesBySource() }); ``` ## Actuator endpoint For a running app, the **`conditions`** Actuator endpoint (`/actuator/conditions`) returns the same information as JSON: positiveMatches, negativeMatches, exclusions, unconditionalClasses — per context. Enable with `management.endpoints.web.exposure.include=conditions`. Useful in ops without restarting with `--debug`. ## When to use which - **Ad-hoc, once:** `--debug` and read the log. - **Regression test that a bean is/ isn't created under given conditions:** `ApplicationContextRunner`. - **Live running system, ops diagnosis:** Actuator `conditions` endpoint. - **Custom tooling / programmatic assertions:** `ConditionEvaluationReport.get(...)`. ## Gotchas - `ConditionEvaluationReport.get` returns the report for that specific bean factory; in a parent/child hierarchy each context has its own. - `FilteredClassLoader` is the trick for testing `@ConditionalOnClass`/`@ConditionalOnMissingClass` without actually changing the module's dependencies. - The report reflects decisions at refresh time; it isn't recomputed later.
- How would you test that @ConditionalOnClass correctly backs off when a dependency is absent, without editing your build file?Use ApplicationContextRunner.withClassLoader(new FilteredClassLoader(TheClass.class)); the FilteredClassLoader hides that class from the context so the OnClassCondition fails, then assert doesNotHaveBean.
- What Actuator endpoint exposes the same information as the --debug report?The 'conditions' endpoint (/actuator/conditions), which returns positiveMatches, negativeMatches, exclusions and unconditionalClasses as JSON. It must be exposed via management.endpoints.web.exposure.include.
saying these in an interview costs you the question
- Claiming the report can only be read from logs
- Using a full @SpringBootTest to test a single auto-config's conditions instead of ApplicationContextRunner
- Not knowing FilteredClassLoader exists for simulating missing classes