<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Marc Philipp</title>
    <description>Personal website</description>
    <link>https://www.marcphilipp.de/</link>
    <atom:link href="https://www.marcphilipp.de/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Tue, 21 Apr 2026 08:41:10 +0000</pubDate>
    <lastBuildDate>Tue, 21 Apr 2026 08:41:10 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>STF Milestone 10: Documentation improvements</title>
        <description>&lt;p class=&quot;lead&quot;&gt;JUnit has long been publishing its User Guide and API documentation for every release on &lt;a href=&quot;https://junit.org&quot;&gt;junit.org&lt;/a&gt;.
However, there was no way to easily navigate between versions and no warnings when browsing outdated or preview releases.
In &lt;a href=&quot;https://github.com/junit-team/junit-framework/issues/5244&quot;&gt;this milestone&lt;/a&gt;, I finally had the chance to resolve these long-standing issues.
&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Prior to this milestone, the only way of accessing a specific version was by manually changing the URL.
For example, the User Guide for version 5.13.0 is available at &lt;a href=&quot;https://docs.junit.org/5.13.0/user-guide/&quot;&gt;https://docs.junit.org/5.13.0/user-guide/&lt;/a&gt;.
Manually changing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5.13.0&lt;/code&gt; in the URL to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5.14.0&lt;/code&gt; takes one to the corresponding version.
However, doing so requires knowledge about which versions are available and is inconvenient and error-prone.&lt;/p&gt;

&lt;p&gt;Moreover, users should view the documentation specific to the version they’re using.
Looking at documentation that is older or more recent can be confusing: it can point out features that have better replacements or don’t exist yet in a stable version.
Therefore, we have decided to adopt &lt;a href=&quot;https://antora.org/&quot;&gt;Antora&lt;/a&gt; which – among other things – adds a version selector to each documentation page.
In addition, since it’s aware of all versions being deployed as part of the documentation site, it provides the ability to render a banner on &lt;a href=&quot;https://docs.junit.org/6.0.1/&quot;&gt;outdated&lt;/a&gt; or &lt;a href=&quot;https://docs.junit.org/6.1.0-M1/&quot;&gt;preview&lt;/a&gt; releases.
Rather than having a single HTML page for each version, Antora encourages splitting the documentation into separate pages that are focussed on a single topic.&lt;/p&gt;

&lt;p&gt;I started working on this milestone after we had released version 6.0.0.
I really wanted 6.0.0 to be part of the new documentation site, though.
Similarly, since we will continue to support 5.14.x for some time, at least the latest 5.14.x release should also be available on the Antora site.
Therefore, I decided to write &lt;a href=&quot;https://github.com/marcphilipp/antora-migrator&quot;&gt;a script&lt;/a&gt; that would migrate our Asciidoctor-based documentation to an Antora component so that I could run it on different Git branches for different releases.
This strategy worked out nicely in the end!&lt;/p&gt;

&lt;p&gt;Another challenge was adopting Antora’s support for the latest release and prerelease versions.
We wanted to ensure that the those versions continued to be available at &lt;a href=&quot;https://docs.junit.org/current/&quot;&gt;https://docs.junit.org/current/&lt;/a&gt; and &lt;a href=&quot;https://docs.junit.org/snapshot/&quot;&gt;https://docs.junit.org/snapshot/&lt;/a&gt;, respectively.
Before adopting Antora, we had achieved that by publishing the latest release in two places: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/current&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/6.0.2&lt;/code&gt;, for example.
Instead of duplicating pages, Antora relies on redirects.
However, GitHub Pages – where the site had been hosted until now – does not support redirects.
After trying a few alternatives, we decided to migrate to &lt;a href=&quot;https://statichost.eu&quot;&gt;statichost.eu&lt;/a&gt; for hosting instead.
It was very simple to set up and connect to GitHub.
It being privacy-focused and hosted in the EU was a nice bonus.&lt;/p&gt;

&lt;p&gt;Overall, I’m very happy how it turned out!
Antora provides a solid foundation to build on.
I’m sure the few limitations we encountered (e.g. its search functionality) will be improved over time.
But don’t take my word for it and have a look at &lt;a href=&quot;https://docs.junit.org&quot;&gt;docs.junit.org&lt;/a&gt; for yourself!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.junit.org&quot;&gt;&lt;img src=&quot;/img/posts/2026-01-25-stf-milestone-10-documentation-improvements/docs-site-screenshot.png&quot; alt=&quot;screenshot of Antora-based documentation site&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 25 Jan 2026 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2026/01/25/stf-milestone-10-documentation-improvements/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2026/01/25/stf-milestone-10-documentation-improvements/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 8: Improved parallel test execution</title>
        <description>&lt;p class=&quot;lead&quot;&gt;Since its introduction in JUnit 5.3, parallel test execution has become a popular feature of the JUnit Platform.
When enabled, it can significantly speed up test execution for many projects.
However, there were still some rough edges and long-standing issues that needed to be addressed.
Therefore, &lt;a href=&quot;https://github.com/junit-team/junit-framework/issues/5115&quot;&gt;this milestone&lt;/a&gt; focused on improving the parallel test execution capabilities of JUnit.
&lt;!--more--&gt;&lt;/p&gt;

&lt;h3 id=&quot;vintage-engine&quot;&gt;Vintage engine&lt;/h3&gt;

&lt;p&gt;JUnit Platform’s parallel execution support works for all test engines extending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HierarchicalTestEngine&lt;/code&gt;, such as JUnit Jupiter, Spock, and Cucumber.
Notably missing from that list is the JUnit Vintage engine, which runs JUnit 3 and JUnit 4 tests.
While the Vintage engine is only intended as a temporary migration aid, there are projects that will probably never migrate all their tests to JUnit Jupiter.
To help projects that use both the Vintage engine and Jupiter (or another testing framework based on the JUnit Platform), the Vintage engine has been enhanced to support parallel test execution as well, starting with version 5.12.0.
As documented in the &lt;a href=&quot;https://docs.junit.org/6.0.2/migrating-from-junit4.html#parallel-execution&quot;&gt;User Guide&lt;/a&gt;, the behavior can be enabled and configured via the following configuration parameters:&lt;/p&gt;

&lt;div class=&quot;language-properties highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;py&quot;&gt;junit.vintage.execution.parallel.enabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;junit.vintage.execution.parallel.classes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;junit.vintage.execution.parallel.methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;junit.vintage.execution.parallel.pool-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Thanks to contributor &lt;a href=&quot;https://github.com/YongGoose&quot;&gt;Yongjun Hong&lt;/a&gt; for implementing this feature!&lt;/p&gt;

