Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing with Spring, AOT, GraalVM, and JUnit 5

Testing with Spring, AOT, GraalVM, and JUnit 5

Attend this talk to learn about the latest and greatest in the world of testing using the Spring Framework and JUnit Jupiter (a.k.a. JUnit 5) including tips for testing with Spring AOT and GraalVM native images.

Sam Brannen

May 24, 2023
Tweet

More Decks by Sam Brannen

Other Decks in Technology

Transcript

  1. • This presentation may contain product features or functionality that

    are currently under development. • This overview of new technology represents no commitment from VMware to deliver these features in any generally available product. • Features are subject to change, and must not be included in contracts, purchase orders, or sales agreements of any kind. • Technical feasibility and market demand will affect final delivery. • Pricing and packaging for any new features/functionality/technology discussed or presented, have not been determined. • The information in this presentation is for informational purposes only and may not be incorporated into any contract. There is no commitment or obligation to deliver any items presented herein. Disclaimer
  2. Sam Brannen • Staff Software Engineer • Java Developer for

    over 20 years • Spring Framework Core Committer since 2007 • JUnit 5 Core Committer since October 2015
  3. Agenda • JUnit 5.9, 5.10, 5.11 • Spring 6.0 and

    6.1 • Spring AOT and GraalVM • Demo
  4. JUnit Platform 1.9.x • XML reports in new Open Test

    Reporting format • https://github.com/ota4j-team/open-test-reporting • New IterationSelector • for selecting a subset of a test’s or container’s iterations • Various improvements to ConsoleLauncher • --single-color and --color-palette • --list-engines • JUnit Platform Suite Engine included in stand-alone JAR
  5. JUnit Jupiter 5.9.x (1/2) • Configurable cleanup mode for @TempDir

    • ALWAYS, ON_SUCCESS, NEVER • New TestInstancePreConstructCallback extension API • counterpart to existing TestInstancePreDestroyCallback • Reusable parameter resolution for method invocations • via ExecutableInvoker API • accessed via ExtensionContext • used for @MethodSource factory methods • @MethodSource factory methods can accept arguments • resolved by ParameterResolver extensions • new syntax to disambiguate local factory methods
  6. JUnit Jupiter 5.9.x (2/2) • Configurable thread mode for @Timeout

    • INFERRED, SAME_THREAD, SEPARATE_THREAD • @EnabledOnOs and @DisabledOnOs • support for OS architectures • support for FreeBSD and OpenBSD • @EnabledInNativeImage and @DisabledInNativeImage • enable/disable tests in GraalVM native image • AssertionFailureBuilder • reuse Jupiter’s logic for creating failure messages
  7. JUnit Platform 1.10 M1 • Promotion of various experimental APIs

    to stable • Stacktrace pruning to hide internal JUnit calls • ConsoleLauncher • new testfeed details mode • new discover subcommand for test discovery without execution • Dry-run mode for test execution • New LauncherInterceptor SPI • New NamespacedHierarchicalStore for use in third-party test engines
  8. Custom ClassLoader Arrangements • New ReflectionSupport.tryToLoadClass(…) method that accepts an

    explicit ClassLoader • ReflectionSupport.findMethod(Class<?>, String, String) • now uses ClassLoader of the supplied class to load parameter types • used by DiscoverySelectors.selectMethod(Class<?>, String, String) • DiscoverySelectors methods for ClassSelector, NestedClassSelector, MethodSelector, and NestedMethodSelector that accept an explicit ClassLoader
  9. JUnit Jupiter 5.10 M1 • Promotion of various experimental APIs

    to stable • Failure threshold for @RepeatedTest • New TempDirFactory SPI for customizing @TempDir • Custom ClassLoader support for @EnabledIf, @DisabledIf, @MethodSource, and @ParameterizedTest argument conversion • Improved configurability of parallel execution • AnnotationBasedArgumentsProvider/AnnotationBasedArgumentConverter • base classes for implementing ArgumentsProvider and ArgumentConverter
  10. Current Roadmap • @BeforeSuite and @AfterSuite • in JUnit Platform

    @Suite engine • Extension API for customizing the ClassLoader in Jupiter • @FieldSource for parameterized tests • @ContainerTemplate • analagous to @TestTemplate • ContainerTemplateInvocationContextProvider extension API • @ParameterizedContainer • effectively parameterized test classes
  11. @FieldSource @ParameterizedTest @FieldSource("example.Utils#fruits") void test(String fruit) { /* perform assertions

    */ } public class Utils { public static final String[] fruits = {"apple", "banana"}; }
  12. @ParameterizedContainer – Field Injection @ParameterizedContainer @CsvSource({"apple, 1", "banana, 2"}) class

    FieldInjectionTestCase { @Parameter(0) private String fruit; @Parameter(1) private int score; @Test void test() { /* perform assertions */ } }
  13. @ParameterizedContainer – Constructor Injection @ParameterizedContainer @CsvSource({"apple, 1", "banana, 2"}) class

    ConstructorInjectionTestCase { private final String fruit; private final int score; ConstructorInjectionTestCase(String fruit, int score) { this.fruit = fruit; this.score = score; } @Test void test() { /* perform assertions */ } }
  14. @ParameterizedContainer – Java Record @ParameterizedContainer @CsvSource({"apple, 1", "banana, 2"}) record

    RecordTestCase(String fruit, int score) { @Test void test() { /* perform assertions */ } }
  15. Spring 6.0.x • Module path scanning for "classpath*:" resource prefix

    • useful for patched test modules with Maven • Servlet API 6.0 baseline in Servlet mocks and MockMvc • with Servlet 5.0 compatibility at runtime • Assertions against Cookie attributes in CookieResultMatchers for MockMvc • Default TestExecutionListeners enabled in JUnit 4 and TestNG base test classes • Revised logging in the TestContext framework (TCF) • ApplicationContextFailureProcessor SPI for processing ApplicationContext failures in the TCF • used by Spring Boot • Ahead of Time and GraalVM native image support
  16. Spring 6.1 • Support for recording async events with @RecordApplicationEvents

    • record events from threads other than main test thread • assert events from separate thread – for example with Awaitility • limitations in JUnit Jupiter • Support for null in MockHttpServletResponse.setCharacterEncoding() • New MockHttpServletRequestBuilder.setRemoteAddress() method • Avoid repeated attempts to load failing ApplicationContext in the TestContext framework • Potential integration with @ParameterizedContainer in JUnit Jupiter 5.11
  17. What are AOT and GraalVM? • AOT • ahead-of-time optimizations

    • part of the build process • utilized at runtime • GraalVM – https://www.graalvm.org/ • polyglot programming on the JVM • native image • compiling JVM applications to OS-specific binaries • closed world assumptions • no classpath • requires runtime hints
  18. What is Spring AOT? • https://docs.spring.io/spring-framework/reference/core/aot.html • AOT optimizations meant

    to inspect an ApplicationContext at build time and apply decisions and discovery logic that usually happens at runtime. • Core infrastructure in Spring Framework • Implemented across the portfolio • Build tools in Spring Boot • Spring AOT optimizations are used in GraalVM native image
  19. AOT Support for Spring Integration Tests • https://docs.spring.io/spring-framework/reference/testing/testcontext- framework/aot.html •

    Build-time detection of Spring integration tests • JUnit Jupiter, JUnit 4, and implicit support for TestNG etc. • Build-time AOT processing ◦ each unique test ApplicationContext will be refreshed for AOT processing • Runtime AOT support ◦ Spring integration tests use AOT-optimized ApplicationContexts
  20. GraalVM Native Build Tools • https://graalvm.github.io/native-build-tools/ • Collaboration between Oracle

    Labs, Spring, and Micronaut • Build native image • Run tests in native image using the JUnit Platform • Plugins for Gradle and Maven ◦ incorporated in Spring Boot build plugins
  21. Test-specific Runtime Hints • A lot is already built-in! •

    TestRuntimeHintsRegistrar • register globally via META-INF/spring/aot.factories • RuntimeHintsRegistrar • register globally via META-INF/spring/aot.factories • register locally on a test class via @ImportRuntimeHints • Annotate test class with @Reflective or @RegisterReflectionForBinding
  22. Implementing Custom AOT Testing Support • ContextLoader: implement AotContextLoader •

    AOT build-time processing • AOT runtime execution support • Spring Framework and Spring Boot context loaders implement AotContextLoader • TestExecutionListener: implement AotTestExecutionListener • to participate in AOT processing • See SqlScriptsTestExecutionListener for an example
  23. JUnit Support for Native Images • @EnabledInNativeImage and @DisabledInNativeImage ◦

    JUnit Jupiter execution conditions • Native Build Tools (NBT) support for JUnit Platform • Explicit support for JUnit Jupiter and JUnit 4 • Spring Boot 3 build tools ◦ Gradle and Maven ◦ Integration with NBT for testing support
  24. Running Spring Integration Tests in Native Image • Maven ◦

    ./mvnw -PnativeTest test • Gradle ◦ ./gradlew nativeTest • Definitely do this before deploying a native image to production • But this can take a considerable amount of time • So you won’t do this multiple times a day • Consider running tests in AOT mode on the JVM first
  25. Disclaimer: Testing in AOT mode on JVM • Not yet

    ready for prime time ◦ in terms of developer experience (DX) ◦ requires manual setup • But still possible with both Maven and Gradle ◦ Today I’ll show you how with Maven • Can be useful to: ◦ run tests in AOT mode on the JVM before running within a native image ◦ debug failing tests first in AOT mode on the JVM
  26. Mini Maven / Spring AOT Tutorial • Generate AOT test

    sources ◦ ./mvnw clean test spring-boot:process-test-aot • Run tests in AOT mode on JVM ◦ ./mvnw -Dspring.aot.enabled=true test • Add generated sources as “test” source folder in IDE ◦ target/spring-aot/test/sources/ ◦ output directory can be same as for your test code • Add CGLIB compiled classes to output classpath in IDE • alternative: @Configuration(proxyBeanMethods = false)
  27. Testing in AOT Mode • Run test class with -Dspring.aot.enabled=true

    • That’s it! • You should see log output like: • Loading ApplicationContext for AOT runtime for test class springio.service.ServiceTests • Starting AOT-processed ServiceTests
  28. Debugging in AOT Mode • Find mapping from test class

    to AOT-optimized ApplicationContext ◦ org.springframework.test.context.aot.AotTestContextInitializers__Generated ◦ For example, ServiceTests__TestContext001_ApplicationContextInitializer • Set breakpoint in ApplicationContextInitializer • Debug test class with -Dspring.aot.enabled=true