What’s new in JUnit 6: Key Changes and Improvements

JUnit 6 is here, eight years after JUnit 5 was released. This isn’t just an incremental update; it’s a significant modernization leap.

Let’s explore the main breaking changes and improvements.

JUnit 6 requires Java 17+ (and Kotlin 2.2+)

JUnit 6 raises the minimum required Java version from 8 to 17. This change is driven by two key factors:

  1. Java’s evolution: a lot of improvements have been introduced since version 8.
  2. Ecosystem shift towards Java 17.

For example:

For projects still using Java versions older than 17, JUnit 5 will continue to be supported for at least one more year.

For Kotlin users, JUnit 6 now requires Kotlin 2.2 or later.

JUnit Platform, Jupiter, and Vintage share the same version

The JUnit framework consists of three major architecture components:

  • Platform — provides the engine API for launching tests.
  • Jupiter — provides the API for writing tests.
  • Vintage — implements an engine that allows running tests written in JUnit 3 and 4.

However, in JUnit 5 only Jupiter and Vintage artifacts shared the same version, while the Platform artifacts followed a separate versioning scheme.

For example:

  • junit-jupiter-engine:5.13.3
  • junit-vintage-engine:5.13.3
  • junit-platform-engine:1.13.3

As you can see, the junit-platform-engine version differs from others.

To simplify the dependency management, JUnit 6 unifies versioning across all the components, meaning that all three of them, including JUnit Platform, will share the same version.

As a result, after migrating to JUnit 6, a single version can be used:

  • junit-jupiter-engine:6.0.0
  • junit-vintage-engine:6.0.0
  • junit-platform-engine:6.0.0

Various improvements and deprecated APIs

JUnit 6 introduces significant cleanup of the codebase by removing deprecated APIs and behaviors, while also adding several improvements:

New features and API improvements:

  • Kotlin’s suspend modifier may now be applied to test and lifecycle methods.
  • Display names for @ParameterizedClass and @ParameterizedTest now consistently style name-value pairs for arguments using name = value formatting – for example, fruit = apple instead of fruit=apple.
  • For consistency with test methods, @Nested classes declared in the same enclosing class or interface are now ordered in a deterministic but intentionally nonobvious way.

Public API deprecations and removals:

  • The JRE enum constants for JAVA_8 to JAVA_16 have been deprecated because they can no longer be used at runtime since JAVA_17 is the new baseline.
  • @EnabledForJreRange and @DisabledForJreRange now use JAVA_17 as their default min value.
  • MethodOrderer.Alphanumeric class removed.
  • ReflectionSupport.loadClass(String) method removed.
  • ReflectionUtils.readFieldValue(…​) method removed.
  • ReflectionUtils.getMethod(…​) method
  • InvocationInterceptor.interceptDynamicTest(Invocation, ExtensionContext) method removed.
  • The JUnit Vintage engine is now deprecated and will report an INFO level discovery issue when it finds at least one JUnit 4 test class.

Module removals and integrations:

  • junit-platform-runner and junit-platform-jfr modules removed. The JFR functionality is now integrated in junit-platform-launcher.
  • The junit-platform-suite-commons module is now integrated into junit-platform-suite.
  • The junit-jupiter-migrationsupport artifact and its contained classes are now deprecated and will be removed in the next major version.

Build tools compatibility:

  • Support for Maven Surefire/Failsafe versions less than 3.0.0 has been dropped.

The full list of changes can be found in the official Release Notes.

More consistent and performant CSV parsing for @CsvSource and @CsvFileSource

JUnit 6 has migrated its CSV parsing implementation from the no longer maintained univocity-parsers library to FastCSV.

This provides:

  • More consistent CSV input handling (including for malformed entries).
  • Better error reporting.
  • Improved performance.

Although the overall behavior remains largely compatible with JUnit 5, there are a few notable changes that might require updates to your tests or input files:

  • The lineSeparator attribute in @CsvFileSource has been removed. The line separator is now automatically detected, meaning that any of r, n, or rn is treated as a line separator.
  • Extra characters after a closing quote are no longer allowed in @CsvSource and @CsvFileSource. For example, if a single quote is used as the quote character, the following CSV value ‘foo’INVALID,’bar’ will now cause an exception to be thrown. This helps ensure that malformed input is not silently accepted or misinterpreted.
  • Attributes such as ignoreLeadingAndTrailingWhitespace, nullValues, and others in @CsvSource and @CsvFileSource now apply to header fields as well as to regular fields.
  • Root causes and messages of exceptions thrown for malformed CSV input may differ in some cases.

If your test suite includes complex or custom-formatted CSV data, it’s worth reviewing and validating it after migration.

JUnit 6 enhances null-safety with JSpecify annotations

In JUnit 5, nullability was described informally via JavaDoc — useful for human readers, but invisible to tools. This led to a few common issues:

  • NullPointerExceptions at runtime.
  • Extra casting or null-safety assertions in Kotlin, due to its distinction between nullable and non-nullable types

JUnit 6 addresses these issues by introducing JSpecify annotation support. These annotations are:

Starting with JUnit 6, all public APIs consistently declare their nullability using the @Nullable annotation.

For example, the Arguments.of() method now clearly indicates it accepts null arguments while always returning a non-null value:

static Arguments of(@Nullable Object... arguments) {
return of(arguments);
}

Non-annotated types are considered non-nullable by default.

Conclusion

JUnit 6 represents a major evolution of the testing framework. While not a complete rewrite like JUnit 5 was, it delivers substantial improvements by:

  • Embracing modern Java language features.
  • Introducing comprehensive null-safety through JSpecify.
  • Simplifying dependency management.

Useful links:


What’s new in JUnit 6: Key Changes and Improvements was originally published in Javarevisited on Medium, where people are continuing the conversation by highlighting and responding to this story.

This post first appeared on Read More