&lt;h3 id=&quot;resource-locks&quot;&gt;Resource locks&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.junit.org/6.0.2/writing-tests/parallel-execution.html#synchronization&quot;&gt;Resource locks&lt;/a&gt; are a declarative mechanism to control which tests may run in parallel.
For example, the test methods in the following example class use resource locks to prevent conflicting concurrent access to system properties:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Execution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONCURRENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StaticSharedResourcesDemo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ResourceLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SYSTEM_PROPERTIES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;customPropertyIsNotSetByDefault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.prop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ResourceLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SYSTEM_PROPERTIES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;anotherCustomPropertyIsNotSetByDefault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.other.prop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ResourceLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SYSTEM_PROPERTIES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;READ_WRITE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;canSetCustomPropertyToApple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.prop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;apple&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apple&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.prop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customPropertyIsNotSetByDefault()&lt;/code&gt; cannot run concurrently with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canSetCustomPropertyToApple()&lt;/code&gt;, it may run in parallel with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;anotherCustomPropertyIsNotSetByDefault()&lt;/code&gt;.
In order to express that, prior to JUnit 5.12, every test method in this test class needs to be annotated with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ResourceLock&lt;/code&gt; annotation.
To reduce this boilerplate, a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target&lt;/code&gt; attribute has been added to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ResourceLock&lt;/code&gt; annotation.
It allows specifying that the lock applies to all test methods in the annotated class.
A test method such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canSetCustomPropertyToApple()&lt;/code&gt; may define a lock with the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; and a stricter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mode&lt;/code&gt;, though.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Execution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONCURRENT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ResourceLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SYSTEM_PROPERTIES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;READ&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CHILDREN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StaticSharedResourcesDemo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;customPropertyIsNotSetByDefault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.prop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;anotherCustomPropertyIsNotSetByDefault&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.other.prop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ResourceLock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SYSTEM_PROPERTIES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;READ_WRITE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;canSetCustomPropertyToApple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.prop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;apple&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apple&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;my.prop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another improvement was made for test engines such as Cucumber where parent containers have no execution behavior of their own but solely exist to group child tests.
Similar to Jupiter’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Isolated&lt;/code&gt; annotation, Cucumber scenarios can be annotated with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@isolated&lt;/code&gt; to indicate that they should not run in parallel with other tests/scenarios.
However, the JUnit Platform always pulled up that lock to the test class/feature level since a test class in Jupiter or Spock can have its own execution behavior (for example, a Jupiter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AfterAll&lt;/code&gt; method).
This made it impossible to run Cucumber features in parallel if one of them contained an isolated scenario.
The Cucumber engine will be able to configure this behavior once the new API is released in JUnit 6.1.&lt;/p&gt;

&lt;h3 id=&quot;open-heart-surgery-reimplementing-parallel-test-execution&quot;&gt;&lt;s&gt;Open heart surgery&lt;/s&gt; Reimplementing parallel test execution&lt;/h3&gt;

&lt;p&gt;The initial implementation of parallel test execution in the JUnit Platform is based on Java’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForkJoinPool&lt;/code&gt;.
Given the hierarchical nature of the test tree, this seemed like a natural fit.
For example, a test class would perform its setup, then submit its test methods to the pool for execution (&lt;em&gt;fork&lt;/em&gt;), wait for them to finish (&lt;em&gt;join&lt;/em&gt;), and finally perform its teardown.&lt;/p&gt;

&lt;p&gt;However, over time, it became clear that this implementation decision is causing issues in certain scenarios.
For example, when the code under test also uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForkJoinPool&lt;/code&gt; for its own parallelism, the pool used to run tests in parallel would suddenly spawn additional threads.
This could lead to more tests running in parallel than configured, causing resource exhaustion and out-of-memory errors.
Another problem was unwanted work-stealing in the presence of resource locks.
We had to implement special handling to prevent tests holding a set of resource locks from stealing work from other tests with conflicting locks.&lt;/p&gt;

&lt;p&gt;There have been several suggestions over the years on how to deal with these issues.
Most recently, a contributor proposed a solution that used a regular thread pool &lt;em&gt;in addition&lt;/em&gt; to the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ForkJoinPool&lt;/code&gt;.
This milestone finally gave me a chance to explore this idea.
However, I soon realized that using two thread pools would not be a good solution.
Not only would it double the number of threads and require context switching between the two pools, it would also add significant complexity to the implementation.
Therefore, I set out to reimplement parallel test execution using a regular thread pool with “simple” work-stealing support.
As you can imagine, that was quite an undertaking, and difficult to test properly.
Luckily, I had help!
Both, &lt;a href=&quot;https://github.com/leonard84&quot;&gt;Leonard Brünings&lt;/a&gt; from Spock and &lt;a href=&quot;https://github.com/mpkorstanje&quot;&gt;Rien Korstanje&lt;/a&gt; from Cucumber helped challenge, test, and improve the new implementation.
A huge thanks to both of them!&lt;/p&gt;

&lt;p&gt;The new implementation will be an opt-in feature in JUnit 6.1.
You can already give it a try today by using version &lt;a href=&quot;https://docs.junit.org/6.1.0-M1/release-notes.html#v6.1.0-M1&quot;&gt;6.1.0-M1&lt;/a&gt; (see &lt;a href=&quot;https://docs.junit.org/6.1.0-M1/writing-tests/parallel-execution.html#config-executor-service&quot;&gt;User Guide&lt;/a&gt; for details).
If we receive promising feedback in 6.1, we will switch the default to the new implementation in 6.2.
Looking forward to your feedback!&lt;/p&gt;
</description>
        <pubDate>Sun, 18 Jan 2026 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2026/01/18/stf-milestone-8-improved-parallel-test-execution/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2026/01/18/stf-milestone-8-improved-parallel-test-execution/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 7: Safe cancellation</title>
        <description>&lt;p class=&quot;lead&quot;&gt;Prior to this milestone, the JUnit Platform provided no safe way to cancel test execution early, e.g. after the first test failed.
