What is RuntimeHintsPredicates and why would you use it in a unit test?
answer
- org.springframework.aot.hint.predicate package
- Predicate<RuntimeHints> factory
- reflection/resource/proxies/serialization
- AssertJ accepts / rejects
- fast test, no native build
basics
~20 sRuntimeHintsPredicates is a Spring test helper that builds predicates checking whether a RuntimeHintsRegistrar registered the expected reflection, resource, or proxy hints — so you can assert hints in a fast unit test instead of running a slow native build.
solid answer
~40 s`RuntimeHintsPredicates` (in `org.springframework.aot.hint.predicate`) is a utility that creates `Predicate<RuntimeHints>` objects for asserting what hints were contributed. In a GraalVM native image, anything accessed by reflection, loaded as a resource, or wrapped in a JDK proxy must be declared ahead of time via hints; if a hint is missing you only find out at runtime in the native binary, which is slow to build. A predicate test lets you run your `RuntimeHintsRegistrar` against a fresh `RuntimeHints` instance and assert, in milliseconds, that the right hint exists — for example `RuntimeHintsPredicates.reflection().onType(MyDto.class)`. You pair it with AssertJ: `assertThat(predicate).accepts(hints)`. It's a fast feedback loop that catches missing-hint regressions before they reach the native pipeline.
go deeper
Know it's a test helper for asserting AOT/native hints without a native build, and that it uses AssertJ accepts/rejects.
Should write a basic reflection predicate test against a hand-run RuntimeHintsRegistrar.
Explains why hints exist (closed-world native), the four predicate categories, and the limits of predicate testing.
Positions predicate tests within a broader native-readiness testing strategy and library ownership.
## The problem it solves When you compile a Spring app to a **GraalVM native image** (Ahead-Of-Time / AOT compilation), the closed-world assumption means the compiler must know at build time about everything reached by **reflection** (e.g. `Class.forName`, invoking a constructor by name), **resources** loaded from the classpath (e.g. `messages.properties`), **JDK dynamic proxies**, and **serialization**. These dynamic behaviours are invisible to static analysis, so Spring (and you) must *declare* them as **runtime hints**. Hints are contributed programmatically through the `RuntimeHintsRegistrar` interface: ```java class MyHints implements RuntimeHintsRegistrar { public void registerHints(RuntimeHints hints, ClassLoader cl) { hints.reflection().registerType(MyDto.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); } } ``` registered via `@ImportRuntimeHints(MyHints.class)`. **The gotcha:** if you forget a hint, the app compiles fine and only *fails at runtime inside the native binary* (e.g. a `ClassNotFoundException` or a missing-resource error). Native builds take minutes, so this is a painful, slow feedback loop. ## What RuntimeHintsPredicates gives you `org.springframework.aot.hint.predicate.RuntimeHintsPredicates` is a **static factory** for `Predicate<RuntimeHints>`. Each predicate answers 'does this `RuntimeHints` object contain the hint I expect?'. Entry points mirror the hint categories: - `reflection()` — reflection hints on types/methods/constructors/fields - `resource()` — resource pattern / resource bundle hints - `proxies()` — JDK dynamic proxy hints for interface sets - `serialization()` — Java serialization hints Example: ```java RuntimeHints hints = new RuntimeHints(); new MyHints().registerHints(hints, getClass().getClassLoader()); assertThat(RuntimeHintsPredicates.reflection().onType(MyDto.class)) .accepts(hints); ``` Here `assertThat(...)` is **AssertJ's** predicate assertion, and `.accepts(hints)` passes if the predicate returns true for that `RuntimeHints`. (`.rejects(hints)` asserts the opposite.) A predicate is just a `Predicate`, so you can also call `predicate.test(hints)` directly. ## Why it matters It turns 'did my registrar do its job?' into a **plain JUnit test** running in milliseconds, giving you a regression guard on hints without ever invoking GraalVM. It does **not** replace an actual native build test — a passing predicate only proves the hint *was registered*, not that the hint set is *complete* for the whole app. ## When to use Write a predicate test whenever you author a custom `RuntimeHintsRegistrar`, a `@Reflective`-driven contribution, or any AOT code that registers hints — especially in libraries meant to be native-ready.
- Does a passing predicate test guarantee the native image will work?No. It only proves the specific hint you asserted was registered. Completeness of the whole hint set for a real native run still needs an actual GraalVM build or the AOT test infrastructure.