Short answer: JUnit 5 does not provide a native switch to suppress successful @ParameterizedTest invocations from XML output. Every invocation is emitted as its own <testcase> element, and neither the name attribute nor @DisplayNameGeneration affects report verbosity — they only change labels. The recommended pattern is to filter at the reporting boundary, either with a custom TestExecutionListener or by post-processing the XML.
Why display names don't solve this
Display name configuration is purely cosmetic. Whether you use name = "[{index}] {argumentsWithNames}" or a custom DisplayNameGenerator, the JUnit Platform still reports one result per invocation. The noise problem lives in the reporting layer, not the naming layer, so the fix has to happen there too.
Option 1: Custom TestExecutionListener
The JUnit Platform's listener API lets you intercept each TestExecutionResult before it reaches the XML reporter. A listener can record only failed invocations and write its own filtered XML (or a summary artifact) alongside the default report:
public class FailureOnlyListener implements TestExecutionListener {
private final List<String> failures = new ArrayList<>();
@Override
public void executionFinished(TestIdentifier id,
TestExecutionResult result) {
if (id.isTest()
&& result.getStatus() == TestExecutionResult.Status.FAILED) {
failures.add(id.getDisplayName() + " :: "
+ result.getThrowable().map(Throwable::getMessage).orElse(""));
}
}
@Override
public void testPlanExecutionFinished(TestPlan plan) {
// write failures to a dedicated file or custom XML
}
}
Register it via the META-INF/services/org.junit.platform.launcher.TestExecutionListener service-loader file (auto-detected by Surefire and Gradle), or through your build plugin's listener configuration. Because the display name of a parameterized invocation already contains the argument values (when using {argumentsWithNames}), failure traceability is preserved even though successes are dropped.
Option 2: Post-process the XML
A simpler, tool-agnostic approach is to leave JUnit alone and strip passing cases afterward. Any <testcase> without a <failure> or <error> child is a pass:
xmlstarlet ed -d '//testcase[not(failure) and not(error)]' \
target/surefire-reports/TEST-*.xml > filtered.xml
An XSLT stylesheet or a short Python script with xml.etree achieves the same. This keeps the build unchanged and works with any JUnit version, but adds a pipeline step to maintain.
Option 3: Let the reporting UI filter
Frameworks such as Allure or Serenity consume the full XML but let you collapse or hide passing tests in their dashboards. If your CI publishes to such a UI, you may not need to filter anything — the noise is a presentation concern the tool already handles, and the complete data remains available for trend and flake analysis.
Caveats
- Removing passed cases from XML can break downstream consumers that expect a complete suite — some coverage aggregators and Jenkins test-trend features assume full counts. Keep the unfiltered report as a build artifact and publish the filtered one separately.
- Listener registration details vary between Maven Surefire and Gradle versions; confirm against your plugin's documentation before relying on a specific configuration hook.
- If your CI parses the XML itself, validate the filtered output against the JUnit schema to avoid parse failures.
Verification
Create a parameterized test with five invocations and one deliberate failure. Run the suite twice: once with defaults (expect five <testcase> elements) and once with your filter in place (expect only the failing case). Confirm the failure entry still contains the parameter values in its name or message.
For most teams, the pragmatic choice is Option 3 if a reporting UI is available, and Option 2 otherwise — the listener approach is the most flexible but carries the highest maintenance cost across build-tool upgrades.