The only option was to forcibly terminate the JVM running the tests.
However, that also caused cleanup operations, such as deleting temporary files or stopping Docker containers used in tests, to be skipped.
Letting all tests run is often wasteful in terms of resources such as CPU and causes longer feedback cycles for developers.
Therefore, &lt;a href=&quot;https://github.com/junit-team/junit-framework/issues/4725&quot;&gt;this milestone&lt;/a&gt; was all about introducing a safe cancellation mechanism to the JUnit Platform.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;The Launcher API is the main entry point to the JUnit Platform for IDEs and build tools.
The new &lt;a href=&quot;https://docs.junit.org/6.0.1/api/org.junit.platform.engine/org/junit/platform/engine/CancellationToken.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt;&lt;/a&gt; API allows clients to request cancellation of a running test execution.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Launcher&lt;/code&gt; interface already contained two methods for executing tests; one taking a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LauncherDiscoveryRequest&lt;/code&gt; and another taking a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestPlan&lt;/code&gt;.
Rather than adding two additional overloads that take a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt;, a new &lt;a href=&quot;https://docs.junit.org/6.0.1/api/org.junit.platform.launcher/org/junit/platform/launcher/LauncherExecutionRequest.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LauncherExecutionRequest&lt;/code&gt;&lt;/a&gt; class was introduced that encapsulates all parameters required for test execution, including an optional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt;.
This design allows for future extensions of the test execution parameters without breaking existing clients.
A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LauncherExecutionRequest&lt;/code&gt; can be created from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LauncherDiscoveryRequest&lt;/code&gt;, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestPlan&lt;/code&gt;, or via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LauncherDiscoveryRequestBuilder.forExecution()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using these new concepts, clients can implement “fail fast” behavior as follows (see &lt;a href=&quot;https://docs.junit.org/6.0.1/advanced-topics/launcher-api.html#launcher-cancellation&quot;&gt;User Guide&lt;/a&gt; for details):&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;CancellationToken&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CancellationToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;TestExecutionListener&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;failFastListener&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestExecutionListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;executionFinished&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TestIdentifier&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestExecutionResult&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getStatus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FAILED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;LauncherExecutionRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executionRequest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LauncherDiscoveryRequestBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;selectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selectClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MyTestClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;forExecution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cancellationToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;listeners&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;failFastListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;LauncherSession&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LauncherFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;openSession&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getLauncher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executionRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cancelling tests relies on test engines checking and responding to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CancellationToken&lt;/code&gt;.
As a stop-gap solution, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Launcher&lt;/code&gt; also checks the token and cancels test execution when multiple test engines are present at runtime.&lt;/p&gt;

&lt;p&gt;At the time of writing, the following test engines support cancellation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;JUnit Jupiter&lt;/li&gt;
  &lt;li&gt;JUnit Vintage&lt;/li&gt;
  &lt;li&gt;JUnit Platform Suite&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/junit-team/testng-engine&quot;&gt;TestNG&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;All &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HierarchicalTestEngine&lt;/code&gt; implementations such as &lt;a href=&quot;https://spockframework.org/&quot;&gt;Spock&lt;/a&gt; and &lt;a href=&quot;https://github.com/cucumber/cucumber-jvm/tree/main/cucumber-junit-platform-engine&quot;&gt;Cucumber&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to the changes in JUnit, I submitted a &lt;a href=&quot;https://github.com/apache/maven-surefire/pull/3155&quot;&gt;pull request&lt;/a&gt; to the Maven Surefire project.
It uses the new cancellation mechanism to implement support for Surefire’s &lt;a href=&quot;https://maven.apache.org/surefire/maven-surefire-plugin/examples/skip-after-failure.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skipAfterFailureCount&lt;/code&gt;&lt;/a&gt; feature.
It was merged and released in Maven Surefire 3.5.4.
For Gradle, there’s an &lt;a href=&quot;https://github.com/gradle/gradle/issues/34184&quot;&gt;open issue&lt;/a&gt; to implement similar support across all testing frameworks.&lt;/p&gt;

&lt;p&gt;To try out the new safe cancellation mechanism, you need to use &lt;a href=&quot;https://docs.junit.org/6.0.1/release-notes.html#v6.0.0&quot;&gt;JUnit 6.0.0&lt;/a&gt; or later.
I am looking forward to your feedback!&lt;/p&gt;
</description>
        <pubDate>Sun, 28 Dec 2025 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2025/12/28/stf-milestone-7-safe-cancellation/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2025/12/28/stf-milestone-7-safe-cancellation/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 6: Improved Kotlin support</title>
        <description>&lt;p class=&quot;lead&quot;&gt;Kotlin is a highly popular language for JVM applications (and beyond).
Not only is it the default language for Android apps, it has also become increasingly popular for backend applications.
JUnit Jupiter has always supported writing tests in Kotlin and provides Kotlin-specific assertions.
However, there were some long-standing and highly-voted issues for improving the usability when writing tests and extensions in Kotlin which have now been addressed in this STF milestone.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;The improvements made in this milestone fall into the following categories:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#kotlin-contracts&quot;&gt;Kotlin contracts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#jspecify-nullness-annotations&quot;&gt;JSpecify nullness annotations&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#kotlin-suspending-functions&quot;&gt;Kotlin suspending functions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#kotlin-sequence-support&quot;&gt;Kotlin &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sequence&lt;/code&gt; support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;kotlin-contracts&quot;&gt;Kotlin contracts&lt;/h2&gt;

&lt;p&gt;Kotlin is a statically typed language and the Kotlin compiler has powerful static analysis capabilities.
One of them and an often cited difference between Java and Kotlin is its null safety.&lt;/p&gt;

&lt;p&gt;Given a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; class, a Kotlin variable can be of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person?&lt;/code&gt; where the former does not allow assigning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; while the latter does.
When using a variable of a nullable type such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person?&lt;/code&gt;, the compiler requires code to deal with the possibility of the value being &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.
For example, to access the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;firstName&lt;/code&gt; property of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Person&lt;/code&gt; class on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person: Person?&lt;/code&gt;, one can’t just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person.firstName&lt;/code&gt;.
Instead, one usually either writes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person!!.firstName&lt;/code&gt; (which will throw a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NullPointerException&lt;/code&gt; in case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;) or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person?.firstName&lt;/code&gt; (which will evaluate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person.firstName&lt;/code&gt; otherwise).&lt;/p&gt;

&lt;p&gt;To make dealing with nullable types easier, Kotlin has a feature called “smart casts”.
If the Kotlin compiler can deduce that a value can no longer be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; because it has already been checked for nullness, it allows using it directly without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!!.&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?.&lt;/code&gt; operators.
To support the compiler, Kotlin functions can define &lt;a href=&quot;https://kotlinlang.org/docs/whatsnew13.html#contracts&quot;&gt;contracts&lt;/a&gt; that describe their behavior.&lt;/p&gt;

&lt;p&gt;As part of this milestone, JUnit’s Kotlin-specific assertion functions have been enhanced to define such contracts.
For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assertNotNull&lt;/code&gt; function now defines the following contract:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;contract&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;returns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;implies&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The contract states that if the function returns normally, meaning without throwing an assertion error, that its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actual&lt;/code&gt; argument has been checked for nullness and that subsequent code can assume it’s not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt;.
The following example demonstrates why this can be useful:&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;findPerson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;John&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Doe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;John&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Doe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In both calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assertEquals&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;person&lt;/code&gt; is dereferenced without using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!!.&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?.&lt;/code&gt; since the compiler knows it can no longer be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; due to the prior call of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assertNotNull&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;jspecify-nullness-annotations&quot;&gt;JSpecify nullness annotations&lt;/h2&gt;

&lt;p&gt;In addition to contracts for Kotlin code, &lt;a href=&quot;https://docs.junit.org/6.0.0-RC1/release-notes/&quot;&gt;JUnit 6.0.0-RC1&lt;/a&gt; started using &lt;a href=&quot;https://jspecify.dev/&quot;&gt;JSpecify&lt;/a&gt; to indicate nullness throughout its Java APIs.
The Kotlin compiler supports JSpecify’s annotations which makes writing Kotlin code using JUnit’s APIs more safe and idiomatic.&lt;/p&gt;

&lt;p&gt;Of course, there are also benefits when writing code in Java.
JSpecify is supported in IDEs such as IntelliJ IDEA so you may notice new warnings when programming against JUnit’s APIs.
To check nullability during your build, please check out &lt;a href=&quot;https://errorprone.info/&quot;&gt;Error Prone&lt;/a&gt; and &lt;a href=&quot;https://github.com/uber/NullAway&quot;&gt;NullAway&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;kotlin-suspending-functions&quot;&gt;Kotlin suspending functions&lt;/h2&gt;

&lt;p&gt;Kotlin has first-class language support for concurrency and asynchronous programming via &lt;a href=&quot;https://kotlinlang.org/docs/coroutines-overview.html&quot;&gt;coroutines&lt;/a&gt;.
One such language feature is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt; keyword that can be applied to function definitions.
Such suspending functions allow writing asynchronous code in regular sequential style but can pause and resume without blocking a thread.&lt;/p&gt;

&lt;p&gt;Prior to &lt;a href=&quot;https://docs.junit.org/6.0.0-RC1/release-notes/&quot;&gt;JUnit 6.0.0-RC1&lt;/a&gt;, writing tests for suspending functions with JUnit Jupiter was not straightforward.
Since suspending functions can only be called from other suspending functions, it felt natural to simply add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt; keyword to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt; method as well.
However, that didn’t work because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt; methods with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt; modifier were silently ignored by JUnit due to the way the Kotlin compiler generates JVM bytecode for them.
For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test suspend fun test() { /*...*/ }&lt;/code&gt; function will be compiled to the equivalent of the following Java method:&lt;/p&gt;
&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kotlin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;coroutines&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Continuation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kotlin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;cm&quot;&gt;/*...*/&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As discussed in the &lt;a href=&quot;/blog/2025/08/16/stf-milestone-5-discovery-issues/&quot;&gt;previous post&lt;/a&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt; methods must have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void&lt;/code&gt; return type.
Consequently, &lt;a href=&quot;https://docs.junit.org/5.13.0/release-notes/&quot;&gt;JUnit 5.13.0&lt;/a&gt; started reporting discovery issues of severity “warning” in such cases.
However, wouldn’t it be better if it “just worked”?&lt;/p&gt;

&lt;p&gt;Kotlin core libraries provide different ways of executing coroutines, meaning code that calls suspending functions.
One of the simplest is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runBlocking&lt;/code&gt; (&lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html&quot;&gt;docs&lt;/a&gt;) from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kotlinx.coroutines&lt;/code&gt;.
It runs the supplied code and blocks the current thread until its completion.
Starting with &lt;a href=&quot;https://docs.junit.org/6.0.0-RC1/release-notes/&quot;&gt;JUnit 6.0.0-RC1&lt;/a&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt; functions written in Kotlin may now carry the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;suspend&lt;/code&gt; modifier.
Behind the scenes, JUnit Jupiter will wrap the function body in a call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runBlocking&lt;/code&gt;.
For example, the following two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt; methods are equivalent.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CoroutineTests&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;someSuspendingFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;runBlocking&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;someSuspendingFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;someSuspendingFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’d rather use one of the alternatives to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runBlocking&lt;/code&gt;, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runTest&lt;/code&gt; (&lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html&quot;&gt;docs&lt;/a&gt;), you can do so explicitly as follows.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CustomCoroutineTests&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;runTest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;someSuspendingFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;kotlin-sequence-support&quot;&gt;Kotlin &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sequence&lt;/code&gt; support&lt;/h2&gt;

&lt;p&gt;Kotlin’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sequence&lt;/code&gt; (&lt;a href=&quot;https://kotlinlang.org/docs/sequences.html&quot;&gt;docs&lt;/a&gt;) abstraction is similar to Java’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterable&lt;/code&gt;.
The main difference lies in how it’s used in Kotlin’s standard library.
Methods operating on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterable&lt;/code&gt; are typically eager whereas those using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sequence&lt;/code&gt; are lazy.
For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterable&amp;lt;T&amp;gt;.map { ... }&lt;/code&gt; extension function applies the supplied function to all elements of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterable&lt;/code&gt; and returns a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&amp;lt;R&amp;gt;&lt;/code&gt;.
The corresponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sequence&amp;lt;T&amp;gt;.map { ... }&lt;/code&gt; extension function returns another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sequence&amp;lt;R&amp;gt;&lt;/code&gt; and does not apply the supplied function until a terminal operation like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toList()&lt;/code&gt; is called.
So, from a Java perspective, it’s more similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;JUnit supports conversion of a variety of types to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream&lt;/code&gt; for use with &lt;a href=&quot;https://docs.junit.org/current/user-guide/#writing-tests-dynamic-tests&quot;&gt;dynamic&lt;/a&gt; or &lt;a href=&quot;https://docs.junit.org/current/user-guide/#writing-tests-parameterized-tests&quot;&gt;parameterized&lt;/a&gt; tests.
Prior to &lt;a href=&quot;https://docs.junit.org/5.13.0/release-notes/&quot;&gt;JUnit 5.13.0&lt;/a&gt;, this included different types of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stream&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Collection&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterable&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterator&lt;/code&gt;, and all array types.
This list has been extended to support all types defining an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterator iterator()&lt;/code&gt; method, including Kotlin’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sequence&lt;/code&gt;.
For example, Kotlin sequences can now be used with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@MethodSource&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@FieldSource&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SequenceTests&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@JvmStatic&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;sequenceOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;JANUARY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MARCH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;AUGUST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;arguments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DECEMBER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@ParameterizedTest&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@FieldSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;Thanks to these improvements writing JUnit Jupiter tests in Kotlin has just gotten more convenient and idiomatic.
Please give &lt;a href=&quot;https://docs.junit.org/6.0.0-RC1/release-notes/&quot;&gt;JUnit 6.0.0-RC1&lt;/a&gt; a try and let us know what you think and provide feedback on &lt;a href=&quot;https://github.com/junit-team/junit-framework/&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Thu, 21 Aug 2025 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2025/08/21/stf-milestone-6-improved-kotlin-support/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2025/08/21/stf-milestone-6-improved-kotlin-support/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 5: Discovery issues</title>
        <description>&lt;p class=&quot;lead&quot;&gt;This milestone was all about finally resolving issue &lt;a href=&quot;https://github.com/junit-team/junit-framework/issues/242&quot;&gt;#242&lt;/a&gt; by introducing an error handling mechanism for test discovery.
As you can tell by the low issue number (we’re currently at #4840) it has been a long time coming.
Since introducing the discovery phase in an early version of JUnit 5, we had seen the need for being able to report “discovery issues” such as errors and warnings but also info-level messages to users.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Have you ever written a test and noticed at some later point that it wasn’t actually being executed?
I know, it sounds like a real nightmare.
But, can you spot what’s wrong with these test methods?&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Java&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-kotlin highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Kotlin&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Nothing&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;They look like regular tests, don’t they?
Well, actually, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt; methods must be declared having a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void&lt;/code&gt; return type (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unit&lt;/code&gt; in Kotlin)!
During the test discovery phase, the JUnit Jupiter test engine looks for methods that are annotated with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt; and have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void&lt;/code&gt; return type.
Before version 5.13, if it found a malformed declaration, such as the ones above, it silently discarded them – even if they were clearly intended to be test methods.&lt;/p&gt;

&lt;p&gt;To be fair, IDEs such as IntelliJ often provide inspections to help avoid running into such issues.
However, such IDE-only warnings are easy to overlook, especially in a large codebase.&lt;/p&gt;

&lt;p&gt;Therefore, rather than keeping you up at night, JUnit 5.13 introduced the concept of &lt;a href=&quot;https://docs.junit.org/current/user-guide/#running-tests-discovery-issues&quot;&gt;“discovery issues”&lt;/a&gt; and a two-part mechanism for reporting them.
First, test engines can &lt;a href=&quot;https://docs.junit.org/current/user-guide/#test-engines-discovery-issues&quot;&gt;report issues&lt;/a&gt; such as malformed declarations during test discovery.
Second, the JUnit Platform takes care of reporting them to users.
It logs all non-critical issues and, if there are any critical issues, test execution will fail for that test engine.
The severity that’s considered “critical” is configurable via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;junit.platform.discovery.issue.severity.critical&lt;/code&gt; &lt;a href=&quot;https://docs.junit.org/current/user-guide/#running-tests-config-params&quot;&gt;configuration parameter&lt;/a&gt;.
Currently, the default value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR&lt;/code&gt;, but that may be changed in a future release.&lt;/p&gt;

&lt;p&gt;All test engines provided by JUnit (Jupiter, Vintage, and Suite) already make use of this feature to report a variety of issues.
For example, the Jupiter engine will now report a warning for malformed test and lifecycle method declarations such as the ones shown above.
For the full list, please consult issue &lt;a href=&quot;https://github.com/junit-team/junit-framework/issues/242&quot;&gt;#242&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;We’ve introduced this mechanism at a stage when the JUnit Platform and its test engines were already widely used.
Therefore, it was probably inevitable that the initial 5.13.0 release reported a few false-positives.
For example, in 5.13.0 abstract test methods were overeagerly reported as being problematic, but we’ve since learned about use cases for them in the wild.
Those and other checks have since been refined and improved in 5.13.x patch releases.&lt;/p&gt;

&lt;p&gt;On the other hand, we’ve already seen a bunch of projects that had actual problems that needed addressing reported to them after they updated.
For example, one project had lots of inner classes that contained test methods but those inner classes were not annotated with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Nested&lt;/code&gt; so those tests were never executed.
That’s exactly the kind of problem we wanted to address by introducing this mechanism!&lt;/p&gt;

&lt;p&gt;If you haven’t yet updated to JUnit 5.13 or later, please do and look out for any reported discovery issues.
To surface all discovery issues in your project, we recommend setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;junit.platform.discovery.issue.severity.critical&lt;/code&gt; configuration parameter to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INFO&lt;/code&gt; and then running all your tests.&lt;/p&gt;
</description>
        <pubDate>Sat, 16 Aug 2025 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2025/08/16/stf-milestone-5-discovery-issues/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2025/08/16/stf-milestone-5-discovery-issues/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 4: Parameterized test classes</title>
        <description>&lt;p class=&quot;lead&quot;&gt;In version 5.0, JUnit Jupiter introduced support for parameterizing test methods.
In JUnit 4, only test classes could be parameterized.
Therefore, being able to do so on the method level provided more flexibility.
However, there are cases where a set of tests should be executed against the same sets of arguments.
The recent &lt;a href=&quot;https://github.com/junit-team/junit5/releases/tag/r5.13.0&quot;&gt;5.13 release&lt;/a&gt; introduced support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedClass&lt;/code&gt; thereby finally resolving this highly-voted feature request.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;To write a parameterized test &lt;em&gt;method&lt;/em&gt;, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedTest&lt;/code&gt; annotation is used instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt;.
In addition, at least one &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@...Source&lt;/code&gt; annotation is required to specify the &lt;em&gt;source&lt;/em&gt; of argument sets that the method will be invoked with.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomeTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ParameterizedTest&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldNotBeNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ParameterizedTest&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lengthShouldBeThree&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above example, both test methods are parameterized with an identical &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ValueSource&lt;/code&gt; annotation.
Each will be invoked twice, once with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;foo&quot;&lt;/code&gt; and once with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;bar&quot;&lt;/code&gt; as its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;Since both test methods use the same parameters, parameterizing the test class allows to remove the duplication.
To do so, the test class is annotated with the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedClass&lt;/code&gt; annotation and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@...Source&lt;/code&gt; annotation.
The test methods are changed to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Test&lt;/code&gt; annotation.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomeTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Parameter&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldNotBeNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lengthShouldBeThree&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of declaring the parameter on the methods, JUnit injects it into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Parameter&lt;/code&gt;-annotated field.
Alternatively, JUnit also supports &lt;a href=&quot;https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests-consuming-arguments-classes&quot;&gt;constructor injection&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SomeTests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldNotBeNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lengthShouldBeThree&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the example above, a Java &lt;em&gt;record&lt;/em&gt; is used and the class-level parameters are specified as record components.
This allows reducing the boilerplate of declaring a constructor and assigning its parameters to fields.
Regular classes may be used as well, though.&lt;/p&gt;

&lt;h3 id=&quot;a-common-use-case&quot;&gt;A common use case&lt;/h3&gt;

&lt;p&gt;A common use case for parameterizing a test class is to run its tests against different implementations of an interface.
Prior to JUnit 5.13, one would typically achieve that by writing an abstract base test class and creating a subclass for each concrete implementation.
For example, the following test class implements two tests against Java’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&lt;/code&gt; interface that are executed against three implementations of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&lt;/code&gt; that are part of the JDK.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@BeforeEach&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initializeList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;createList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newListIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itemCanBeAdded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;added&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Nested&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ArrayList&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayListTests&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Nested&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LinkedList&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedListTests&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Nested&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@DisplayName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Vector&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VectorTests&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This test can be rewritten using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedClass&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@MethodSource&lt;/code&gt; as follows.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@MethodSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;listImplementations&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ParameterizedListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listImplementations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;argumentSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ArrayList&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;()),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;argumentSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LinkedList&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;()),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;argumentSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Vector&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;())&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Parameter&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newListIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itemCanBeAdded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;added&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@AfterEach&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clearList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Necessary since the `List` parameter is mutable!&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This allows to get rid of the abstract base class and subclasses resulting in a much simpler structure.&lt;/p&gt;

