Where do the hints being tested come from, and what are the limits of predicate-based hint tests in a native-readiness strategy?
answer
- RuntimeHintsRegistrar + @ImportRuntimeHints
- @Reflective / ReflectiveRuntimeHintsRegistrar
- BeanRegistrationAotProcessor contributions
- isolation ≠ completeness
- predicate → AOT-context → native build layering
basics
~20 sHints come from RuntimeHintsRegistrar (via @ImportRuntimeHints), @Reflective-driven processors, and AOT bean-registration contributions. Predicate tests prove a specific hint was registered but not that the app's whole hint set is complete — you still need an actual native build or AOT test.
solid answer
~40 sThe `RuntimeHints` you assert against is populated by several sources: a `RuntimeHintsRegistrar` you register with `@ImportRuntimeHints`, annotation-driven contributions (`@Reflective` and its `ReflectiveRuntimeHintsRegistrar`), Spring Data / framework AOT processors, and per-bean `BeanRegistrationAotProcessor`/`BeanRegistrationAotContribution` output during context AOT processing. A predicate test typically instantiates just *your* registrar against a fresh `RuntimeHints` and asserts it. That's a precise, fast unit test — but it verifies only the hint you assert exists; it can't tell you the *aggregate* hint set is complete for a real native run. Missing hints from code paths you didn't think of still surface only in the native binary. So predicate tests guard your registrar's contract; genuine native-readiness also needs the AOT-processed context test (`GraalVM`/native build, or Spring's AOT test support) that exercises real bean wiring.
go deeper
Knows hints come from a registrar; limits are advanced.
Aware hints have multiple sources and that predicate tests are per-registrar.
Articulates the full contributor list and why predicate tests aren't completeness proofs; designs a layered test strategy.
Owns the native-readiness testing pyramid across a codebase and sets policy on predicate vs AOT-context vs native-build coverage.
## Where hints originate When Spring performs **AOT processing** (at build time, e.g. via the `process-aot` Gradle/Maven step or `ProcessAheadOfTimeCommand`), it aggregates `RuntimeHints` from many contributors: 1. **`RuntimeHintsRegistrar`** — your explicit registrar, wired with `@ImportRuntimeHints(MyHints.class)` on a `@Configuration` class or bean. This is what predicate tests most often target directly. 2. **`@Reflective`** — a meta-annotation; `ReflectiveRuntimeHintsRegistrar` scans annotated elements and registers reflection hints. Custom `@Reflective` processors (`ReflectiveProcessor`) can add tailored hints. 3. **`BeanRegistrationAotProcessor` / `BeanRegistrationAotContribution`** — per-bean contributions produced while Spring generates AOT bean definitions; frameworks (Spring Data, validation, etc.) plug in here. 4. **`BeanFactoryInitializationAotProcessor`** — factory-level contributions. 5. Framework built-ins for AOP proxies, `@ConfigurationProperties`, Jackson binding, etc. ## What a predicate test actually covers A typical predicate test does: ```java RuntimeHints hints = new RuntimeHints(); new MyHints().registerHints(hints, getClass().getClassLoader()); assertThat(RuntimeHintsPredicates.reflection().onType(MyDto.class)).accepts(hints); ``` It exercises exactly one contributor (`MyHints`) in isolation. Strengths: **fast** (milliseconds), **deterministic**, **no GraalVM**. It's the right tool to lock the *contract* of a registrar you own — a regression guard so a refactor doesn't silently drop a hint. ## The limits (the senior insight) - **Isolation ≠ completeness.** Passing predicates prove *the asserted hint is present*, not that the full set of hints your application needs is present. A hint you never thought to register (and never asserted) is invisible to this test. - **It doesn't run AOT processing.** Hints contributed by `BeanRegistrationAotProcessor`s only appear when Spring AOT-processes the actual `BeanFactory`. Newing up one registrar won't surface those. - **It doesn't run GraalVM.** Even a complete hint set can still fail native compilation for reasons predicates can't see (unsupported features, build-time initialization issues). - **Over-registration is invisible unless asserted.** Predicates won't warn you that you registered *too much* (footprint/attack surface) unless you add explicit `.rejects(...)` assertions. ## Complementary tests Spring provides infrastructure to AOT-process a test `ApplicationContext` and inspect the aggregated `RuntimeHints` (via `TestContextAotGenerator` / the AOT test support), which is closer to reality than a single registrar. Above that, a periodic **actual native build** (e.g. in CI, less frequently because it's slow) is the ground truth. The layering: predicate unit tests (fast, per-registrar) → AOT-context hint inspection (integration) → native build (slow, authoritative). ## When to reach for predicates Use them for **library or module-level registrars you author** — they give contributors a cheap, enforceable spec. Don't treat a green predicate suite as 'native-ready'; treat it as 'my registrar still does what it promised'.
- Why can't a predicate test catch a hint contributed by a BeanRegistrationAotProcessor?Those hints are produced during AOT processing of the actual BeanFactory. A predicate test that just instantiates one RuntimeHintsRegistrar never runs AOT processing, so those contributions are absent from its RuntimeHints instance.
- How would you catch over-registration of reflection hints?Add explicit negative assertions with .rejects(...) for types/members that must not be exposed. Predicates are silent about excess unless you assert it.