3. Sam Brannen
● Staff Software Engineer
● Java Developer for over 20 years
● Spring Framework Core Committer since 2007
● JUnit 5 Core Committer since October 2015
5. JUnit Jupiter Support in Spring
JUnit Jupiter and Spring are a great match for testing
● Spring Framework
● @ExtendWith(SpringExtension.class)
● @SpringJUnitConfig
● @SpringJUnitWebConfig
● Spring Boot
● @SpringBootTest
● @WebMvcTest, etc.
7. Odds and Ends
https://junit.org/junit5/docs/5.7.2/release-notes/
● Numerous APIs promoted from experimental to stable
● Improvements to EngineTestKit
● Improvements to Assertions
● Improvements to @CsvFileSource and @CsvSource
● Custom disabledReason for all @Enabled* / @Disabled* annotations
8. Major Features in 5.7
● Java Flight Recorder support
● junit.jupiter.testmethod.order.default configuration parameter to set the
default MethodOrderer
● used unless @TestMethodOrder is present
● @EnabledIf and @DisabledIf: based on condition methods
● @Isolated: run the test class in isolation during parallel execution
● TypedArgumentConverter for converting one specific type to another
○ use with @ConvertWith in a @ParameterizedTest
○ enhancements and bug fixes in 5.8
10. Major Features in JUnit Platform 1.8
● Declarative test suites via @Suite classes
● SuiteTestEngine in junit-platform-suite-engine module
● new annotations in junit-platform-suite-api module
■ @Suite, @ConfigurationParameter, @SelectUris, @SelectFile, etc.
● UniqueIdTrackingListener
○ TestExecutionListener that tracks the unique IDs of all tests
○ generates a file containing the unique IDs
○ can be used to rerun those tests
■ for example, with GraalVM Native Build Tools
11. Example: Suites before 5.8 – Soon to be Deprecated
// Uses JUnit 4 to run JUnit 5
@RunWith(JUnitPlatform.class)
@SuiteDisplayName("Integration Tests")
@IncludeEngines("junit-jupiter")
@SelectPackages("com.example")
@IncludeTags("integration-test")
public class IntegrationTestSuite {
}
12. Example: Suites with JUnit 5.8
// Uses JUnit 5 to run JUnit 5
@Suite
@SuiteDisplayName("Integration Tests")
@IncludeEngines("junit-jupiter")
@SelectPackages("com.example")
@IncludeTags("integration-test")
public class IntegrationTestSuite {
}
13. Small Enhancements in JUnit 5.8
https://junit.org/junit5/docs/snapshot/release-notes/
● More fine-grained Java Flight Recorder (JFR) events
● plus support on Java 8 update 262 or higher
● assertThrowsExactly()
○ alternative to assertThrows()
● assertInstanceOf()
○ instead of assertTrue(obj instanceof X)
● @RegisterExtension fields may now be private
15. Test Class Execution Order
● ClassOrderer API analogous to the MethodOrderer API
○ ClassName
○ DisplayName
○ OrderAnnotation
○ Random
● Global configuration via junit.jupiter.testclass.order.default configuration
parameter for all test classes
● for example, to optimize the build
● Local configuration via @TestClassOrder for @Nested test classes
17. @TempDir – New Behavior
Due to popular demand from the community…
● @TempDir previously created a single temporary directory per context
● @TempDir can now be used to create multiple temporary directories
● JUnit now creates a separate temporary directory per @TempDir annotation
● Revert to the old behavior by setting the junit.jupiter.tempdir.scope
configuration parameter to per_context
18. @ExtendWith on Fields and Parameters
Improves programming model
● @RegisterExtension: register extensions via fields programmatically
○ nothing new
● @ExtendWith: can now register extensions via fields and parameters declaratively
● fields: static or instance
● parameters: constructor, lifecycle method, test method
● typically as a meta-annotation
● avoids the need to declare @ExtendWith at the class or method level
19. Example: RandomNumberExtension
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(RandomNumberExtension.class)
public @interface Random {
}
class RandomNumberExtension implements BeforeAllCallback,
BeforeEachCallback, ParameterResolver {
// implementation...
}
20. Example: @Random in Action
class RandomNumberTests {
@Random
private int randomNumber1;
RandomNumberTests(@Random int randomNumber2) {
}
@BeforeEach
void beforeEach(@Random int randomNumber3) {
}
@Test
void test(@Random int randomNumber4) {
}
}
21. Named API
● Named: container that associates a name with a given payload
○ The meaning of the payload depends on the context
○ Named.of() vs. Named.named()
● DynamicTests.stream() can consume Named input and will use each name-value pair
as the display name and value for each generated dynamic test
● In parameterized tests using @MethodSource or @ArgumentSource, arguments can
now have explicit names supplied via the Named API
○ explicit name used in display name instead of the argument value
22. Example: Named Dynamic Tests
@TestFactory
Stream<DynamicTest> dynamicTests() {
Stream<Named<String>> inputStream = Stream.of(
named("racecar is a palindrome", "racecar"),
named("radar is also a palindrome", "radar"),
named("mom also seems to be a palindrome", "mom"),
named("dad is yet another palindrome", "dad")
);
return DynamicTest.stream(inputStream,
text -> assertTrue(isPalindrome(text)));
}
23. AutoCloseable Arguments in Parameterized Tests
● In parameterized tests, arguments that implement AutoCloseable will now be
automatically closed after the test completes
● Allows for automatic cleanup of resources
○ closing a file
○ stopping a server
○ etc.
● Similar to the CloseableResource support in the ExtensionContext.Store
25. New in Spring Framework 5.3 GA to 5.3.9
https://github.com/spring-projects/spring-framework/releases
● Test configuration is now discovered on enclosing classes for @Nested test classes
● ApplicationEvents abstraction for capturing application events published in the
ApplicationContext during a test
● Set spring.test.constructor.autowire.mode in junit-platform.properties
● Detection for @Autowired violations in JUnit Jupiter
● Improvements for file uploads and multipart support in MockMvc and
MockRestServiceServer
● Various enhancements in MockHttpServletRequest and MockHttpServletResponse
● Improved SQL script parsing regarding delimiters and comments
26. Example: @Nested tests before 5.3
@SpringJUnitConfig(TestConfig.class)
@ActiveProfiles("dev")
@Transactional
class DevTests {
@Nested
@SpringJUnitConfig(TestConfig.class)
@ActiveProfiles("dev")
@Transactional
class OrderTests { /* tests */ }
@Nested
@SpringJUnitConfig(PricingConfig.class)
class PricingTests { /* tests */ }
}
27. Example: @Nested tests after 5.3
@SpringJUnitConfig(TestConfig.class)
@ActiveProfiles("dev")
@Transactional
class DevTests {
@Nested
class OrderTests { /* tests */ }
@Nested
@NestedTestConfiguration(OVERRIDE)
@SpringJUnitConfig(PricingConfig.class)
class PricingTests { /* tests */ }
}
28. Example: ApplicationEvents
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents
class OrderServiceTests {
@Autowired OrderService orderService;
@Autowired ApplicationEvents events;
@Test
void submitOrder() {
// Invoke method in OrderService that publishes an event
orderService.submitOrder(new Order(/* ... */));
// Verify that 1 OrderSubmitted event was published
assertThat(events.stream(OrderSubmitted.class)).hasSize(1);
}
}
29. Coming in Spring Framework 5.3.10
Improving every step of the way…
● ExceptionCollector testing utility
● Soft assertions for MockMvc and WebTestClient
● Support for HtmlFileInput.setData() with HtmlUnit and MockMvc
● setDefaultCharacterEncoding() in MockHttpServletResponse
● Default character encoding for responses in MockMvc
30. Example: MockMvc without Soft Assertions
mockMvc.perform(get("/person/5").accept(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Jane"));
36. GraalVM Native Build Tools
“The Native Build Tools project provides plugins for different build
tools to add support for building and testing native applications
written in Java (or any other language compiled to JVM bytecode)”
● Collaboration between the GraalVM team, the Spring team, and recently the
Micronaut team as well
● Build tools
● Gradle plugin
● Maven plugin
● Compiling native images
● Testing within a native image using the JUnit Platform
37. Why test within a native image?
● Java is “write once; run anywhere”.
● … but a native image is not your app running on the JVM.
● You could just rely on your normal JVM-based test suite.
● … because your app is already tested – right?
● You could test your native app from the outside – for example, via HTTP endpoints.
○ … but do you have HTTP endpoints for every feature of your app?
● Best practice:
○ Write your JVM tests like you normally do.
○ Run the same tests (or a subset) within a native image.
38. How can a Native Build Tools plugin help?
● Runs your tests in the JVM first, using the UniqueIdTrackingListener
○ Tracks the tests you want to run in the native image
○ Necessary since classpath scanning doesn’t work in a native image
● If your tests or the code you’re testing requires reflection, you might need to run your
JVM tests with the GraalVM agent
● Compiles your app and tests into a native image using the JUnitPlatformFeature
(GraalVM Feature)
● Runs your tests within the native image using the NativeImageJUnitLauncher
○ Launches the JUnit Platform and selects your tests based on the output of the
UniqueIdTrackingListener
● Outputs results to the console and XML test reports
39. How to use Native Build Tools
● Follow the instructions for configuring your Gradle or Maven build
○ https://graalvm.github.io/native-build-tools/
● Or use the Spring Native support configured for your automatically
○ https://start.spring.io
● Gradle:
○ gradle nativeTest
○ gradle –Pagent test and gradle –Pagent nativeTest
● Maven:
○ mvn –Dskip -Pnative test
40. Things to keep in mind
● Not everything works in a native image
○ Classpath scanning
○ Dynamic class creation
○ Mockito and various mocking libraries
● You might need to exclude certain tests within a native image
○ For example, via tags or a custom ExecutionCondition in JUnit Jupiter
● Building a native image can take a long time
○ Probably not something you want to do multiple times a day
○ A dedicated CI pipeline might be a better choice