&lt;h3 id=&quot;converters&quot;&gt;Converters&lt;/h3&gt;

&lt;p&gt;The same use case can also be implemented using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ValueSource&lt;/code&gt; (and constructor injection) instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@MethodSource&lt;/code&gt; as follows.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[{index}] {0}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ParameterizedWithValueSourceListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;ParameterizedWithValueSourceListTests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReflectionSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newInstance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newListIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// same as above...&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itemCanBeAdded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// same as above...&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ValueSource&lt;/code&gt; only supports class literals, not concrete instances, the constructor relies on JUnit’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReflectionSupport&lt;/code&gt; class to instantiate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;List&lt;/code&gt; via the implementations’ default constructors.
Whether it’s ok to use reflection here is, of course, debatable.
Personally, I think it’s acceptable in this case since test code is executed with every build and would fail right away.&lt;/p&gt;

&lt;p&gt;If you find yourself using such a &lt;em&gt;conversion&lt;/em&gt; in multiple places, it might make sense to extract it into a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArgumentConverter&lt;/code&gt; and annotate the parameter with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ConvertWith&lt;/code&gt; instead.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[{index}] {0}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ParameterizedWithValueSourceAndConverterListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Parameter&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ConvertWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ClassToInstanceConverter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newListIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// same as above...&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itemCanBeAdded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// same as above...&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@AfterEach&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clearList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Necessary since the `List` parameter is mutable!&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ArgumentConverter&lt;/code&gt; implementation is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClassToInstanceConverter&lt;/code&gt; in this example and contains the code that was part of the constructor in the previous example.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ClassToInstanceConverter&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SimpleArgumentConverter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ReflectionSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;newInstance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can even make this shorter by utilizing JUnit’s support for composed annotations.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@Retention&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RUNTIME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@Target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PARAMETER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;FIELD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ConvertWith&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ClassToInstanceConverter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Instantiate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course, that also works with constructor injections, for example, using a Java record.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[{index}] {0}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ParameterizedWithValueSourceAndConverterRecordListTests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Instantiate&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newListIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itemCanBeAdded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;added&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@AfterEach&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clearList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Necessary since the `List` parameter is mutable!&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;mutable-vs-immutable-arguments&quot;&gt;Mutable vs. immutable arguments&lt;/h3&gt;

