Document RuntimeHints testing strategies
Closes gh-29523
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
:chomp: default headers packages
|
||||
:docs-site: https://docs.spring.io
|
||||
// Spring Framework
|
||||
:docs-spring-framework: {docs-site}/spring-framework/docs/{spring-version}
|
||||
@@ -13,3 +14,4 @@
|
||||
:gh-rsocket: https://github.com/rsocket
|
||||
:gh-rsocket-extensions: {gh-rsocket}/rsocket/blob/master/Extensions
|
||||
:gh-rsocket-java: {gh-rsocket}/rsocket-java
|
||||
:doc-graalvm: https://www.graalvm.org/22.3/reference-manual
|
||||
|
||||
@@ -52,27 +52,13 @@ An application context is created with any number of entry points, usually in th
|
||||
|
||||
Let's look at a basic example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Configuration(proxyBeanMethods=false)
|
||||
@ComponentScan
|
||||
@Import({DataSourceConfiguration.class, ContainerConfiguration.class})
|
||||
public class MyApplication {
|
||||
}
|
||||
----
|
||||
include::code:AotProcessingSample[tag=myapplication]
|
||||
|
||||
Starting this application with the regular runtime involves a number of steps including classpath scanning, configuration class parsing, bean instantiation, and lifecycle callback handling.
|
||||
Refresh for AOT processing only applies a subset of what happens with a <<beans-introduction,regular `refresh`>>.
|
||||
AOT processing can be triggered as follows:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
GenericApplicationContext applicationContext = new AnnotatedConfigApplicationContext();
|
||||
context.register(MyApplication.class);
|
||||
context.refreshForAotProcessing();
|
||||
----
|
||||
include::code:AotProcessingSample[tag=aotcontext]
|
||||
|
||||
In this mode, <<beans-factory-extension-factory-postprocessors,`BeanFactoryPostProcessor` implementations>> are invoked as usual.
|
||||
This includes configuration class parsing, import selectors, classpath scanning, etc.
|
||||
@@ -229,24 +215,7 @@ A number of convenient annotations are also provided for common use cases.
|
||||
Implementations of this interface can be registered using `@ImportRuntimeHints` on any Spring bean or `@Bean` factory method.
|
||||
`RuntimeHintsRegistrar` implementations are detected and invoked at build time.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Component
|
||||
@ImportRuntimeHints(MyComponentRuntimeHints.class)
|
||||
public class MyComponent {
|
||||
|
||||
// ...
|
||||
|
||||
private static class MyComponentRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
include::code:SpellCheckService[]
|
||||
|
||||
If at all possible, `@ImportRuntimeHints` should be used as close as possible to the component that requires the hints.
|
||||
This way, if the component is not contributed to the `BeanFactory`, the hints won't be contributed either.
|
||||
@@ -290,3 +259,50 @@ The following example registers `Account` for serialization.
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
[[core.aot.hints.testing]]
|
||||
=== Testing Runtime Hints
|
||||
|
||||
Spring Core also ships `RuntimeHintsPredicates`, an utility for checking that existing hints match a particular use case.
|
||||
This can be used in your own tests, for validating that a `RuntimeHintsRegistrar` has the expected result.
|
||||
We can write a test for our `SpellCheckService` and ensure that we can load a dictionary at runtime:
|
||||
|
||||
include::code:SpellCheckServiceTests[tag=hintspredicates]
|
||||
|
||||
With `RuntimeHintsPredicates`, we can check for reflection, resource, serialization or proxy generation hints.
|
||||
This approach works well for unit tests but implies that the runtime behavior of a component is well known.
|
||||
|
||||
You can learn more about the global runtime behavior of an application by running its test suite (or the app itself) with the {doc-graalvm}/native-image/metadata/AutomaticMetadataCollection/[GraalVM tracing agent].
|
||||
This agent will record all relevant calls requiring GraalVM hints at runtime and write them out as JSON configuration files.
|
||||
|
||||
For more targeted discovery and testing, Spring Framework ships a dedicated module with core AOT testing utilities, `"org.springframework:spring-core-test"`.
|
||||
This module contains the RuntimeHints Agent, a Java agent that all method invocations that are related to runtime hints and helps you to assert that a given `RuntimeHints` instance covers all recorded invocations.
|
||||
Let's consider a piece of infrastructure for which we'd like to test the hints we're contributing during the AOT phase.
|
||||
|
||||
|
||||
include::code:SampleReflection[]
|
||||
|
||||
We can then write a Java test (no native compilation required!) that checks our contributed hints:
|
||||
|
||||
include::code:SampleReflectionRuntimeHintsTests[]
|
||||
|
||||
If you forgot to contribute a hint, the test will fail and give some details on the invocation:
|
||||
|
||||
[source,txt,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
org.springframework.docs.core.aot.hints.testing.SampleReflection performReflection
|
||||
INFO: Spring version:6.0.0-SNAPSHOT
|
||||
|
||||
Missing <"ReflectionHints"> for invocation <java.lang.Class#forName>
|
||||
with arguments ["org.springframework.core.SpringVersion",
|
||||
false,
|
||||
jdk.internal.loader.ClassLoaders$AppClassLoader@251a69d7].
|
||||
Stacktrace:
|
||||
<"org.springframework.util.ClassUtils#forName, Line 284
|
||||
io.spring.runtimehintstesting.SampleReflection#performReflection, Line 19
|
||||
io.spring.runtimehintstesting.SampleReflectionRuntimeHintsTests#lambda$shouldRegisterReflectionHints$0, Line 25
|
||||
----
|
||||
|
||||
There are various ways to configure this Java agent into your build, please refer to your build tool and test execution plugin documentation.
|
||||
The agent itself can be configured to instrument some packages (by default, only `org.springframework` is instrumented).
|
||||
You'll find more details in the https://github.com/spring-projects/spring-framework/blob/main/buildSrc/README.md[Spring Framework buildSrc README].
|
||||
|
||||
Reference in New Issue
Block a user