&lt;p&gt;When using &lt;em&gt;mutable&lt;/em&gt; data as parameters, one has to be mindful of tests that change state.
In the above example, if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;itemCanBeAdded()&lt;/code&gt; were to run before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newListIsEmpty()&lt;/code&gt; without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AfterEach&lt;/code&gt; lifecycle method, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newListIsEmpty()&lt;/code&gt; would fail because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list&lt;/code&gt; would no longer be empty.
Therefore, it’s usually a better idea to use &lt;em&gt;immutable&lt;/em&gt; data as parameters.
In this case, this can be achieved by using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Supplier&lt;/code&gt; to create a list in the test class constructor.
Since each test method uses a separate instance of the test class (unless &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@TestInstance(PER_CLASS)&lt;/code&gt; is used), this will prevent test methods from influencing each other.
This also removes the need to reset the state of the parameters in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AfterEach&lt;/code&gt; lifecycle method.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@MethodSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;listImplementations&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ParameterizedListWithSuppliersTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listImplementations&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;argumentSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ArrayList&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Supplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;)&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;ArrayList:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;argumentSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LinkedList&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Supplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;)&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;LinkedList:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;argumentSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Vector&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Supplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;)&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;Vector:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;ParameterizedListWithSuppliersTests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Supplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listSupplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;listSupplier&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newListIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// same as above...&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itemCanBeAdded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// same as above...&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;argument-sources&quot;&gt;Argument sources&lt;/h3&gt;

&lt;p&gt;In addition to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ValueSource&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@MethodSource&lt;/code&gt;, JUnit provides the following annotations for specifying the sources of arguments:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@EnumSource&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@NullSource&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@EmptySource&lt;/code&gt; for simple values&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@CsvSource&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@CsvFileSource&lt;/code&gt; for CSV content&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@FieldSource&lt;/code&gt; for custom code&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ArgumentsSource(MyProvider.class)&lt;/code&gt; for custom providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please refer to JUnit’s &lt;a href=&quot;https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests-sources&quot;&gt;User Guide&lt;/a&gt; for details.&lt;/p&gt;

&lt;h3 id=&quot;lifecycle-methods&quot;&gt;Lifecycle methods&lt;/h3&gt;

&lt;p&gt;Parameterized classes may declare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@BeforeParameterizedClassInvocation&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@AfterParameterizedClassInvocation&lt;/code&gt; lifecycle methods which are called once before/after each invocation of the parameterized class with a set of arguments.
This may be used, for example, to initialize an argument as demonstrated in the following example.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;{0}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CustomInitializationListTests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Instantiate&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@BeforeParameterizedClassInvocation&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;arrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ensureCapacity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newListIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itemCanBeAdded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;added&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@AfterEach&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clearList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;cartesian-products&quot;&gt;Cartesian products&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedClass&lt;/code&gt; annotation may be combined with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Nested&lt;/code&gt;, also within an enclosing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedClass&lt;/code&gt;-annotated test class.
Moreover, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedClass&lt;/code&gt; may contain &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedTest&lt;/code&gt; methods.
Both can be used to test all combinations or, mathematically speaking, the Cartesian product of two parameter lists.&lt;/p&gt;

&lt;p&gt;The following example demonstrates that by testing all three concrete list implementations from the previous examples against each other in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Nested&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Interoperability&lt;/code&gt; test class.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Nested&lt;/code&gt; class additionally contains a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@ParameterizedTest&lt;/code&gt; method  to test against different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;String&lt;/code&gt; values.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[{index}] {0}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ParameterizedWithNestedListTests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Parameter&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Instantiate&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;newListIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itemCanBeAdded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;added&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@AfterEach&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clearList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Nested&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ParameterizedClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[{index}] {0}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinkedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Interoperability&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Parameter&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@Instantiate&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;secondList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;twoListsWithSameItemsAreEqual&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;secondList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@ParameterizedTest&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;@ValueSource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;qux&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;removeAllItemsInPassedList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extraItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;secondList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;secondList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extraItem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;removeAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secondList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nd&quot;&gt;@AfterEach&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;clearList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;secondList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A word of warning: the number of combinations can quickly become very large!
Therefore, you should take that into consideration when deciding whether to use this feature.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;p&gt;Parameterized test classes are a powerful testing tool that has long been missing from JUnit Jupiter.
I’m super happy that I’ve finally had the chance to resolve this long-standing and highly-voted issue thanks to the &lt;a href=&quot;/blog/2025/01/19/being-a-full-time-open-source-maintainer-supported-by-the-sovereign-tech-fund/&quot;&gt;Sovereign Tech Fund&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Edit: Thanks to my fellow JUnit 5 co-maintainer &lt;a href=&quot;https://github.com/sbrannen&quot;&gt;Sam Brannen&lt;/a&gt; for his feedback which I applied after initially publishing this post.&lt;/p&gt;
</description>
        <pubDate>Sat, 07 Jun 2025 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2025/06/07/stf-milestone-4-parameterized-test-classes/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2025/06/07/stf-milestone-4-parameterized-test-classes/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 3: Release verification and automation</title>
        <description>&lt;p class=&quot;lead&quot;&gt;A major goal of the Sovereign Tech Fund’s investment is to help projects become more sustainable and decrease their &lt;a href=&quot;https://en.wikipedia.org/wiki/Bus_factor&quot;&gt;truck factor&lt;/a&gt;. For JUnit, one activity in desperate need of improvement in this area was performing a release. Prior to this milestone, all JUnit releases of the past years had been performed from my local computer.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Artifacts were uploaded to Maven Central via Sonatype’s OSSRH infrastructure from my machine. Documentation and sample projects were also updated by a local build running on my machine. In total, the release checklist had 24 steps that I would perform manually for every release. Whenever I told people this, I was afraid they would gasp. However, releases of JUnit were not that frequent and it never felt important enough to spend the little time I had on release automation.&lt;/p&gt;

&lt;p&gt;While that may sound reasonable, doing releases this way was also a bad idea from a security perspective. A malicious actor would only have to compromise my machine (or me) in order to pull of a supply chain attack. A manipulated JUnit jar would soon find its way to a large portion of the Java ecosystem. While usually not part of any production system, JUnit runs during the build and could be used to manipulate production code. Therefore, I jumped at the opportunity to rectify this deficiency and proposed a milestone to the STF for verifying the release and automating the majority of it.&lt;/p&gt;

&lt;p&gt;One additional reason for doing releases locally was that Maven Central requires artifacts to be signed with a PGP key. Doing the release on a CI system therefore would mean trusting that system with my private key. In my book, that’s a bad idea as well. Even if the CI system might only store the private key temporarily, it could have been compromised, have some kind of data leak etc. Thus, I decided to keep that step local. However, since JUnit’s artifacts are completely reproducible, I would keep building and signing them locally, but rebuild them on a CI system. Thereby, the release process would verify the integrity of the binaries and rule out that they had been tampered with.&lt;/p&gt;

&lt;p&gt;Thus, I decided to keep the first part of the release process local:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Create a release branch&lt;/li&gt;
  &lt;li&gt;Change to release version&lt;/li&gt;
  &lt;li&gt;Change release date in README and release notes&lt;/li&gt;
  &lt;li&gt;Build and deploy release artifacts to Sonatype staging repository&lt;/li&gt;
  &lt;li&gt;Create a tag for the release&lt;/li&gt;
  &lt;li&gt;Change back to snapshot version&lt;/li&gt;
  &lt;li&gt;Push to GitHub&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The remainder of the release is now performed by triggering a &lt;a href=&quot;https://github.com/junit-team/junit5/actions/workflows/release.yml&quot;&gt;GitHub Actions workflow&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/2025-04-01-stf-milestone-3-release-verification-and-automation/github-actions-release-workflow.png&quot; alt=&quot;screenshot of GitHub Actions workflow chart&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The most important job in the above workflow is “Verify reproducibility”. It checks out the Git tag, rebuilds all binaries, and verifies that they are identical to the ones uploaded to the staging repository. This step verifies not only their reproducibility but also rules out that they have been compromised by my local build.&lt;/p&gt;

&lt;p&gt;The “Verify consumability” job updates all sample projects to consume the artifacts from the staging repository and builds them. Once the first phase is done, the staging repository is released. While waiting for the release artifacts to be synced to Maven Central, the documentation is published. Once the artifacts are indeed available on Maven Central, the sample projects are updated again but without the previous configuration that caused them to consume artifacts from the staging repository. Finally, a GitHub release is created.&lt;/p&gt;

&lt;p&gt;Since I introduced this workflow in January, it has already been run 7 times taking between 15 and 26 minutes. It has definitely made releasing less stressful and I’m really happy how it turned out.&lt;/p&gt;
</description>
        <pubDate>Tue, 01 Apr 2025 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2025/04/01/stf-milestone-3-release-verification-and-automation/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2025/04/01/stf-milestone-3-release-verification-and-automation/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 2: Open Test Reporting</title>
        <description>&lt;p&gt;When JUnit was first released, it did not provide a feature to write test reports in machine-readable format. Build tools like &lt;a href=&quot;https://ant.apache.org/manual/Tasks/junit.html&quot;&gt;Ant&lt;/a&gt; filled that gap and introduced an XML-based report. It was later adopted and sometimes extended by other Java-based build tools such as &lt;a href=&quot;https://maven.apache.org/surefire/maven-surefire-plugin/index.html&quot;&gt;Maven&lt;/a&gt; and &lt;a href=&quot;https://docs.gradle.org/current/userguide/java_testing.html#test_reporting&quot;&gt;Gradle&lt;/a&gt;. CI tools like &lt;a href=&quot;https://plugins.jenkins.io/junit/&quot;&gt;Jenkins&lt;/a&gt; introduced support for parsing these XML reports in order to display test results for each CI run. That led to other ecosystems also adopting the format to benefit from existing tool support. However, given that the format was initially defined for JUnit 3, it’s focused on test classes and methods and does not support nested structures. Some tools defined their own extensions of the schema that added such support or additional attributes. However, there are various partially conflicting schema definitions and subtle differences what gets written to which XML attributes since a proper “standard” was never agreed upon.&lt;/p&gt;

&lt;h3 id=&quot;open-test-reporting-to-the-rescue&quot;&gt;Open Test Reporting to the rescue&lt;/h3&gt;

&lt;p&gt;To address this situation, the JUnit team originally introduced the &lt;a href=&quot;https://github.com/ota4j-team/open-test-reporting?tab=readme-ov-file#xml-format-specification&quot;&gt;Open Test Reporting format&lt;/a&gt; in 2022 along with JUnit 5.9. It aims to replace the legacy XML format in a way that supports all features of JUnit 5. Moreover, it intends to support general testing concepts rather being tied to test classes and methods in order to appeal to other ecosystems as well. Additionally, it supports &lt;a href=&quot;https://github.com/ota4j-team/open-test-reporting#schema-extensions&quot;&gt;extension schemas&lt;/a&gt; for adding framework-specific information.&lt;/p&gt;

&lt;p&gt;Establishing a new format will, of course, take some time. Testing frameworks need to be adjusted in order to write the new format which they will only do if there is significant interest in their user communities. Similarly, tools like CI servers will only add support for reading test results written in the new format once there is a considerable number of testing frameworks supporting it. Therefore, &lt;a href=&quot;https://github.com/junit-team/junit5/issues/4113&quot;&gt;milestone 2&lt;/a&gt; of my STF-supported work focused on making the new format production-ready and increasing its appeal by providing an API and command-line tool for converting from XML into a user-friendly, standalone HTML report.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;developing-a-new-html-report&quot;&gt;Developing a new HTML report&lt;/h3&gt;

&lt;p&gt;It’s been a long time since I’ve been involved in setting up a frontend project from scratch. Therefore, I had mixed feelings before starting on the development of the HTML report. However, it actually turned out to be a lot of fun to work on once the initial setup was in place. Compared to Java, the development loop of making a change and seeing it in action was insanely fast. For reference, I decided to use &lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue.js&lt;/a&gt; with Typescript and &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt;. In addition, there’s a little Java code that injects the test results into the template that is produced by &lt;a href=&quot;https://vite.dev/&quot;&gt;Vite&lt;/a&gt; during the build process.&lt;/p&gt;

&lt;p&gt;I introduced a &lt;a href=&quot;https://github.com/ota4j-team/open-test-reporting#html-report&quot;&gt;CLI command&lt;/a&gt; that takes a single or multiple XML files as input and produces a self-contained HTML file. The test tree is rendered on the left with a details view on the right. The color and icon of the top bar changes depending on the overall status: it’s red if there was at least one failure and green otherwise.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/2025-03-21-stf-milestone-2-open-test-reporting/html-report-successful.png&quot; alt=&quot;screenshot of successful test report&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The screenshot also shows one other major new features that was added in this milestone: file attachments. In case of images they are rendered inline. This is useful for integration and UI tests. The screenshot shows a browser test that is part of Open Test Reporting’s own test suite and verifies the HTML report gets rendered correctly. The &lt;a href=&quot;https://junit.org/junit5/docs/5.12.1/api/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html#publishFile(java.lang.String,org.junit.jupiter.api.extension.MediaType,org.junit.jupiter.api.function.ThrowingConsumer)&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestReporter&lt;/code&gt; API&lt;/a&gt; in JUnit (along with a &lt;a href=&quot;https://junit.org/junit5/docs/5.12.1/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.html#publishFile(java.lang.String,org.junit.jupiter.api.extension.MediaType,org.junit.jupiter.api.function.ThrowingConsumer)&quot;&gt;similar method&lt;/a&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExtensionContext&lt;/code&gt; for extensions) allows attaching files during test execution.&lt;/p&gt;

&lt;p&gt;Of course, there’s also a dark theme:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/2025-03-21-stf-milestone-2-open-test-reporting/html-report-failed.png&quot; alt=&quot;screenshot of failed test report&quot; class=&quot;img-responsive img-thumbnail&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The format is &lt;a href=&quot;https://github.com/ota4j-team/open-test-reporting#extending-the-html-report&quot;&gt;extensible via an SPI&lt;/a&gt;. For example, the “JUnit metadata” section from the screenshot above is contributed by a JUnit-specific extension. This allows other testing frameworks to render HTML sections if they write additional information to the XML reports.&lt;/p&gt;

&lt;h3 id=&quot;seeing-it-in-action&quot;&gt;Seeing it in action&lt;/h3&gt;

&lt;p&gt;If you want to see it in action, please have a look at &lt;a href=&quot;https://github.com/junit-team/junit5/actions/workflows/main.yml?query=branch%3Amain+is%3Asuccess&quot;&gt;JUnit’s CI builds&lt;/a&gt; which archive the reports. You can find them at the end of the summary page of every GitHub Actions workflow run. Unfortunately, GitHub does not (yet?) support browsing HTML files directly, so you’ll have to download the zip file and extract it locally. It contains one HTML report per subproject.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/2025-03-21-stf-milestone-2-open-test-reporting/github-artifacts.png&quot; alt=&quot;screenshot of GitHub Actions workflow run artifacts&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Even better, you can (and should!) try it out in your own project. JUnit‘s User Guide documents how to enable writing the XML report. You can then use the &lt;a href=&quot;https://github.com/ota4j-team/open-test-reporting#html-report&quot;&gt;Open Test Reporting CLI&lt;/a&gt; to convert it to HTML.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;java -jar open-test-reporting-cli-0.2.2.jar html-report \
	--output open-test-report.html \
	open-test-reporting.xml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;where-to-go-from-here&quot;&gt;Where to go from here&lt;/h3&gt;

&lt;p&gt;Once I was more or less done with the implementation part of this milestone, I reached out to testing framework maintainers in other ecosystems. Some were hesitant or lacked the time to investigate but others were quite enthusiastic. For example, &lt;a href=&quot;https://github.com/sebastianbergmann/phpunit/issues/6077#issuecomment-2613863770&quot;&gt;PHPUnit&lt;/a&gt; right away implemented a proof-of-concept and drafted a concrete plan for adopting the Open Test Reporting format. Moreover, they plan to deprecate the “legacy” XML format in their next major version!&lt;/p&gt;

&lt;p&gt;All in all, I am really happy with the outcome of this milestone! If you have any questions about any of the above, please raise an issue in the &lt;a href=&quot;https://github.com/ota4j-team/open-test-reporting&quot;&gt;Open Test Reporting&lt;/a&gt; repository (if it’s about the format, CLI, etc.) or the &lt;a href=&quot;https://github.com/junit-team/junit5/&quot;&gt;JUnit 5&lt;/a&gt; repo (if it’s JUnit-specific).&lt;/p&gt;

&lt;p&gt;Happy testing!&lt;/p&gt;
</description>
        <pubDate>Fri, 21 Mar 2025 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2025/03/21/stf-milestone-2-open-test-reporting/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2025/03/21/stf-milestone-2-open-test-reporting/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 9: Jupiter extension improvements</title>
        <description>&lt;p class=&quot;lead&quot;&gt;As you can tell from the high number, this milestone was originally planned to be done later. However, a contributor (&lt;a href=&quot;https://github.com/JojOatXGME&quot;&gt;@JojOatXGME&lt;/a&gt;) was eager to work on it so I reached out to the STF and asked to change the order. They agreed and we changed the plan accordingly.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/junit-team/junit5/issues/4057&quot;&gt;issue addressed by this milestone&lt;/a&gt; was a crucial piece of missing functionality in the Jupiter extension API that kept popping up and was re-discussed every few weeks/months: there was no way to access the method-level extension context for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TestInstancePostProcessor&lt;/code&gt; (and other) extensions which is important to create and destroy resources on the test class instance level. The hope was that resolving this issue would reduce friction for users and free up time for maintainers to work on other duties.&lt;/p&gt;

&lt;p&gt;After the &lt;a href=&quot;https://github.com/junit-team/junit5/pull/4032&quot;&gt;initial PR&lt;/a&gt; from Johannes that introduced a new annotation to opt-in to the new behavior, I iterated quite a bit on it based on feedback from the rest of the JUnit team. We decided to introduce a new interface with a method instead because it allows custom logic to determine which scope should be used (for example, checking for a configuration parameter):&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestInstantiationAwareExtension&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Extension&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExtensionContextScope&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getTestInstantiationExtensionContextScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ExtensionContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExtensionContextScope&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DEFAULT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ExtensionContextScope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;  
       &lt;span class=&quot;no&quot;&gt;DEFAULT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TEST_METHOD&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I then added a configuration parameter to change the default behavior so it could easily be tested by consuming projects without editing any code. Moreover, I updated the &lt;a href=&quot;https://junit.org/junit5/docs/snapshot/user-guide/#extensions-test-instance-post-processing&quot;&gt;User Guide&lt;/a&gt; and modified all core extensions to opt-in right away. This change introduced constructor injection support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@TempDir&lt;/code&gt; and now allows writing a test class that requires a temporary directory like this (using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;record&lt;/code&gt; for brevity):&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TempDirTests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;@TempDir&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Path&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tempDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldExists&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tempDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;shouldAllowCreatingFiles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IOException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;nc&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tempDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test.txt&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Summing up, I was grateful that the STF allowed us the flexibility to change the original plan. Even more importantly, I was really happy that this long-standing issue was finally resolved!&lt;/p&gt;
</description>
        <pubDate>Mon, 27 Jan 2025 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2025/01/27/stf-milestone-9-jupiter-extension-improvements/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2025/01/27/stf-milestone-9-jupiter-extension-improvements/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
      <item>
        <title>STF Milestone 1: Adopt Renovate</title>
        <description>&lt;p class=&quot;lead&quot;&gt;When drafting the project description for the STF, it was important to choose something as a first milestone that I could get done in under a month and that would have a direct benefit to the project right away and improve its maintainability. That’s why I eventually decided on migrating to Renovate for automating updates to dependencies and build tools.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;We had already come a long way as a team when it comes to managing dependency updates. When we started to work on JUnit 5 in 2015, dependencies were few and we updated them manually. Over time, we adopted more tools, in particular in the build. We used the &lt;a href=&quot;https://github.com/ben-manes/gradle-versions-plugin&quot;&gt;Gradle Versions Plugin&lt;/a&gt; for a while but it had its limitations. Eventually, we adopted &lt;a href=&quot;https://docs.github.com/en/code-security/dependabot&quot;&gt;Dependabot&lt;/a&gt; and switched to Gradle’s &lt;a href=&quot;https://docs.gradle.org/current/userguide/version_catalogs.html&quot;&gt;version catalogs&lt;/a&gt; which made updating our main dependencies much easier.&lt;/p&gt;

&lt;p&gt;However, the amount of builds we needed to manage increased over time. Standalone projects such as the &lt;a href=&quot;https://github.com/junit-team/testng-engine&quot;&gt;TestNG engine&lt;/a&gt; were introduced and &lt;a href=&quot;https://github.com/junit-team/junit5-samples&quot;&gt;samples&lt;/a&gt; for more build tools were added. Dependabot only supported a subset of those. It also did not support keeping the build tools themselves up to date. So we had a &lt;a href=&quot;https://github.com/junit-team/wrapper-upgrade&quot;&gt;separate repository&lt;/a&gt; that used the &lt;a href=&quot;https://github.com/gradle/wrapper-upgrade-gradle-plugin/&quot;&gt;Wrapper Upgrade Gradle Plugin&lt;/a&gt; for updating our Gradle and Maven (for samples) builds.&lt;/p&gt;

&lt;p&gt;Adopting &lt;a href=&quot;https://mend.io/renovate&quot;&gt;Renovate&lt;/a&gt; promised to enable us to remove this incomplete patchwork of tools with a single solution. So far, it has lived up to this promise. It not only supports Gradle and Maven, but also Bazel, sbt, and npm. We now also use it for pinning our dependencies in GitHub Action workflows. It allowed us to remove the separate repo for updating wrappers and gives us peace of mind that everything is shipshape regarding our dependencies.&lt;/p&gt;

&lt;p&gt;The flip side of the coin are many PRs that can be quite noisy at times. While Renovate can be configured to work without PRs, we decided against that after discussing the topic in the JUnit team. We also decided to enable its auto-merge feature but require its PRs to be approved prior to merging to reduce the risk of supply chain attacks. Overall, I think we’re happy with the switch and would do it again.&lt;/p&gt;
</description>
        <pubDate>Sun, 19 Jan 2025 00:01:00 +0000</pubDate>
        <link>https://www.marcphilipp.de/blog/2025/01/19/stf-milestone-1-adopt-renovate/</link>
        <guid isPermaLink="true">https://www.marcphilipp.de/blog/2025/01/19/stf-milestone-1-adopt-renovate/</guid>
        
        
        <category>Sovereign Tech Fund</category>
        
        <category>JUnit</category>
        
      </item>
    
  </channel>
</rss>
